OpenDaylight Controller functional modules.
[controller.git] / opendaylight / switchmanager / src / main / java / org / opendaylight / controller / switchmanager / internal / SwitchManagerImpl.java
diff --git a/opendaylight/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImpl.java b/opendaylight/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImpl.java
new file mode 100755 (executable)
index 0000000..b36d8c1
--- /dev/null
@@ -0,0 +1,1774 @@
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.switchmanager.internal;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Dictionary;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.dm.Component;
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.sal.core.Bandwidth;
+import org.opendaylight.controller.sal.core.Config;
+import org.opendaylight.controller.sal.core.Name;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.State;
+import org.opendaylight.controller.sal.core.Tier;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.inventory.IInventoryService;
+import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.IObjectReader;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.opendaylight.controller.sal.utils.ObjectReader;
+import org.opendaylight.controller.sal.utils.ObjectWriter;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.switchmanager.IInventoryListener;
+import org.opendaylight.controller.switchmanager.ISpanAware;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.controller.switchmanager.ISwitchManagerAware;
+import org.opendaylight.controller.switchmanager.SpanConfig;
+import org.opendaylight.controller.switchmanager.Subnet;
+import org.opendaylight.controller.switchmanager.SubnetConfig;
+import org.opendaylight.controller.switchmanager.Switch;
+import org.opendaylight.controller.switchmanager.SwitchConfig;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The class describes SwitchManager which is the central repository of all the
+ * inventory data including nodes, node connectors, properties attached, Layer3
+ * configurations, Span configurations, node configurations, network device
+ * representations viewed by Controller Web applications. One SwitchManager
+ * instance per container of the network. All the node/nodeConnector properties
+ * are maintained in the default container only.
+ */
+public class SwitchManagerImpl implements ISwitchManager,
+        IConfigurationContainerAware, IObjectReader,
+        ICacheUpdateAware<Long, String>, IListenInventoryUpdates,
+        CommandProvider {
+    private static Logger log = LoggerFactory
+            .getLogger(SwitchManagerImpl.class);
+    private static String ROOT = GlobalConstants.STARTUPHOME.toString();
+    private static final String SAVE = "Save";
+    private String subnetFileName = null, spanFileName = null,
+            switchConfigFileName = null;
+    private List<NodeConnector> spanNodeConnectors = new CopyOnWriteArrayList<NodeConnector>();
+    private ConcurrentMap<InetAddress, Subnet> subnets; // set of Subnets keyed by the InetAddress
+    private ConcurrentMap<String, SubnetConfig> subnetsConfigList;
+    private ConcurrentMap<Integer, SpanConfig> spanConfigList;
+    private ConcurrentMap<String, SwitchConfig> nodeConfigList; // manually configured parameters for the node, like name and tier
+    private ConcurrentMap<Long, String> configSaveEvent;
+    private ConcurrentMap<Node, Map<String, Property>> nodeProps; // properties are maintained in default container only
+    private ConcurrentMap<NodeConnector, Map<String, Property>> nodeConnectorProps; // properties are maintained in default container only
+    private ConcurrentMap<Node, Map<String, NodeConnector>> nodeConnectorNames;
+    private IInventoryService inventoryService;
+    private Set<ISwitchManagerAware> switchManagerAware = Collections
+            .synchronizedSet(new HashSet<ISwitchManagerAware>());
+    private Set<IInventoryListener> inventoryListeners = Collections
+            .synchronizedSet(new HashSet<IInventoryListener>());
+    private Set<ISpanAware> spanAware = Collections
+            .synchronizedSet(new HashSet<ISpanAware>());
+    private byte[] MAC;
+    private static boolean hostRefresh = true;
+    private int hostRetryCount = 5;
+    private IClusterContainerServices clusterContainerService = null;
+    private String containerName = null;
+    private boolean isDefaultContainer = true;
+    
+    public enum ReasonCode {
+        SUCCESS("Success"), FAILURE("Failure"), INVALID_CONF(
+                "Invalid Configuration"), EXIST("Entry Already Exist"), CONFLICT(
+                "Configuration Conflict with Existing Entry");
+
+        private String name;
+
+        private ReasonCode(String name) {
+            this.name = name;
+        }
+
+        public String toString() {
+            return name;
+        }
+    }
+
+    public void notifySubnetChange(Subnet sub, boolean add) {
+        synchronized (switchManagerAware) {
+            for (Object subAware : switchManagerAware) {
+                try {
+                    ((ISwitchManagerAware) subAware).subnetNotify(sub, add);
+                } catch (Exception e) {
+                    log.error("Failed to notify Subnet change", e);
+                }
+            }
+        }
+    }
+
+    public void notifySpanPortChange(Node node, List<NodeConnector> ports,
+            boolean add) {
+        synchronized (spanAware) {
+            for (Object sa : spanAware) {
+                try {
+                    ((ISpanAware) sa).spanUpdate(node, ports, add);
+                } catch (Exception e) {
+                    log.error("Failed to notify Span Interface change", e);
+                }
+            }
+        }
+    }
+
+    private void notifyModeChange(Node node, boolean proactive) {
+        synchronized (switchManagerAware) {
+            for (ISwitchManagerAware service : switchManagerAware) {
+                try {
+                    service.modeChangeNotify(node, proactive);
+                } catch (Exception e) {
+                    log.error("Failed to notify Subnet change", e);
+                }
+            }
+        }
+    }
+
+    public void startUp() {
+        // Initialize configuration file names
+        subnetFileName = ROOT + "subnets" + this.getContainerName() + ".conf";
+        spanFileName = ROOT + "spanPorts_" + this.getContainerName() + ".conf";
+        switchConfigFileName = ROOT + "switchConfig_" + this.getContainerName()
+                + ".conf";
+
+        // Instantiate cluster synced variables
+        allocateCaches();
+        retrieveCaches();
+
+        /*
+         * Read startup and build database if we have not already gotten the
+         * configurations synced from another node
+         */
+        if (subnetsConfigList.isEmpty())
+            loadSubnetConfiguration();
+        if (spanConfigList.isEmpty())
+            loadSpanConfiguration();
+        if (nodeConfigList.isEmpty())
+            loadSwitchConfiguration();
+
+        MAC = getHardwareMAC();
+    }
+
+    public void shutDown() {
+        destroyCaches(this.getContainerName());
+    }
+
+    @SuppressWarnings("deprecation")
+       private void allocateCaches() {
+        if (this.clusterContainerService == null) {
+            log.info("un-initialized clusterContainerService, can't create cache");
+            return;
+        }
+
+        try {
+            clusterContainerService.createCache(
+                    "switchmanager.subnetsConfigList", EnumSet
+                            .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+            clusterContainerService.createCache("switchmanager.spanConfigList",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+            clusterContainerService.createCache("switchmanager.nodeConfigList",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+            clusterContainerService.createCache("switchmanager.subnets",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+            clusterContainerService.createCache(
+                    "switchmanager.configSaveEvent", EnumSet
+                            .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+            clusterContainerService.createCache("switchmanager.nodeProps",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+            clusterContainerService.createCache(
+                    "switchmanager.nodeConnectorProps", EnumSet
+                            .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+            clusterContainerService.createCache(
+                    "switchmanager.nodeConnectorNames", EnumSet
+                            .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+        } catch (CacheConfigException cce) {
+            log.error("\nCache configuration invalid - check cache mode");
+        } catch (CacheExistException ce) {
+            log.error("\nCache already exits - destroy and recreate if needed");
+        }
+    }
+
+    @SuppressWarnings({ "unchecked", "deprecation" })
+    private void retrieveCaches() {
+        if (this.clusterContainerService == null) {
+            log
+                    .info("un-initialized clusterContainerService, can't create cache");
+            return;
+        }
+
+        subnetsConfigList = (ConcurrentMap<String, SubnetConfig>) clusterContainerService
+                .getCache("switchmanager.subnetsConfigList");
+        if (subnetsConfigList == null) {
+            log.error("\nFailed to get cache for subnetsConfigList");
+        }
+
+        spanConfigList = (ConcurrentMap<Integer, SpanConfig>) clusterContainerService
+                .getCache("switchmanager.spanConfigList");
+        if (spanConfigList == null) {
+            log.error("\nFailed to get cache for spanConfigList");
+        }
+
+        nodeConfigList = (ConcurrentMap<String, SwitchConfig>) clusterContainerService
+                .getCache("switchmanager.nodeConfigList");
+        if (nodeConfigList == null) {
+            log.error("\nFailed to get cache for nodeConfigList");
+        }
+
+        subnets = (ConcurrentMap<InetAddress, Subnet>) clusterContainerService
+                .getCache("switchmanager.subnets");
+        if (subnets == null) {
+            log.error("\nFailed to get cache for subnets");
+        }
+
+        configSaveEvent = (ConcurrentMap<Long, String>) clusterContainerService
+                .getCache("switchmanager.configSaveEvent");
+        if (configSaveEvent == null) {
+            log.error("\nFailed to get cache for configSaveEvent");
+        }
+
+        nodeProps = (ConcurrentMap<Node, Map<String, Property>>) clusterContainerService
+                .getCache("switchmanager.nodeProps");
+        if (nodeProps == null) {
+            log.error("\nFailed to get cache for nodeProps");
+        }
+
+        nodeConnectorProps = (ConcurrentMap<NodeConnector, Map<String, Property>>) clusterContainerService
+                .getCache("switchmanager.nodeConnectorProps");
+        if (nodeConnectorProps == null) {
+            log.error("\nFailed to get cache for nodeConnectorProps");
+        }
+
+        nodeConnectorNames = (ConcurrentMap<Node, Map<String, NodeConnector>>) clusterContainerService
+                .getCache("switchmanager.nodeConnectorNames");
+        if (nodeConnectorNames == null) {
+            log.error("\nFailed to get cache for nodeConnectorNames");
+        }
+    }
+
+    void nonClusterObjectCreate() {
+        subnetsConfigList = new ConcurrentHashMap<String, SubnetConfig>();
+        spanConfigList = new ConcurrentHashMap<Integer, SpanConfig>();
+        nodeConfigList = new ConcurrentHashMap<String, SwitchConfig>();
+        subnets = new ConcurrentHashMap<InetAddress, Subnet>();
+        configSaveEvent = new ConcurrentHashMap<Long, String>();
+        nodeProps = new ConcurrentHashMap<Node, Map<String, Property>>();
+        nodeConnectorProps = new ConcurrentHashMap<NodeConnector, Map<String, Property>>();
+        nodeConnectorNames = new ConcurrentHashMap<Node, Map<String, NodeConnector>>();
+    }
+
+    @SuppressWarnings("deprecation")
+       private void destroyCaches(String container) {
+        if (this.clusterContainerService == null) {
+            log
+                    .info("un-initialized clusterContainerService, can't create cache");
+            return;
+        }
+
+        clusterContainerService.destroyCache("switchmanager.subnetsConfigList");
+        clusterContainerService.destroyCache("switchmanager.spanConfigList");
+        clusterContainerService.destroyCache("switchmanager.nodeConfigList");
+        clusterContainerService.destroyCache("switchmanager.subnets");
+        clusterContainerService.destroyCache("switchmanager.configSaveEvent");
+        clusterContainerService.destroyCache("switchmanager.nodeProps");
+        clusterContainerService
+                .destroyCache("switchmanager.nodeConnectorProps");
+        clusterContainerService
+                .destroyCache("switchmanager.nodeConnectorNames");
+        nonClusterObjectCreate();
+    }
+
+    public List<SubnetConfig> getSubnetsConfigList() {
+        return new ArrayList<SubnetConfig>(subnetsConfigList.values());
+    }
+
+    @Override
+    public SubnetConfig getSubnetConfig(String subnet) {
+        return subnetsConfigList.get(subnet);
+    }
+
+    private List<SpanConfig> getSpanConfigList(Node node) {
+        List<SpanConfig> confList = new ArrayList<SpanConfig>();
+        String nodeId = node.toString();
+        for (SpanConfig conf : spanConfigList.values()) {
+            if (conf.matchNode(nodeId)) {
+                confList.add(conf);
+            }
+        }
+        return confList;
+    }
+
+    public List<SwitchConfig> getNodeConfigList() {
+        return new ArrayList<SwitchConfig>(nodeConfigList.values());
+    }
+
+    public SwitchConfig getSwitchConfig(String switchId) {
+        return nodeConfigList.get(switchId);
+    }
+
+    public Switch getSwitchByNode(Node node) {
+        Switch sw = new Switch(node);
+        sw.setNode(node);
+
+        Set<NodeConnector> ncSet = getPhysicalNodeConnectors(node);
+        sw.setNodeConnectors(ncSet);
+
+        List<NodeConnector> ncList = new ArrayList<NodeConnector>();
+        for (NodeConnector nodeConnector : ncSet) {
+            if (spanNodeConnectors.contains(nodeConnector)) {
+                ncList.add(nodeConnector);
+            }
+        }
+        sw.addSpanPorts(ncList);
+
+        return sw;
+    }
+
+    public List<Switch> getNetworkDevices() {
+        Set<Node> nodeSet = getNodes();
+        List<Switch> swList = new ArrayList<Switch>();
+        if (nodeSet != null) {
+            for (Node node : nodeSet) {
+                swList.add(getSwitchByNode(node));
+            }
+        }
+
+        return swList;
+    }
+
+    private void updateConfig(SubnetConfig conf, boolean add) {
+        if (add) {
+            subnetsConfigList.put(conf.getName(), conf);
+        } else {
+            subnetsConfigList.remove(conf.getName());
+        }
+    }
+
+    private void updateDatabase(SubnetConfig conf, boolean add) {
+        Subnet subnet = subnets.get(conf.getIPnum());
+        if (add) {
+            if (subnet == null) {
+                subnet = new Subnet(conf);
+            }
+            // In case of API3 call we may receive the ports along with the
+            // subnet creation
+            if (!conf.isGlobal()) {
+                Set<NodeConnector> sp = conf.getSubnetNodeConnectors();
+                subnet.addNodeConnectors(sp);
+            }
+            subnets.put(conf.getIPnum(), subnet);
+        } else { // This is the deletion of the whole subnet
+            if (subnet == null)
+                return;
+            subnets.remove(conf.getIPnum());
+        }
+    }
+
+    private Status semanticCheck(SubnetConfig conf) {
+        Subnet newSubnet = new Subnet(conf);
+        Set<InetAddress> IPs = subnets.keySet();
+        if (IPs == null) {
+               return new Status(StatusCode.SUCCESS, null);
+        }
+        for (InetAddress i : IPs) {
+            Subnet existingSubnet = subnets.get(i);
+            if ((existingSubnet != null)
+                    && !existingSubnet.isMutualExclusive(newSubnet)) {
+                return new Status(StatusCode.CONFLICT, null);
+            }
+        }
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    private Status addRemoveSubnet(SubnetConfig conf, boolean add) {
+        // Valid config check
+        if (!conf.isValidConfig()) {
+               String msg = "Invalid Subnet configuration";
+            log.warn(msg);
+            return new Status(StatusCode.BADREQUEST, msg);
+        }
+
+        if (add) {
+            // Presence check
+            if (subnetsConfigList.containsKey(conf.getName())) {
+               return new Status(StatusCode.CONFLICT, 
+                               "Same subnet config already exists");
+            }
+            // Semantyc check
+            Status rc = semanticCheck(conf);
+            if (!rc.isSuccess()) {
+                return rc;
+            }
+        }
+        // Update Configuration
+        updateConfig(conf, add);
+
+        // Update Database
+        updateDatabase(conf, add);
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    /**
+     * Adds Subnet configured in GUI or API3
+     */
+    public Status addSubnet(SubnetConfig conf) {
+        return this.addRemoveSubnet(conf, true);
+    }
+
+    @Override
+    public Status removeSubnet(SubnetConfig conf) {
+        return this.addRemoveSubnet(conf, false);
+    }
+
+    @Override
+    public Status removeSubnet(String name) {
+        SubnetConfig conf = subnetsConfigList.get(name);
+        if (conf == null) {
+            return new Status(StatusCode.SUCCESS, "Subnet not present");
+        }
+        return this.addRemoveSubnet(conf, false);
+    }
+
+    @Override
+    public Status addPortsToSubnet(String name, String switchPorts) {
+        // Update Configuration
+        SubnetConfig conf = subnetsConfigList.get(name);
+        if (conf == null) {
+            return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
+        }
+        if (!conf.isValidSwitchPort(switchPorts)) {
+               return new Status(StatusCode.BADREQUEST, "Invalid switchports");
+        }
+
+        conf.addNodeConnectors(switchPorts);
+
+        // Update Database
+        Subnet sub = subnets.get(conf.getIPnum());
+        Set<NodeConnector> sp = conf.getNodeConnectors(switchPorts);
+        sub.addNodeConnectors(sp);
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    @Override
+    public Status removePortsFromSubnet(String name, String switchPorts) {
+        // Update Configuration
+        SubnetConfig conf = subnetsConfigList.get(name);
+        if (conf == null) {
+               return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
+        }
+        conf.removeNodeConnectors(switchPorts);
+
+        // Update Database
+        Subnet sub = subnets.get(conf.getIPnum());
+        Set<NodeConnector> sp = conf.getNodeConnectors(switchPorts);
+        sub.deleteNodeConnectors(sp);
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    public String getContainerName() {
+        if (containerName == null) {
+            return GlobalConstants.DEFAULT.toString();
+        }
+        return containerName;
+    }
+
+    @Override
+    public Subnet getSubnetByNetworkAddress(InetAddress networkAddress) {
+        Subnet sub;
+        Set<InetAddress> indices = subnets.keySet();
+        for (InetAddress i : indices) {
+            sub = subnets.get(i);
+            if (sub.isSubnetOf(networkAddress)) {
+                return sub;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Object readObject(ObjectInputStream ois)
+            throws FileNotFoundException, IOException, ClassNotFoundException {
+        // Perform the class deserialization locally, from inside the package
+        // where the class is defined
+        return ois.readObject();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void loadSubnetConfiguration() {
+        ObjectReader objReader = new ObjectReader();
+        ConcurrentMap<Integer, SubnetConfig> confList = (ConcurrentMap<Integer, SubnetConfig>) objReader
+                .read(this, subnetFileName);
+
+        if (confList == null) {
+            return;
+        }
+
+        for (SubnetConfig conf : confList.values()) {
+            addSubnet(conf);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void loadSpanConfiguration() {
+        ObjectReader objReader = new ObjectReader();
+        ConcurrentMap<Integer, SpanConfig> confList = (ConcurrentMap<Integer, SpanConfig>) objReader
+                .read(this, spanFileName);
+
+        if (confList == null) {
+            return;
+        }
+
+        for (SpanConfig conf : confList.values()) {
+            addSpanConfig(conf);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void loadSwitchConfiguration() {
+        ObjectReader objReader = new ObjectReader();
+        ConcurrentMap<String, SwitchConfig> confList = (ConcurrentMap<String, SwitchConfig>) objReader
+                .read(this, switchConfigFileName);
+
+        if (confList == null) {
+            return;
+        }
+
+        for (SwitchConfig conf : confList.values()) {
+            updateSwitchConfig(conf);
+        }
+    }
+
+    public void updateSwitchConfig(SwitchConfig cfgObject) {
+        boolean modeChange = false;
+
+        SwitchConfig sc = nodeConfigList.get(cfgObject.getNodeId());
+        if ((sc == null) || !cfgObject.getMode().equals(sc.getMode()))
+            modeChange = true;
+
+        nodeConfigList.put(cfgObject.getNodeId(), cfgObject);
+        try {
+            // update default container only
+            if (isDefaultContainer) {
+                String nodeId = cfgObject.getNodeId();
+                Node node = Node.fromString(nodeId);
+                Map<String, Property> propMap;
+                if (nodeProps.get(node) != null) {
+                    propMap = nodeProps.get(node);
+                } else {
+                    propMap = new HashMap<String, Property>();
+                }
+                Property name = new Name(cfgObject.getNodeName());
+                propMap.put(name.getName(), name);
+                Property tier = new Tier(Integer.parseInt(cfgObject.getTier()));
+                propMap.put(tier.getName(), tier);
+                addNodeProps(node, propMap);
+
+                log.info("Set Node {}'s Mode to {}", nodeId, cfgObject
+                        .getMode());
+
+                if (modeChange) {
+                    notifyModeChange(node, (Integer.parseInt(cfgObject
+                            .getMode()) != 0));
+                }
+            }
+        } catch (Exception e) {
+            log.debug("updateSwitchConfig: {}", e);
+        }
+    }
+
+    @Override
+    public Status saveSwitchConfig() {
+        // Publish the save config event to the cluster nodes
+        configSaveEvent.put(new Date().getTime(), SAVE);
+        return saveSwitchConfigInternal();
+    }
+
+    public Status saveSwitchConfigInternal() {
+        Status retS = null, retP = null;
+        ObjectWriter objWriter = new ObjectWriter();
+
+        retS = objWriter.write(new ConcurrentHashMap<String, SubnetConfig>(
+                subnetsConfigList), subnetFileName);
+        retP = objWriter.write(new ConcurrentHashMap<Integer, SpanConfig>(
+                spanConfigList), spanFileName);
+        retS = objWriter.write(new ConcurrentHashMap<String, SwitchConfig>(
+                nodeConfigList), switchConfigFileName);
+
+        if (retS.equals(retP)) {
+            if (retS.isSuccess()) {
+                return retS;
+            } else {
+                return new Status(StatusCode.INTERNALERROR, "Save failed");
+            }
+        } else {
+            return new Status(StatusCode.INTERNALERROR, "Partial save failure");
+        }
+    }
+
+    @Override
+    public List<SpanConfig> getSpanConfigList() {
+        return new ArrayList<SpanConfig>(spanConfigList.values());
+    }
+
+    @Override
+    public Status addSpanConfig(SpanConfig conf) {
+        // Valid config check
+        if (!conf.isValidConfig()) {
+               String msg = "Invalid Span configuration";
+            log.warn(msg);
+            return new Status(StatusCode.BADREQUEST, msg);
+        }
+
+        // Presence check
+        if (spanConfigList.containsKey(conf.hashCode())) {
+               return new Status(StatusCode.CONFLICT, "Same span config exists");
+        }
+
+        // Update database and notify clients
+        addSpanPorts(conf.getNode(), conf.getPortArrayList());
+
+        // Update configuration
+        spanConfigList.put(conf.hashCode(), conf);
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    @Override
+    public Status removeSpanConfig(SpanConfig conf) {
+        removeSpanPorts(conf.getNode(), conf.getPortArrayList());
+
+        // Update configuration
+        spanConfigList.remove(conf.hashCode());
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    @Override
+    public List<NodeConnector> getSpanPorts(Node node) {
+        List<NodeConnector> ncList = new ArrayList<NodeConnector>();
+
+        for (NodeConnector nodeConnector : spanNodeConnectors) {
+            if (nodeConnector.getNode().equals(node)) {
+                ncList.add(nodeConnector);
+            }
+        }
+        return ncList;
+    }
+
+    @Override
+    public void entryCreated(Long key, String cacheName, boolean local) {
+    }
+
+    @Override
+    public void entryUpdated(Long key, String new_value, String cacheName,
+            boolean originLocal) {
+        saveSwitchConfigInternal();
+    }
+
+    @Override
+    public void entryDeleted(Long key, String cacheName, boolean originLocal) {
+    }
+
+    private void addNode(Node node, Set<Property> props) {
+        log.trace("{} added", node);
+        if (nodeProps == null)
+            return;
+
+        Map<String, Property> propMap;
+        if (nodeProps.get(node) != null) {
+            propMap = nodeProps.get(node);
+        } else {
+            propMap = new HashMap<String, Property>();
+        }
+
+        // copy node properties from plugin
+        if (props != null) {
+            for (Property prop : props) {
+                propMap.put(prop.getName(), prop);
+            }
+        }
+
+        // copy node properties from config
+        if (nodeConfigList != null) {
+            String nodeId = node.getNodeIDString();
+            for (SwitchConfig conf : nodeConfigList.values()) {
+                if (conf.getNodeId().equals(nodeId)) {
+                    Property name = new Name(conf.getNodeName());
+                    propMap.put(name.getName(), name);
+                    Property tier = new Tier(Integer.parseInt(conf.getTier()));
+                    propMap.put(tier.getName(), tier);
+                    break;
+                }
+            }
+        }
+        addNodeProps(node, propMap);
+
+        // check if span ports are configed
+        addSpanPorts(node);
+
+        /* notify node listeners */
+        notifyNode(node, UpdateType.ADDED, propMap);
+    }
+
+    private void removeNode(Node node) {
+        log.trace("{} removed", node);
+        if (nodeProps == null)
+            return;
+        nodeProps.remove(node);
+
+        // check if span ports need to be cleaned up
+        removeSpanPorts(node);
+
+        /* notify node listeners */
+        notifyNode(node, UpdateType.REMOVED, null);
+    }
+
+    private void updateNode(Node node, Set<Property> props) {
+        log.trace("{} updated", node);
+        if (nodeProps == null) {
+            return;
+        }
+
+        Map<String, Property> propMap;
+        if (nodeProps.get(node) != null) {
+            propMap = nodeProps.get(node);
+        } else {
+            propMap = new HashMap<String, Property>();
+        }
+
+        // copy node properties from plugin
+        if (props != null) {
+            for (Property prop : props) {
+                propMap.put(prop.getName(), prop);
+            }
+        }
+        addNodeProps(node, propMap);
+
+        /* notify node listeners */
+        notifyNode(node, UpdateType.CHANGED, propMap);
+    }
+
+    @Override
+    public void updateNode(Node node, UpdateType type, Set<Property> props) {
+        switch (type) {
+        case ADDED:
+            addNode(node, props);
+            break;
+        case CHANGED:
+            updateNode(node, props);
+            break;
+        case REMOVED:
+            removeNode(node);
+            break;
+        default:
+            break;
+        }
+    }
+
+    @Override
+    public void updateNodeConnector(NodeConnector nodeConnector,
+            UpdateType type, Set<Property> props) {
+        Node node = nodeConnector.getNode();
+        Map<String, Property> propMap = new HashMap<String, Property>();
+
+        log.trace("{} {}", nodeConnector, type);
+
+        if (nodeConnectorProps == null)
+            return;
+
+        switch (type) {
+        case ADDED:
+        case CHANGED:
+            if (props != null) {
+                for (Property prop : props) {
+                    addNodeConnectorProp(nodeConnector, prop);
+                    propMap.put(prop.getName(), prop);
+                }
+            } else {
+                addNodeConnectorProp(nodeConnector, null);
+                addNodeProps(node, null);
+            }
+
+            // check if span is configed
+            addSpanPort(nodeConnector);
+            break;
+        case REMOVED:
+            removeNodeConnectorAllProps(nodeConnector);
+            removeNodeProps(node);
+
+            // clean up span config
+            removeSpanPort(nodeConnector);
+            break;
+        default:
+            break;
+        }
+
+        notifyNodeConnector(nodeConnector, type, propMap);
+    }
+
+    @Override
+    public Set<Node> getNodes() {
+        return (nodeProps != null) ? new HashSet<Node>(nodeProps.keySet())
+                : null;
+    }
+
+    /*
+     * test utility function which assumes all nodes are OF nodes
+     */
+    private Node getNode(Long id) {
+        Set<Node> nodes = getNodes();
+        if (nodes != null) {
+            for (Node node : nodes) {
+                if (id == (Long) node.getID()) {
+                    return node;
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Returns a copy of a list of properties for a given node
+     *
+     * (non-Javadoc)
+     * @see org.opendaylight.controller.switchmanager.ISwitchManager#getNodeProps(org.opendaylight.controller.sal.core.Node)
+     */
+    @Override
+    public Map<String, Property> getNodeProps(Node node) {
+        if (isDefaultContainer) {
+            Map<String, Property> rv = null;
+            if (this.nodeProps != null) {
+                rv = this.nodeProps.get(node);
+                if (rv != null) {
+                    /* make a copy of it */
+                    rv = new HashMap<String, Property>(rv);
+                }
+            }
+            return rv;
+        } else {
+            // get it from default container
+            ISwitchManager defaultSwitchManager = (ISwitchManager) ServiceHelper
+                    .getInstance(ISwitchManager.class, GlobalConstants.DEFAULT
+                            .toString(), this);
+            return defaultSwitchManager.getNodeProps(node);
+        }
+    }
+
+    @Override
+    public Property getNodeProp(Node node, String propName) {
+        Map<String, Property> propMap = getNodeProps(node);
+        return (propMap != null) ? propMap.get(propName) : null;
+    }
+
+    @Override
+    public void setNodeProp(Node node, Property prop) {
+        /* Get a copy of the property map */
+        Map<String, Property> propMap = getNodeProps(node);
+        if (propMap == null)
+            return;
+
+        propMap.put(prop.getName(), prop);
+        this.nodeProps.put(node, propMap);
+    }
+
+    @Override
+    public Status removeNodeProp(Node node, String propName) {
+        Map<String, Property> propMap = getNodeProps(node);
+        if (propMap != null) {
+               propMap.remove(propName);
+               this.nodeProps.put(node, propMap);
+        }
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    @Override
+    public Status removeNodeAllProps(Node node) {
+        this.nodeProps.remove(node);
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    @Override
+    public Set<NodeConnector> getUpNodeConnectors(Node node) {
+        if (nodeConnectorProps == null)
+            return null;
+
+        Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
+        for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+            if (((Long) nodeConnector.getNode().getID())
+                    .longValue() != (Long) node.getID())
+                continue;
+            if (isNodeConnectorEnabled(nodeConnector))
+                nodeConnectorSet.add(nodeConnector);
+        }
+
+        return nodeConnectorSet;
+    }
+
+    @Override
+    public Set<NodeConnector> getNodeConnectors(Node node) {
+        if (nodeConnectorProps == null)
+            return null;
+
+        Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
+        for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+            if (((Long) nodeConnector.getNode().getID())
+                    .longValue() != (Long) node.getID())
+                continue;
+            nodeConnectorSet.add(nodeConnector);
+        }
+
+        return nodeConnectorSet;
+    }
+
+    @Override
+    public Set<NodeConnector> getPhysicalNodeConnectors(Node node) {
+        if (nodeConnectorProps == null)
+            return null;
+
+        Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
+        for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+            if (!nodeConnector.getNode().equals(node)
+                    || isSpecial(nodeConnector)) {
+                continue;
+            }
+            nodeConnectorSet.add(nodeConnector);
+        }
+
+        return nodeConnectorSet;
+    }
+
+    /*
+     * testing utility function which assumes we are dealing with OF Node nodeconnectors only
+     */
+    @SuppressWarnings("unused")
+    private Set<Long> getEnabledNodeConnectorIds(Node node) {
+        Set<Long> ids = new HashSet<Long>();
+        Set<NodeConnector> nodeConnectors = getUpNodeConnectors(node);
+
+        if (nodeConnectors != null) {
+            for (NodeConnector nodeConnector : nodeConnectors) {
+                ids.add((Long) nodeConnector.getID());
+            }
+        }
+
+        return ids;
+    }
+
+    @Override
+    public Map<String, Property> getNodeConnectorProps(
+            NodeConnector nodeConnector) {
+        if (isDefaultContainer) {
+            Map<String, Property> rv = null;
+            if (this.nodeConnectorProps != null) {
+                rv = this.nodeConnectorProps.get(nodeConnector);
+                if (rv != null) {
+                    rv = new HashMap<String, Property>(rv);
+                }
+            }
+            return rv;
+        } else {
+            // get it from default container
+            ISwitchManager defaultSwitchManager = (ISwitchManager) ServiceHelper
+                    .getInstance(ISwitchManager.class, GlobalConstants.DEFAULT
+                            .toString(), this);
+            return defaultSwitchManager.getNodeConnectorProps(nodeConnector);
+        }
+    }
+
+    @Override
+    public Property getNodeConnectorProp(NodeConnector nodeConnector,
+            String propName) {
+        Map<String, Property> propMap = getNodeConnectorProps(nodeConnector);
+        return (propMap != null) ? propMap.get(propName) : null;
+    }
+
+    @Override
+    public List<Map<String, String>> getListNodeIdNameMap() {
+        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
+        Set<Node> nset = this.nodeProps.keySet();
+        if (nset == null) {
+            return list;
+        }
+        for (Node node : nset) {
+            Map<String, String> map = new HashMap<String, String>(2);
+            Name nodeName = (Name) getNodeProp(node, Name.NamePropName);
+            map.put("nodeId", node.toString());
+            map.put("nodeName", ((nodeName == null) || nodeName.getValue()
+                    .equals("")) ? node.toString() : nodeName.getValue());
+            list.add(map);
+        }
+        return list;
+    }
+
+    private byte[] getHardwareMAC() {
+        Enumeration<NetworkInterface> nis;
+        try {
+            nis = NetworkInterface.getNetworkInterfaces();
+        } catch (SocketException e1) {
+            e1.printStackTrace();
+            return null;
+        }
+        byte[] MAC = null;
+        for (; nis.hasMoreElements();) {
+            NetworkInterface ni = nis.nextElement();
+            try {
+                MAC = ni.getHardwareAddress();
+            } catch (SocketException e) {
+                e.printStackTrace();
+            }
+            if (MAC != null) {
+                return MAC;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public byte[] getControllerMAC() {
+        return MAC;
+    }
+
+    @Override
+    public boolean isHostRefreshEnabled() {
+        return hostRefresh;
+    }
+
+    @Override
+    public int getHostRetryCount() {
+        return hostRetryCount;
+    }
+
+    @Override
+    public NodeConnector getNodeConnector(Node node, String nodeConnectorName) {
+        if (nodeConnectorNames == null)
+            return null;
+
+        Map<String, NodeConnector> map = nodeConnectorNames.get(node);
+        if (map == null)
+            return null;
+
+        return map.get(nodeConnectorName);
+    }
+
+    /**
+     * Adds a node connector and its property if any
+     * 
+     * @param nodeConnector {@link org.opendaylight.controller.sal.core.NodeConnector}
+     * @param propName                 name of {@link org.opendaylight.controller.sal.core.Property}
+     * @return success or failed reason
+     */
+    @Override
+    public Status addNodeConnectorProp(NodeConnector nodeConnector, Property prop) {
+        Map<String, Property> propMap = getNodeConnectorProps(nodeConnector);
+
+        if (propMap == null) {
+            propMap = new HashMap<String, Property>();
+        }
+
+        // Just add the nodeConnector if prop is not available (in a non-default container)
+        if (prop == null) {
+            nodeConnectorProps.put(nodeConnector, propMap);
+            return new Status(StatusCode.SUCCESS, null);
+        }
+
+        propMap.put(prop.getName(), prop);
+        nodeConnectorProps.put(nodeConnector, propMap);
+
+        if (prop.getName().equals(Name.NamePropName)) {
+            if (nodeConnectorNames != null) {
+                Node node = nodeConnector.getNode();
+                Map<String, NodeConnector> map = nodeConnectorNames.get(node);
+                if (map == null) {
+                    map = new HashMap<String, NodeConnector>();
+                }
+
+                map.put(((Name) prop).getValue(), nodeConnector);
+                nodeConnectorNames.put(node, map);
+            }
+        }
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    /**
+     * Removes one property of a node connector
+     * 
+     * @param nodeConnector {@link org.opendaylight.controller.sal.core.NodeConnector}
+     * @param propName                 name of {@link org.opendaylight.controller.sal.core.Property}
+     * @return success or failed reason
+     */
+    @Override
+    public Status removeNodeConnectorProp(NodeConnector nodeConnector, String propName) {
+        Map<String, Property> propMap = getNodeConnectorProps(nodeConnector);
+
+        if (propMap == null) {
+               /* Nothing to remove */
+            return new Status(StatusCode.SUCCESS, null);
+        }
+
+        propMap.remove(propName);
+        nodeConnectorProps.put(nodeConnector, propMap);
+
+        if (nodeConnectorNames != null) {
+            Name name = ((Name) getNodeConnectorProp(nodeConnector,
+                    Name.NamePropName));
+            if (name != null) {
+                Node node = nodeConnector.getNode();
+                Map<String, NodeConnector> map = nodeConnectorNames.get(node);
+                if (map != null) {
+                    map.remove(name.getValue());
+                    nodeConnectorNames.put(node, map);
+                }
+            }
+        }
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    /**
+     * Removes all the properties of a node connector
+     * 
+     * @param nodeConnector {@link org.opendaylight.controller.sal.core.NodeConnector}
+     * @return success or failed reason
+     */
+    @Override
+    public Status removeNodeConnectorAllProps(NodeConnector nodeConnector) {
+        if (nodeConnectorNames != null) {
+            Name name = ((Name) getNodeConnectorProp(nodeConnector,
+                    Name.NamePropName));
+            if (name != null) {
+                Node node = nodeConnector.getNode();
+                Map<String, NodeConnector> map = nodeConnectorNames.get(node);
+                if (map != null) {
+                    map.remove(name.getValue());
+                    nodeConnectorNames.put(node, map);
+                }
+            }
+        }
+        nodeConnectorProps.remove(nodeConnector);
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    /**
+     * Function called by the dependency manager when all the required
+     * dependencies are satisfied
+     *
+     */
+    void init(Component c) {
+        Dictionary<?, ?> props = c.getServiceProperties();
+        if (props != null) {
+            this.containerName = (String) props.get("containerName");
+            log.trace("Running containerName:" + this.containerName);
+        } else {
+            // In the Global instance case the containerName is empty
+            this.containerName = "";
+        }
+        isDefaultContainer = containerName.equals(GlobalConstants.DEFAULT
+                .toString());
+
+        startUp();
+    }
+
+    /**
+     * Function called by the dependency manager when at least one
+     * dependency become unsatisfied or when the component is shutting
+     * down because for example bundle is being stopped.
+     *
+     */
+    void destroy() {
+        shutDown();
+    }
+
+    /**
+     * Function called by dependency manager after "init ()" is called
+     * and after the services provided by the class are registered in
+     * the service registry
+     *
+     */
+    void start() {
+        // OSGI console
+        registerWithOSGIConsole();
+    }
+
+    /**
+     * Function called after registered the
+     * service in OSGi service registry.
+     */
+    void started() {
+        // solicit for existing inventories
+        getInventories();
+    }
+
+    /**
+     * Function called by the dependency manager before the services
+     * exported by the component are unregistered, this will be
+     * followed by a "destroy ()" calls
+     *
+     */
+    void stop() {
+    }
+
+    public void setInventoryService(IInventoryService service) {
+        log.trace("Got inventory service set request {}", service);
+        this.inventoryService = service;
+
+        // solicit for existing inventories
+        getInventories();
+    }
+
+    public void unsetInventoryService(IInventoryService service) {
+        log.trace("Got a service UNset request");
+        this.inventoryService = null;
+
+        // clear existing inventories
+        clearInventories();
+    }
+
+    public void setSwitchManagerAware(ISwitchManagerAware service) {
+        log.trace("Got inventory service set request {}", service);
+        if (this.switchManagerAware != null) {
+            this.switchManagerAware.add(service);
+        }
+
+        // bulk update for newly joined
+        switchManagerAwareNotify(service);
+    }
+
+    public void unsetSwitchManagerAware(ISwitchManagerAware service) {
+        log.trace("Got a service UNset request");
+        if (this.switchManagerAware != null) {
+            this.switchManagerAware.remove(service);
+        }
+    }
+
+    public void setInventoryListener(IInventoryListener service) {
+        log.trace("Got inventory listener set request {}", service);
+        if (this.inventoryListeners != null) {
+            this.inventoryListeners.add(service);
+        }
+
+        // bulk update for newly joined
+        bulkUpdateService(service);
+    }
+
+    public void unsetInventoryListener(IInventoryListener service) {
+        log.trace("Got a service UNset request");
+        if (this.inventoryListeners != null) {
+            this.inventoryListeners.remove(service);
+        }
+    }
+
+    public void setSpanAware(ISpanAware service) {
+        log.trace("Got SpanAware set request {}", service);
+        if (this.spanAware != null) {
+            this.spanAware.add(service);
+        }
+
+        // bulk update for newly joined
+        spanAwareNotify(service);
+    }
+
+    public void unsetSpanAware(ISpanAware service) {
+        log.trace("Got a service UNset request");
+        if (this.spanAware != null) {
+            this.spanAware.remove(service);
+        }
+    }
+
+    void setClusterContainerService(IClusterContainerServices s) {
+        log.trace("Cluster Service set");
+        this.clusterContainerService = s;
+    }
+
+    void unsetClusterContainerService(IClusterContainerServices s) {
+        if (this.clusterContainerService == s) {
+            log.trace("Cluster Service removed!");
+            this.clusterContainerService = null;
+        }
+    }
+
+    private void getInventories() {
+        if (inventoryService == null) {
+            log.trace("inventory service not avaiable");
+            return;
+        }
+
+        nodeProps = this.inventoryService.getNodeProps();
+        Set<Node> nodeSet = nodeProps.keySet();
+        if (nodeSet != null) {
+            for (Node node : nodeSet) {
+                addNode(node, null);
+            }
+        }
+
+        nodeConnectorProps = inventoryService.getNodeConnectorProps();
+    }
+
+    private void clearInventories() {
+        nodeProps.clear();
+        nodeConnectorProps.clear();
+        nodeConnectorNames.clear();
+        spanNodeConnectors.clear();
+    }
+
+    private void notifyNode(Node node, UpdateType type,
+            Map<String, Property> propMap) {
+        synchronized (inventoryListeners) {
+            for (IInventoryListener service : inventoryListeners) {
+                service.notifyNode(node, type, propMap);
+            }
+        }
+    }
+
+    private void notifyNodeConnector(NodeConnector nodeConnector,
+            UpdateType type, Map<String, Property> propMap) {
+        synchronized (inventoryListeners) {
+            for (IInventoryListener service : inventoryListeners) {
+                service.notifyNodeConnector(nodeConnector, type, propMap);
+            }
+        }
+    }
+
+    /*
+     * For those joined late, bring them up-to-date.
+     */
+    private void switchManagerAwareNotify(ISwitchManagerAware service) {
+        for (Subnet sub : subnets.values()) {
+            service.subnetNotify(sub, true);
+        }
+
+        for (Node node : getNodes()) {
+            SwitchConfig sc = getSwitchConfig(node.toString());
+            if ((sc != null) && isDefaultContainer) {
+                service.modeChangeNotify(node,
+                        (Integer.parseInt(sc.getMode()) != 0));
+            }
+        }
+    }
+
+    private void bulkUpdateService(IInventoryListener service) {
+        for (Node node : getNodes()) {
+            service.notifyNode(node, UpdateType.ADDED, null);
+        }
+
+        Map<String, Property> propMap = new HashMap<String, Property>();
+        propMap.put(State.StatePropName, new State(State.EDGE_UP));
+        for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+            if (isNodeConnectorEnabled(nodeConnector)) {
+                service.notifyNodeConnector(nodeConnector, UpdateType.ADDED,
+                        propMap);
+            }
+        }
+    }
+
+    private void spanAwareNotify(ISpanAware service) {
+        for (Node node : getNodes()) {
+            for (SpanConfig conf : getSpanConfigList(node)) {
+                service.spanUpdate(node, conf.getPortArrayList(), true);
+            }
+        }
+    }
+
+    private void registerWithOSGIConsole() {
+        BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
+                .getBundleContext();
+        bundleContext.registerService(CommandProvider.class.getName(), this,
+                null);
+    }
+
+    @Override
+    public Boolean isNodeConnectorEnabled(NodeConnector nodeConnector) {
+        if (nodeConnector == null)
+            return false;
+
+        Config config = (Config) getNodeConnectorProp(nodeConnector,
+                Config.ConfigPropName);
+        State state = (State) getNodeConnectorProp(nodeConnector,
+                State.StatePropName);
+        return ((config != null) && (config.getValue() == Config.ADMIN_UP)
+                && (state != null) && (state.getValue() == State.EDGE_UP));
+    }
+
+    @Override
+    public String getHelp() {
+        StringBuffer help = new StringBuffer();
+        help.append("---Switch Manager---\n");
+        help.append("\t pns                    - Print connected nodes\n");
+        help
+                .append("\t pncs <node id>         - Print node connectors for a given node\n");
+        help
+                .append("\t pencs <node id>        - Print enabled node connectors for a given node\n");
+        help
+                .append("\t pdm <node id>          - Print switch ports in device map\n");
+        help.append("\t snt <node id> <tier>   - Set node tier number\n");
+        help
+                .append("\t hostRefresh <on/off/?> - Enable/Disable/Query host refresh\n");
+        help.append("\t hostRetry <count>      - Set host retry count\n");
+        return help.toString();
+    }
+
+    public void _pns(CommandInterpreter ci) {
+        ci
+                .println("           Node                       Type             Name             Tier");
+        if (nodeProps == null) {
+            return;
+        }
+        Set<Node> nodeSet = nodeProps.keySet();
+        if (nodeSet == null) {
+            return;
+        }
+        for (Node node : nodeSet) {
+            Name name = ((Name) getNodeProp(node, Name.NamePropName));
+            Tier tier = ((Tier) getNodeProp(node, Tier.TierPropName));
+            String nodeName = (name == null) ? "" : name.getValue();
+            int tierNum = (tier == null) ? 0 : tier.getValue();
+            ci.println(node + "            " + node.getType()
+                    + "            " + nodeName + "            " + tierNum);
+        }
+    }
+
+    public void _pencs(CommandInterpreter ci) {
+        String st = ci.nextArgument();
+        if (st == null) {
+            ci.println("Please enter node id");
+            return;
+        }
+        Long id = Long.decode(st);
+
+        Node node = NodeCreator.createOFNode(id);
+        Set<NodeConnector> nodeConnectorSet = getUpNodeConnectors(node);
+        if (nodeConnectorSet == null) {
+            return;
+        }
+        for (NodeConnector nodeConnector : nodeConnectorSet) {
+            if (nodeConnector == null) {
+                continue;
+            }
+            ci.println(nodeConnector);
+        }
+    }
+
+    public void _pncs(CommandInterpreter ci) {
+        String st = ci.nextArgument();
+        if (st == null) {
+            ci.println("Please enter node id");
+            return;
+        }
+        Long id = Long.decode(st);
+
+        ci.println("          NodeConnector               BandWidth(Gbps)     Admin     State");
+        Node node = NodeCreator.createOFNode(id);
+        Set<NodeConnector> nodeConnectorSet = getNodeConnectors(node);
+        if (nodeConnectorSet == null) {
+            return;
+        }
+        for (NodeConnector nodeConnector : nodeConnectorSet) {
+            if (nodeConnector == null) {
+                continue;
+            }
+            Map<String, Property> propMap = getNodeConnectorProps(nodeConnector);
+            Bandwidth bw = (Bandwidth) propMap.get(Bandwidth.BandwidthPropName);
+            Config config = (Config) propMap.get(Config.ConfigPropName);
+            State state = (State) propMap.get(State.StatePropName);
+            String out = nodeConnector + "           ";
+            out += (bw != null) ? bw.getValue() / Math.pow(10, 9) : "    ";
+            out += "             ";
+            out += (config != null) ? config.getValue() : " ";
+            out += "          ";
+            out += (state != null) ? state.getValue() : " ";
+            ci.println(out);
+        }
+    }
+
+    public void _pdm(CommandInterpreter ci) {
+        String st = ci.nextArgument();
+        if (st == null) {
+            ci.println("Please enter node id");
+            return;
+        }
+        Object id = Long.decode(st);
+        Switch sw = getSwitchByNode(NodeCreator.createOFNode((Long) id));
+
+        ci
+                .println("          NodeConnector                        Name");
+        if (sw == null) {
+            return;
+        }
+        Set<NodeConnector> nodeConnectorSet = sw.getNodeConnectors();
+        String nodeConnectorName;
+        if (nodeConnectorSet != null && nodeConnectorSet.size() > 0) {
+            for (NodeConnector nodeConnector : nodeConnectorSet) {
+                Map<String, Property> propMap = getNodeConnectorProps(nodeConnector);
+                nodeConnectorName = (propMap == null) ? null : ((Name) propMap
+                        .get(Name.NamePropName)).getValue();
+                if (nodeConnectorName != null) {
+                    Node node = nodeConnector.getNode();
+                    if (!node.equals(getNode((Long) id))) {
+                        log.debug("node not match {} {}", node,
+                                getNode((Long) id));
+                    }
+                    Map<String, NodeConnector> map = nodeConnectorNames
+                            .get(node);
+                    if (map != null) {
+                        NodeConnector nc = map.get(nodeConnectorName);
+                        if (nc == null) {
+                            log.debug("no nodeConnector named {}",
+                                    nodeConnectorName);
+                        } else if (!nc.equals(nodeConnector)) {
+                            log.debug("nodeConnector not match {} {}", nc,
+                                    nodeConnector);
+                        }
+                    }
+                }
+
+                ci
+                        .println(nodeConnector
+                                + "            "
+                                + ((nodeConnectorName == null) ? ""
+                                        : nodeConnectorName) + "("
+                                + nodeConnector.getID() + ")");
+            }
+        }
+    }
+
+    public void _snt(CommandInterpreter ci) {
+        String st = ci.nextArgument();
+        if (st == null) {
+            ci.println("Please enter node id");
+            return;
+        }
+        Long id = Long.decode(st);
+
+        Node node = NodeCreator.createOFNode(id);
+
+        st = ci.nextArgument();
+        if (st == null) {
+            ci.println("Please enter tier number");
+            return;
+        }
+        Integer tid = Integer.decode(st);
+        Tier tier = new Tier(tid);
+        setNodeProp(node, tier);
+    }
+
+    public void _hostRefresh(CommandInterpreter ci) {
+        String mode = ci.nextArgument();
+        if (mode == null) {
+            ci.println("expecting on/off/?");
+            return;
+        }
+        if (mode.toLowerCase().equals("on"))
+            hostRefresh = true;
+        else if (mode.toLowerCase().equals("off"))
+            hostRefresh = false;
+        else if (mode.equals("?")) {
+            if (hostRefresh)
+                ci.println("host refresh is ON");
+            else
+                ci.println("host refresh is OFF");
+        } else
+            ci.println("expecting on/off/?");
+        return;
+    }
+
+    public void _hostRetry(CommandInterpreter ci) {
+        String retry = ci.nextArgument();
+        if (retry == null) {
+            ci.println("Please enter a valid number. Current retry count is "
+                    + hostRetryCount);
+            return;
+        }
+        try {
+            hostRetryCount = Integer.parseInt(retry);
+        } catch (Exception e) {
+            ci.println("Please enter a valid number");
+        }
+        return;
+    }
+
+    @Override
+    public byte[] getNodeMAC(Node node) {
+        if (node.getType().equals(Node.NodeIDType.OPENFLOW)) {
+            byte[] gmac = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+            long dpid = (Long) node.getID();
+
+            for (short i = 0; i < 6; i++) {
+                gmac[5 - i] = (byte) dpid;
+                dpid >>= 8;
+            }
+            return gmac;
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isSpecial(NodeConnector p) {
+        if (p.equals(NodeConnectorIDType.CONTROLLER) ||
+            p.equals(NodeConnectorIDType.ALL) ||
+            p.equals(NodeConnectorIDType.SWSTACK) ||
+            p.equals(NodeConnectorIDType.HWPATH)) {
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * Add span configuration to local cache and notify clients
+     */
+    private void addSpanPorts(Node node, List<NodeConnector> nodeConncetors) {
+        List<NodeConnector> ncLists = new ArrayList<NodeConnector>();
+
+        for (NodeConnector nodeConnector : nodeConncetors) {
+            if (!spanNodeConnectors.contains(nodeConnector)) {
+                ncLists.add(nodeConnector);
+            }
+        }
+
+        if (ncLists.size() > 0) {
+            spanNodeConnectors.addAll(ncLists);
+            notifySpanPortChange(node, ncLists, true);
+        }
+    }
+
+    private void addSpanPorts(Node node) {
+        for (SpanConfig conf : getSpanConfigList(node)) {
+            addSpanPorts(node, conf.getPortArrayList());
+        }
+    }
+
+    private void addSpanPort(NodeConnector nodeConncetor) {
+        List<NodeConnector> ncLists = new ArrayList<NodeConnector>();
+        ncLists.add(nodeConncetor);
+        addSpanPorts(nodeConncetor.getNode(), ncLists);
+    }
+
+    /*
+     * Remove span configuration to local cache and notify clients
+     */
+    private void removeSpanPorts(Node node, List<NodeConnector> nodeConncetors) {
+        List<NodeConnector> ncLists = new ArrayList<NodeConnector>();
+
+        for (NodeConnector nodeConnector : nodeConncetors) {
+            if (!spanNodeConnectors.contains(nodeConnector)) {
+                ncLists.add(nodeConnector);
+            }
+        }
+
+        if (ncLists.size() > 0) {
+            spanNodeConnectors.removeAll(ncLists);
+            notifySpanPortChange(node, ncLists, false);
+        }
+    }
+
+    private void removeSpanPorts(Node node) {
+        for (SpanConfig conf : getSpanConfigList(node)) {
+            addSpanPorts(node, conf.getPortArrayList());
+        }
+    }
+
+    private void removeSpanPort(NodeConnector nodeConncetor) {
+        List<NodeConnector> ncLists = new ArrayList<NodeConnector>();
+        ncLists.add(nodeConncetor);
+        removeSpanPorts(nodeConncetor.getNode(), ncLists);
+    }
+
+    private void addNodeProps(Node node, Map<String, Property> propMap) {
+        if (propMap == null) {
+            propMap = new HashMap<String, Property>();
+        }
+        nodeProps.put(node, propMap);
+    }
+
+    private void removeNodeProps(Node node) {
+        if (getUpNodeConnectors(node).size() == 0) {
+            nodeProps.remove(node);
+        }
+    }
+
+    @Override
+    public Status saveConfiguration() {
+        return saveSwitchConfig();
+    }
+
+       /**
+        * Creates a Name/Tier/Bandwidth Property object based on given property
+        * name and value. Other property types are not supported yet.
+        * 
+        * @param propName  Name of the Property
+        * @param propValue Value of the Property
+        * @return {@link org.opendaylight.controller.sal.core.Property}
+        */
+       @Override
+       public Property createProperty(String propName, String propValue) {
+               if (propName == null) {
+                       log.debug("propName is null");
+                       return null;
+               }
+               if (propValue == null) {
+                       log.debug("propValue is null");
+                       return null;
+               }
+               
+               try {
+                       if (propName.equalsIgnoreCase(Name.NamePropName)) {
+                               return new Name(propValue);
+                       } else if (propName.equalsIgnoreCase(Tier.TierPropName)) {
+                               int tier = Integer.parseInt(propValue);
+                               return new Tier(tier);
+                       } else if (propName.equalsIgnoreCase(Bandwidth.BandwidthPropName)) {
+                               long bw = Long.parseLong(propValue);
+                               return new Bandwidth(bw);
+                       } else {
+                               log.debug("Not able to create {} property", propName);
+                       }
+               } catch (Exception e) {
+                       log.debug(e.getMessage());
+               }
+
+               return null;
+    }
+}