/*
* Copyright (c) 2013 Big Switch Networks, Inc.
*
* Licensed under the Eclipse Public License, Version 1.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.sdnplatform.ovsdb.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.sdnplatform.core.IControllerService;
import org.sdnplatform.core.IHAListener;
import org.sdnplatform.core.IControllerService.Role;
import org.sdnplatform.core.annotations.LogMessageDoc;
import org.sdnplatform.core.annotations.LogMessageDocs;
import org.sdnplatform.core.module.ModuleContext;
import org.sdnplatform.core.module.ModuleException;
import org.sdnplatform.core.module.IModule;
import org.sdnplatform.core.module.IPlatformService;
import org.sdnplatform.ovsdb.IOVSDB;
import org.sdnplatform.ovsdb.IOVSDBListener;
import org.sdnplatform.ovsdb.IOVSDBManagerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation for {@link IOVSDBManagerService}
*/
public class OVSDBManagerImpl
implements IModule, IOVSDBManagerService,
IHAListener {
protected static Logger logger =
LoggerFactory.getLogger(OVSDBManagerImpl.class);
/**
* OVSDB objects that are connected to the controller on the OpenFlow
* channel. Indexed by dpid.
*/
public static Map<Long, IOVSDB> ovsSwitchMap =
new ConcurrentHashMap<Long, IOVSDB>();
/**
* OVSDB objects that are not connected to the controller on the OpenFlow
* channel but are reachable on the JSON RPC channel. Indexed by management
* IP address.
*/
public static Map<String, IOVSDB> ovsSwitchMapNC =
new ConcurrentHashMap<String, IOVSDB>();
protected IControllerService controllerProvider;
ClientBootstrap bootstrap;
OVSDBClientPipelineFactory ovsdbcFact;
public Collection<IOVSDBListener> listeners;
public void shutDown() {
}
//*****************************
// Getters/Setters
//*****************************
public void setControllerProvider(
IControllerService controllerProvider) {
this.controllerProvider = controllerProvider;
}
//**************
// IOVSDBManager
//**************
@Override
public IOVSDB getOVSDB(long dpid) {
return ovsSwitchMap.get(dpid);
}
@Override
public Collection<IOVSDB> getOVSDB() {
return ovsSwitchMap.values();
}
@Override
public void addOVSDBListener(IOVSDBListener l) {
if (listeners == null)
listeners = new ArrayList<IOVSDBListener>();
listeners.add(l);
}
@Override
public IOVSDB removeOVSDB(long dpid) {
IOVSDB o = ovsSwitchMap.remove(dpid);
if (logger.isDebugEnabled() && o != null) {
logger.debug("removing OVSDB obj from ovs map for dpid {}", dpid);
}
return o;
}
@Override
public IOVSDB addOVSDB(long dpid) {
if (ovsSwitchMap.containsKey(dpid)) {
logger.info("OVS Switch {} already connected", dpid);
return ovsSwitchMap.get(dpid);
} else {
//create new ovsdb instance for OVSDBManager
Object statusObject = new Object();
if (controllerProvider.getSwitches() != null &&
controllerProvider.getSwitches().get(dpid) != null) {
String mgmtIPAddr = controllerProvider.getSwitches().get(dpid)
.getInetAddress().toString();
mgmtIPAddr = mgmtIPAddr.substring(1, mgmtIPAddr.indexOf(':'));
if (ovsSwitchMapNC.containsKey(mgmtIPAddr)) {
ovsSwitchMapNC.remove(mgmtIPAddr);
logger.debug("removed ovsdb object in NC map " +
"for ovs @ {}", mgmtIPAddr);
}
logger.debug("adding new OVSDB object to ovs map for " +
"dpid {} at mgmt. ip {}", dpid, mgmtIPAddr);
OVSDBImpl tsw = new OVSDBImpl(dpid, mgmtIPAddr,
ovsdbcFact, bootstrap, statusObject);
tsw.getTunnelIPAddress(); //populate tunnel-IP address
ovsSwitchMap.put(dpid, tsw);
return tsw;
}
return null;
}
}
@Override
public boolean addPort(long dpid, String portname) {
IOVSDB ovs = ovsSwitchMap.get(dpid);
if (ovs != null) {
ovs.addPort(portname, null, null, false);
return true;
}
return false;
}
@Override
public boolean delPort(long dpid, String portname) {
IOVSDB ovs = ovsSwitchMap.get(dpid);
if (ovs != null) {
ovs.delPort(portname);
return true;
}
return false;
}
@Override
public ArrayList<String> getControllerIPAddresses(long dpid) {
if (ovsSwitchMap.containsKey(dpid))
return ovsSwitchMap.get(dpid).getControllerIPs();
//check in map for switches not connected on OF channel
for (IOVSDB o : ovsSwitchMapNC.values()) {
if (o.getDpid() == dpid) {
return o.getControllerIPs();
}
}
return null;
}
@Override
public void setControllerIPAddresses(long dpid, ArrayList<String> cntrIP) {
if (cntrIP == null || cntrIP.size() == 0) {
logger.error("must specify at least one controller-ip to set" +
"at dpid {}", dpid);
return;
}
if (ovsSwitchMap.containsKey(dpid)) {
ovsSwitchMap.get(dpid).setControllerIPs(cntrIP);
return;
} else {
for (IOVSDB o : ovsSwitchMapNC.values()) {
if (o.getDpid() == dpid) {
o.setControllerIPs(cntrIP);
return;
}
}
}
logger.error("Switch dpid {} not set - could not find ovsdb object" +
" when trying to set controller-IPs", dpid);
}
private IOVSDB findOrCreateBridge(String mgmtIPAddr) {
for (IOVSDB o : ovsSwitchMap.values()) {
if (o.getMgmtIPAddr().equals(mgmtIPAddr)) {
return o;
}
}
if (ovsSwitchMapNC.containsKey(mgmtIPAddr)) {
return ovsSwitchMapNC.get(mgmtIPAddr);
}
// otherwise create a new OVSDB object with dummy dpid
Object statusObject = new Object();
OVSDBImpl dsw = new OVSDBImpl(-1, mgmtIPAddr, ovsdbcFact, bootstrap,
statusObject);
ovsSwitchMapNC.put(mgmtIPAddr, dsw);
return dsw;
}
@Override
public String getBridgeDpid(String mgmtIPAddr) {
IOVSDB dsw = findOrCreateBridge(mgmtIPAddr);
return dsw.getBridgeDpid();
}
@Override
public void setBridgeDpid(String mgmtIPAddr, String dpidstr) {
// We may have stale objects with same dpid in NC map, remove conflicts
Long dpid = Long.parseLong(dpidstr, 16);
for (IOVSDB o : ovsSwitchMapNC.values()) {
if (o.getDpid() == dpid && !o.getMgmtIPAddr().equals(mgmtIPAddr)) {
if (logger.isDebugEnabled()) {
logger.debug("Removing stale object with IP {} and dpid {}",
mgmtIPAddr, dpidstr);
}
ovsSwitchMapNC.remove(o.getMgmtIPAddr());
}
}
IOVSDB dsw = findOrCreateBridge(mgmtIPAddr);
dsw.setBridgeDpid(dpidstr);
}
//*****************
// Internal helpers
//*****************
protected void setupNettyClient() {
// Configure the client.
bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// Set up the event pipeline factory.
// TODO: intelligently decide between SSL and plain
ovsdbcFact = new OVSDBClientPipelineFactory();
bootstrap.setPipelineFactory(ovsdbcFact);
bootstrap.setOption("reuseAddr", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.setOption("child.tcpNoDelay", true);
}
// IModule
@Override
public Collection<Class<? extends IPlatformService>> getModuleServices() {
Collection<Class<? extends IPlatformService>> l =
new ArrayList<Class<? extends IPlatformService>>();
l.add(IOVSDBManagerService.class);
return l;
}
@Override
public Map<Class<? extends IPlatformService>, IPlatformService>
getServiceImpls() {
Map<Class<? extends IPlatformService>,
IPlatformService> m =
new HashMap<Class<? extends IPlatformService>,
IPlatformService>();
m.put(IOVSDBManagerService.class, this);
return m;
}
@Override
public Collection<Class<? extends IPlatformService>>
getModuleDependencies() {
Collection<Class<? extends IPlatformService>> l =
new ArrayList<Class<? extends IPlatformService>>();
l.add(IControllerService.class);
return l;
}
@Override
public void init(ModuleContext context)
throws ModuleException {
controllerProvider =
context.getServiceImpl(IControllerService.class);
}
@Override
public void startUp(ModuleContext context) {
controllerProvider.addHAListener(this);
setupNettyClient();
}
@Override
@LogMessageDocs({
@LogMessageDoc(level="WARN",
message="Unknown controller role: {role}",
explanation="The OVSDB manager received a notification "
+ "that the controller changed to a new role that"
+ "is currently not supported",
recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
})
public void roleChanged(Role oldRole, Role newRole) {
switch(newRole) {
case MASTER:
// no-op for now
break;
case SLAVE:
logger.debug("Clearing OVS Switch Maps due to " +
"HA change to SLAVE");
ovsSwitchMap.clear();
ovsSwitchMapNC.clear();
break;
default:
logger.warn("Unknow controller role: {}", newRole);
break;
}
}
@Override
public void controllerNodeIPsChanged(
Map<String, String> curControllerNodeIPs,
Map<String, String> addedControllerNodeIPs,
Map<String, String> removedControllerNodeIPs) {
// ignore
}
}