Improve iteration over subnets map by using entrySet instead of keyset.
[controller.git] / opendaylight / switchmanager / implementation / src / main / java / org / opendaylight / controller / switchmanager / internal / SwitchManager.java
index 0705984bfe334b0eaff2ef2a0c3675c615ec3821..f895ca0c85bd4583405453d65fcd4195dcb8b5ce 100644 (file)
@@ -16,7 +16,6 @@ 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;
@@ -34,7 +33,6 @@ 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;
@@ -54,6 +52,7 @@ 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.reader.NodeDescription;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.HexEncode;
 import org.opendaylight.controller.sal.utils.IObjectReader;
@@ -61,6 +60,7 @@ import org.opendaylight.controller.sal.utils.ObjectReader;
 import org.opendaylight.controller.sal.utils.ObjectWriter;
 import org.opendaylight.controller.sal.utils.Status;
 import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.statisticsmanager.IStatisticsManager;
 import org.opendaylight.controller.switchmanager.IInventoryListener;
 import org.opendaylight.controller.switchmanager.ISpanAware;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
@@ -83,39 +83,47 @@ import org.slf4j.LoggerFactory;
  * instance per container of the network. All the node/nodeConnector properties
  * are maintained in the default container only.
  */
-public class SwitchManager implements ISwitchManager,
-IConfigurationContainerAware, IObjectReader,
-ICacheUpdateAware<Long, String>, IListenInventoryUpdates,
-CommandProvider {
+public class SwitchManager implements ISwitchManager, IConfigurationContainerAware,
+                                      IObjectReader, IListenInventoryUpdates, CommandProvider {
     private static Logger log = LoggerFactory.getLogger(SwitchManager.class);
     private static String ROOT = GlobalConstants.STARTUPHOME.toString();
-    private static final String SAVE = "Save";
     private String subnetFileName, spanFileName, switchConfigFileName;
     private final List<NodeConnector> spanNodeConnectors = new CopyOnWriteArrayList<NodeConnector>();
-    // set of Subnets keyed by the InetAddress
+    // Collection of Subnets keyed by the InetAddress
     private ConcurrentMap<InetAddress, Subnet> subnets;
     private ConcurrentMap<String, SubnetConfig> subnetsConfigList;
     private ConcurrentMap<SpanConfig, SpanConfig> spanConfigList;
     // manually configured parameters for the node such as name, tier, mode
     private ConcurrentMap<String, SwitchConfig> nodeConfigList;
-    private ConcurrentMap<Long, String> configSaveEvent;
     private ConcurrentMap<Node, Map<String, Property>> nodeProps;
     private ConcurrentMap<NodeConnector, Map<String, Property>> nodeConnectorProps;
     private ConcurrentMap<Node, Map<String, NodeConnector>> nodeConnectorNames;
     private ConcurrentMap<String, Property> controllerProps;
     private IInventoryService inventoryService;
+    private IStatisticsManager statisticsManager;
     private final Set<ISwitchManagerAware> switchManagerAware = Collections
             .synchronizedSet(new HashSet<ISwitchManagerAware>());
     private final Set<IInventoryListener> inventoryListeners = Collections
             .synchronizedSet(new HashSet<IInventoryListener>());
     private final Set<ISpanAware> spanAware = Collections.synchronizedSet(new HashSet<ISpanAware>());
-    private static boolean hostRefresh = true;
-    private int hostRetryCount = 5;
     private IClusterContainerServices clusterContainerService = null;
     private String containerName = null;
     private boolean isDefaultContainer = true;
     private static final int REPLACE_RETRY = 1;
 
+    /* Information about the default subnet. If there have been no configured subnets, i.e.,
+     * subnets.size() == 0 or subnetsConfigList.size() == 0, then this subnet will be the
+     * only subnet returned. As soon as a user-configured subnet is created this one will
+     * vanish.
+     */
+    protected static SubnetConfig DEFAULT_SUBNETCONFIG;
+    protected static Subnet DEFAULT_SUBNET;
+    protected static String DEFAULT_SUBNET_NAME = "default (cannot be modifed)";
+    static{
+        DEFAULT_SUBNETCONFIG = new SubnetConfig(DEFAULT_SUBNET_NAME, "0.0.0.0/0", new ArrayList<String>());
+        DEFAULT_SUBNET = new Subnet(DEFAULT_SUBNETCONFIG);
+    }
+
     public void notifySubnetChange(Subnet sub, boolean add) {
         synchronized (switchManagerAware) {
             for (Object subAware : switchManagerAware) {
@@ -196,7 +204,6 @@ CommandProvider {
     public void shutDown() {
     }
 
-    @SuppressWarnings("deprecation")
     private void allocateCaches() {
         if (this.clusterContainerService == null) {
             this.nonClusterObjectCreate();
@@ -207,27 +214,24 @@ CommandProvider {
         try {
             clusterContainerService.createCache(
                     "switchmanager.subnetsConfigList",
-                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
             clusterContainerService.createCache("switchmanager.spanConfigList",
-                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
             clusterContainerService.createCache("switchmanager.nodeConfigList",
-                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
             clusterContainerService.createCache("switchmanager.subnets",
-                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
-            clusterContainerService.createCache(
-                    "switchmanager.configSaveEvent",
-                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
             clusterContainerService.createCache("switchmanager.nodeProps",
-                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
             clusterContainerService.createCache(
                     "switchmanager.nodeConnectorProps",
-                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
             clusterContainerService.createCache(
                     "switchmanager.nodeConnectorNames",
-                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
             clusterContainerService.createCache(
                     "switchmanager.controllerProps",
-                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
         } catch (CacheConfigException cce) {
             log.error("\nCache configuration invalid - check cache mode");
         } catch (CacheExistException ce) {
@@ -235,7 +239,7 @@ CommandProvider {
         }
     }
 
-    @SuppressWarnings({ "unchecked", "deprecation" })
+    @SuppressWarnings({ "unchecked" })
     private void retrieveCaches() {
         if (this.clusterContainerService == null) {
             log.info("un-initialized clusterContainerService, can't create cache");
@@ -266,12 +270,6 @@ CommandProvider {
             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) {
@@ -302,7 +300,6 @@ CommandProvider {
         spanConfigList = new ConcurrentHashMap<SpanConfig, 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>>();
@@ -311,12 +308,22 @@ CommandProvider {
 
     @Override
     public List<SubnetConfig> getSubnetsConfigList() {
-        return new ArrayList<SubnetConfig>(subnetsConfigList.values());
+        // if there are no subnets, return the default subnet
+        if(subnetsConfigList.size() == 0){
+            return Collections.singletonList(DEFAULT_SUBNETCONFIG);
+        }else{
+            return new ArrayList<SubnetConfig>(subnetsConfigList.values());
+        }
     }
 
     @Override
     public SubnetConfig getSubnetConfig(String subnet) {
-        return subnetsConfigList.get(subnet);
+        // if there are no subnets, return the default subnet
+        if(subnetsConfigList.size() == 0 && subnet == DEFAULT_SUBNET_NAME){
+            return DEFAULT_SUBNETCONFIG;
+        }else{
+            return subnetsConfigList.get(subnet);
+        }
     }
 
     private List<SpanConfig> getSpanConfigList(Node node) {
@@ -388,7 +395,7 @@ CommandProvider {
 
     private Status updateDatabase(SubnetConfig conf, boolean add) {
         if (add) {
-            Subnet subnetCurr = subnets.get(conf.getIPnum());
+            Subnet subnetCurr = subnets.get(conf.getIPAddress());
             Subnet subnet;
             if (subnetCurr == null) {
                 subnet = new Subnet(conf);
@@ -398,23 +405,24 @@ CommandProvider {
             // 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);
+                subnet.addNodeConnectors(conf.getNodeConnectors());
             }
-            boolean result = false;
+            boolean putNewSubnet = false;
             if(subnetCurr == null) {
-                if(subnets.putIfAbsent(conf.getIPnum(), subnet) == null) {
-                    result = true;
+                if(subnets.putIfAbsent(conf.getIPAddress(), subnet) == null) {
+                    putNewSubnet = true;
                 }
             } else {
-                result = subnets.replace(conf.getIPnum(), subnetCurr, subnet);
+                putNewSubnet = subnets.replace(conf.getIPAddress(), subnetCurr, subnet);
             }
-            if(!result) {
-                String msg = "Cluster conflict: Conflict while adding the subnet " + conf.getIPnum();
+            if(!putNewSubnet) {
+                String msg = "Cluster conflict: Conflict while adding the subnet " + conf.getIPAddress();
                 return new Status(StatusCode.CONFLICT, msg);
             }
-        } else { // This is the deletion of the whole subnet
-            subnets.remove(conf.getIPnum());
+
+        // Subnet removal case
+        } else {
+            subnets.remove(conf.getIPAddress());
         }
         return new Status(StatusCode.SUCCESS);
     }
@@ -427,47 +435,60 @@ CommandProvider {
         }
         for (InetAddress i : IPs) {
             Subnet existingSubnet = subnets.get(i);
-            if ((existingSubnet != null)
-                    && !existingSubnet.isMutualExclusive(newSubnet)) {
-                return new Status(StatusCode.CONFLICT);
+            if ((existingSubnet != null) && !existingSubnet.isMutualExclusive(newSubnet)) {
+                return new Status(StatusCode.CONFLICT, "This subnet conflicts with an existing one.");
             }
         }
         return new Status(StatusCode.SUCCESS);
     }
 
-    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);
+    private Status addRemoveSubnet(SubnetConfig conf, boolean isAdding) {
+        // Valid configuration check
+        Status status = conf.validate();
+        if (!status.isSuccess()) {
+            log.warn(status.getDescription());
+            return status;
         }
 
-        if (add) {
+        if (isAdding) {
             // Presence check
             if (subnetsConfigList.containsKey(conf.getName())) {
                 return new Status(StatusCode.CONFLICT,
-                        "Same subnet config already exists");
+                        "Subnet with the specified name already exists.");
             }
-            // Semantyc check
-            Status rc = semanticCheck(conf);
-            if (!rc.isSuccess()) {
-                return rc;
+            // Semantic check
+            status = semanticCheck(conf);
+            if (!status.isSuccess()) {
+                return status;
+            }
+        } else {
+            if (conf.getName().equals(DEFAULT_SUBNET_NAME)) {
+                return new Status(StatusCode.NOTALLOWED, "The specified subnet gateway cannot be removed");
             }
         }
 
         // Update Database
-        Status rc = updateDatabase(conf, add);
+        status = updateDatabase(conf, isAdding);
 
-        if (rc.isSuccess()) {
+        if (status.isSuccess()) {
             // Update Configuration
-            rc = updateConfig(conf, add);
-            if(!rc.isSuccess()) {
-                updateDatabase(conf, (!add));
+            status = updateConfig(conf, isAdding);
+            if(!status.isSuccess()) {
+                updateDatabase(conf, (!isAdding));
+            } else {
+                // update the listeners
+                Subnet subnetCurr = subnets.get(conf.getIPAddress());
+                Subnet subnet;
+                if (subnetCurr == null) {
+                    subnet = new Subnet(conf);
+                } else {
+                    subnet = subnetCurr.clone();
+                }
+                notifySubnetChange(subnet, isAdding);
             }
         }
 
-        return rc;
+        return status;
     }
 
     /**
@@ -485,6 +506,9 @@ CommandProvider {
 
     @Override
     public Status removeSubnet(String name) {
+        if (name.equals(DEFAULT_SUBNET_NAME)) {
+            return new Status(StatusCode.NOTALLOWED, "The specified subnet gateway cannot be removed");
+        }
         SubnetConfig conf = subnetsConfigList.get(name);
         if (conf == null) {
             return new Status(StatusCode.SUCCESS, "Subnet not present");
@@ -493,27 +517,101 @@ CommandProvider {
     }
 
     @Override
-    public Status addPortsToSubnet(String name, String switchPorts) {
+    public Status modifySubnet(SubnetConfig conf) {
+        // Sanity check
+        if (conf == null) {
+            return new Status(StatusCode.BADREQUEST, "Invalid Subnet configuration: null");
+        }
+
+        // Valid configuration check
+        Status status = conf.validate();
+        if (!status.isSuccess()) {
+            log.warn(status.getDescription());
+            return status;
+        }
+
+        // If a subnet configuration with this name does not exist, consider this is a creation
+        SubnetConfig target = subnetsConfigList.get(conf.getName());
+        if (target == null) {
+            return this.addSubnet(conf);
+        }
+
+        // No change
+        if (target.equals(conf)) {
+            return new Status(StatusCode.SUCCESS);
+        }
+
+        // Check not allowed modifications
+        if (!target.getSubnet().equals(conf.getSubnet())) {
+            return new Status(StatusCode.BADREQUEST, "IP address change is not allowed");
+        }
+
+        // Derive the set of node connectors that are being removed
+        Set<NodeConnector> toRemove = target.getNodeConnectors();
+        toRemove.removeAll(conf.getNodeConnectors());
+        List<String> nodeConnectorStrings = null;
+        if (!toRemove.isEmpty()) {
+            nodeConnectorStrings = new ArrayList<String>();
+            for (NodeConnector nc : toRemove) {
+                nodeConnectorStrings.add(nc.toString());
+            }
+            status = this.removePortsFromSubnet(conf.getName(), nodeConnectorStrings);
+            if (!status.isSuccess()) {
+                return status;
+            }
+        }
+
+        // Derive the set of node connectors that are being added
+        Set<NodeConnector> toAdd = conf.getNodeConnectors();
+        toAdd.removeAll(target.getNodeConnectors());
+        if (!toAdd.isEmpty()) {
+            List<String> nodeConnectorStringRemoved = nodeConnectorStrings;
+            nodeConnectorStrings = new ArrayList<String>();
+            for (NodeConnector nc : toAdd) {
+                nodeConnectorStrings.add(nc.toString());
+            }
+            status = this.addPortsToSubnet(conf.getName(), nodeConnectorStrings);
+            if (!status.isSuccess()) {
+                // If any port was removed, add it back as a best recovery effort
+                if (!toRemove.isEmpty()) {
+                    this.addPortsToSubnet(conf.getName(), nodeConnectorStringRemoved);
+                }
+                return status;
+            }
+        }
+
+        // Update Configuration
+        subnetsConfigList.put(conf.getName(), conf);
+
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    @Override
+    public Status addPortsToSubnet(String name, List<String> switchPorts) {
+        if (name == null) {
+            return new Status(StatusCode.BADREQUEST, "Null subnet name");
+        }
         SubnetConfig confCurr = subnetsConfigList.get(name);
         if (confCurr == null) {
             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
         }
-        if (!confCurr.isValidSwitchPort(switchPorts)) {
-            return new Status(StatusCode.BADREQUEST, "Invalid switchports");
+
+        if (switchPorts == null || switchPorts.isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Null or empty port set");
         }
 
-        Subnet subCurr = subnets.get(confCurr.getIPnum());
+        Subnet subCurr = subnets.get(confCurr.getIPAddress());
         if (subCurr == null) {
-            log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPnum());
+            log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPAddress());
             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
         }
 
         // Update Database
         Subnet sub = subCurr.clone();
-        Set<NodeConnector> sp = confCurr.getNodeConnectors(switchPorts);
+        Set<NodeConnector> sp = NodeConnector.fromString(switchPorts);
         sub.addNodeConnectors(sp);
-        boolean subnetsReplace = subnets.replace(confCurr.getIPnum(), subCurr, sub);
-        if (!subnetsReplace) {
+        boolean subnetsReplaced = subnets.replace(confCurr.getIPAddress(), subCurr, sub);
+        if (!subnetsReplaced) {
             String msg = "Cluster conflict: Conflict while adding ports to the subnet " + name;
             return new Status(StatusCode.CONFLICT, msg);
         }
@@ -521,8 +619,8 @@ CommandProvider {
         // Update Configuration
         SubnetConfig conf = confCurr.clone();
         conf.addNodeConnectors(switchPorts);
-        boolean result = subnetsConfigList.replace(name, confCurr, conf);
-        if (!result) {
+        boolean configReplaced = subnetsConfigList.replace(name, confCurr, conf);
+        if (!configReplaced) {
             // TODO: recovery using Transactionality
             String msg = "Cluster conflict: Conflict while adding ports to the subnet " + name;
             return new Status(StatusCode.CONFLICT, msg);
@@ -532,23 +630,35 @@ CommandProvider {
     }
 
     @Override
-    public Status removePortsFromSubnet(String name, String switchPorts) {
+    public Status removePortsFromSubnet(String name, List<String> switchPorts) {
+        if (name == null) {
+            return new Status(StatusCode.BADREQUEST, "Null subnet name");
+        }
         SubnetConfig confCurr = subnetsConfigList.get(name);
         if (confCurr == null) {
             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
         }
 
-        Subnet subCurr = subnets.get(confCurr.getIPnum());
+        if (switchPorts == null || switchPorts.isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Null or empty port set");
+        }
+
+        Subnet subCurr = subnets.get(confCurr.getIPAddress());
         if (subCurr == null) {
-            log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPnum());
+            log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPAddress());
             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
         }
 
+        // Validation check
+        Status status = SubnetConfig.validatePorts(switchPorts);
+        if (!status.isSuccess()) {
+            return status;
+        }
         // Update Database
         Subnet sub = subCurr.clone();
-        Set<NodeConnector> sp = confCurr.getNodeConnectors(switchPorts);
+        Set<NodeConnector> sp = NodeConnector.fromString(switchPorts);
         sub.deleteNodeConnectors(sp);
-        boolean subnetsReplace = subnets.replace(confCurr.getIPnum(), subCurr, sub);
+        boolean subnetsReplace = subnets.replace(confCurr.getIPAddress(), subCurr, sub);
         if (!subnetsReplace) {
             String msg = "Cluster conflict: Conflict while removing ports from the subnet " + name;
             return new Status(StatusCode.CONFLICT, msg);
@@ -576,12 +686,14 @@ CommandProvider {
 
     @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;
+        // if there are no subnets, return the default subnet
+        if (subnets.size() == 0) {
+            return DEFAULT_SUBNET;
+        }
+
+        for(Map.Entry<InetAddress,Subnet> subnetEntry : subnets.entrySet()) {
+            if(subnetEntry.getValue().isSubnetOf(networkAddress)) {
+                return subnetEntry.getValue();
             }
         }
         return null;
@@ -636,10 +748,11 @@ CommandProvider {
         }
 
         for (SwitchConfig conf : confList.values()) {
-            updateSwitchConfig(conf);
+            updateNodeConfig(conf);
         }
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public void updateSwitchConfig(SwitchConfig cfgObject) {
         // update default container only
@@ -696,18 +809,43 @@ CommandProvider {
         }
 
         Map<String, Property> updateProperties = switchConfig.getNodeProperties();
-        String nodeId = switchConfig.getNodeId();
         ForwardingMode mode = (ForwardingMode) updateProperties.get(ForwardingMode.name);
         if (mode != null) {
             if (isDefaultContainer) {
                 if (!mode.isValid()) {
-                    return new Status(StatusCode.BADREQUEST, "Invalid Forwarding Mode Value.");
+                    return new Status(StatusCode.BADREQUEST, "Invalid Forwarding Mode Value");
                 }
             } else {
                 return new Status(StatusCode.NOTACCEPTABLE,
                         "Forwarding Mode modification is allowed only in default container");
             }
         }
+
+        Description description = (Description) switchConfig.getProperty(Description.propertyName);
+        String nodeId = switchConfig.getNodeId();
+        Node node = Node.fromString(nodeId);
+        NodeDescription nodeDesc = (this.statisticsManager == null) ? null : this.statisticsManager
+                .getNodeDescription(node);
+        String advertisedDesc = (nodeDesc == null) ? "" : nodeDesc.getDescription();
+        if (description != null && description.getValue() != null) {
+            if (description.getValue().isEmpty() || description.getValue().equals(advertisedDesc)) {
+                updateProperties.remove(Description.propertyName);
+                switchConfig = new SwitchConfig(nodeId, updateProperties);
+            } else {
+                // check if description is configured or was published by any other node
+                for (Map.Entry<Node, Map<String, Property>> entry : nodeProps.entrySet()) {
+                    Node n = entry.getKey();
+                    Description desc = (Description) getNodeProp(n, Description.propertyName);
+                    NodeDescription nDesc = (this.statisticsManager == null) ? null : this.statisticsManager
+                            .getNodeDescription(n);
+                    String advDesc = (nDesc == null) ? "" : nDesc.getDescription();
+                    if ((description.equals(desc) || description.getValue().equals(advDesc)) && !node.equals(n)) {
+                        return new Status(StatusCode.CONFLICT, "Node name already in use");
+                    }
+                }
+            }
+        }
+
         boolean modeChange = false;
         SwitchConfig sc = nodeConfigList.get(nodeId);
         Map<String, Property> prevNodeProperties = new HashMap<String, Property>();
@@ -741,30 +879,32 @@ CommandProvider {
                 }
             }
         }
-        Node node = Node.fromString(nodeId);
         Map<String, Property> propMapCurr = nodeProps.get(node);
         if (propMapCurr == null) {
             return new Status(StatusCode.SUCCESS);
         }
         Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
-        if (!prevNodeProperties.isEmpty()) {
-            for (String prop : prevNodeProperties.keySet()) {
-                if (!updateProperties.containsKey(prop)) {
-                    if (prop.equals(Description.propertyName)) {
-                        Map<Node, Map<String, Property>> nodeProp = this.inventoryService.getNodeProps();
-                        if (nodeProp.get(node) != null) {
-                            propMap.put(Description.propertyName, nodeProp.get(node).get(Description.propertyName));
-                            continue;
-                        }
+        for (Map.Entry<String, Property> entry : prevNodeProperties.entrySet()) {
+            String prop = entry.getKey();
+            if (!updateProperties.containsKey(prop)) {
+                if (prop.equals(Description.propertyName)) {
+                    if (!advertisedDesc.isEmpty()) {
+                        Property desc = new Description(advertisedDesc);
+                        propMap.put(Description.propertyName, desc);
                     }
-                    propMap.remove(prop);
+                    continue;
+                } else if (prop.equals(ForwardingMode.name)) {
+                    Property defaultMode = new ForwardingMode(ForwardingMode.REACTIVE_FORWARDING);
+                    propMap.put(ForwardingMode.name, defaultMode);
+                    continue;
                 }
+                propMap.remove(prop);
             }
         }
         propMap.putAll(updateProperties);
         if (!nodeProps.replace(node, propMapCurr, propMap)) {
             // TODO rollback using Transactionality
-            return new Status(StatusCode.CONFLICT, "Cluster conflict: Unable to update node configuration.");
+            return new Status(StatusCode.CONFLICT, "Cluster conflict: Unable to update node configuration");
         }
         if (modeChange) {
             notifyModeChange(node, (mode == null) ? false : mode.isProactive());
@@ -782,7 +922,8 @@ CommandProvider {
         Map<String, Property> propMapCurr = nodeProps.get(node);
         if ((propMapCurr != null) && (nodeProperties != null) && (!nodeProperties.isEmpty())) {
             Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
-            for (String prop : nodeProperties.keySet()) {
+            for (Map.Entry<String, Property> entry : nodeProperties.entrySet()) {
+                String prop = entry.getKey();
                 if (prop.equals(Description.propertyName)) {
                     Map<Node, Map<String, Property>> nodeProp = this.inventoryService.getNodeProps();
                     if (nodeProp.get(node) != null) {
@@ -804,8 +945,6 @@ CommandProvider {
 
     @Override
     public Status saveSwitchConfig() {
-        // Publish the save config event to the cluster nodes
-        configSaveEvent.put(new Date().getTime(), SAVE);
         return saveSwitchConfigInternal();
     }
 
@@ -880,20 +1019,6 @@ CommandProvider {
         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, props: {}", node, props);
         if (nodeProps == null) {
@@ -911,8 +1036,8 @@ CommandProvider {
             }
         }
 
-        // copy node properties from config
         boolean proactiveForwarding = false;
+        // copy node properties from config
         if (nodeConfigList != null) {
             String nodeId = node.toString();
             SwitchConfig conf = nodeConfigList.get(nodeId);
@@ -926,6 +1051,10 @@ CommandProvider {
             }
         }
 
+        if (!propMap.containsKey(ForwardingMode.name)) {
+            Property defaultMode = new ForwardingMode(ForwardingMode.REACTIVE_FORWARDING);
+            propMap.put(ForwardingMode.name, defaultMode);
+        }
         boolean result = false;
         if (propMapCurr == null) {
             if (nodeProps.putIfAbsent(node, propMap) == null) {
@@ -958,6 +1087,17 @@ CommandProvider {
             return;
         }
         nodeProps.remove(node);
+        nodeConnectorNames.remove(node);
+        Set<NodeConnector> removeNodeConnectorSet = new HashSet<NodeConnector>();
+        for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+            NodeConnector nodeConnector = entry.getKey();
+            if (nodeConnector.getNode().equals(node)) {
+                removeNodeConnectorSet.add(nodeConnector);
+            }
+        }
+        for (NodeConnector nc : removeNodeConnectorSet) {
+            nodeConnectorProps.remove(nc);
+        }
 
         // check if span ports need to be cleaned up
         removeSpanPorts(node);
@@ -1031,6 +1171,7 @@ CommandProvider {
     public void updateNodeConnector(NodeConnector nodeConnector,
             UpdateType type, Set<Property> props) {
         Map<String, Property> propMap = new HashMap<String, Property>();
+        boolean update = true;
 
         log.debug("updateNodeConnector: {} type {} props {} for container {}",
                 new Object[] { nodeConnector, type, props, containerName });
@@ -1041,7 +1182,6 @@ CommandProvider {
 
         switch (type) {
         case ADDED:
-        case CHANGED:
             if (props != null) {
                 for (Property prop : props) {
                     addNodeConnectorProp(nodeConnector, prop);
@@ -1053,23 +1193,77 @@ CommandProvider {
 
             addSpanPort(nodeConnector);
             break;
+        case CHANGED:
+            if (!nodeConnectorProps.containsKey(nodeConnector) || (props == null)) {
+                update = false;
+            } else {
+                for (Property prop : props) {
+                    addNodeConnectorProp(nodeConnector, prop);
+                    propMap.put(prop.getName(), prop);
+                }
+            }
+            break;
         case REMOVED:
+            if (!nodeConnectorProps.containsKey(nodeConnector)) {
+                update = false;
+            }
             removeNodeConnectorAllProps(nodeConnector);
 
             // clean up span config
             removeSpanPort(nodeConnector);
             break;
         default:
+            update = false;
             break;
         }
 
-        notifyNodeConnector(nodeConnector, type, propMap);
+        if (update) {
+            notifyNodeConnector(nodeConnector, type, propMap);
+        }
     }
 
     @Override
     public Set<Node> getNodes() {
-        return (nodeProps != null) ? new HashSet<Node>(nodeProps.keySet())
-                : null;
+        return (nodeProps != null) ? new HashSet<Node>(nodeProps.keySet()) : new HashSet<Node>();
+    }
+
+    @Override
+    public Map<String, Property> getControllerProperties() {
+        return new HashMap<String, Property>(this.controllerProps);
+    }
+
+    @Override
+    public Property getControllerProperty(String propertyName) {
+        if (propertyName != null) {
+            HashMap<String, Property> propertyMap =  new HashMap<String, Property>(this.controllerProps);
+            return propertyMap.get(propertyName);
+        }
+        return null;
+    }
+
+    @Override
+    public Status setControllerProperty(Property property) {
+        if (property != null) {
+            this.controllerProps.put(property.getName(), property);
+            return new Status(StatusCode.SUCCESS);
+        }
+        return new Status(StatusCode.BADREQUEST, "Invalid property provided when setting property");
+    }
+
+    @Override
+    public Status removeControllerProperty(String propertyName) {
+        if (propertyName != null) {
+            if (this.controllerProps.containsKey(propertyName)) {
+                this.controllerProps.remove(propertyName);
+                if (!this.controllerProps.containsKey(propertyName)) {
+                    return new Status(StatusCode.SUCCESS);
+                }
+            }
+            String msg = "Unable to remove property " + propertyName + " from Controller";
+            return new Status(StatusCode.BADREQUEST, msg);
+        }
+        String msg = "Invalid property provided when removing property from Controller";
+        return new Status(StatusCode.BADREQUEST, msg);
     }
 
     /*
@@ -1164,7 +1358,8 @@ CommandProvider {
         }
 
         Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
-        for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+        for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+            NodeConnector nodeConnector = entry.getKey();
             if (!nodeConnector.getNode().equals(node)) {
                 continue;
             }
@@ -1183,7 +1378,8 @@ CommandProvider {
         }
 
         Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
-        for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+        for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+            NodeConnector nodeConnector = entry.getKey();
             if (!nodeConnector.getNode().equals(node)) {
                 continue;
             }
@@ -1200,7 +1396,8 @@ CommandProvider {
         }
 
         Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
-        for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+        for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+            NodeConnector nodeConnector = entry.getKey();
             if (!nodeConnector.getNode().equals(node)
                     || isSpecial(nodeConnector)) {
                 continue;
@@ -1266,16 +1463,6 @@ CommandProvider {
         return (macProperty == null) ? null : macProperty.getMacAddress();
     }
 
-    @Override
-    public boolean isHostRefreshEnabled() {
-        return hostRefresh;
-    }
-
-    @Override
-    public int getHostRetryCount() {
-        return hostRetryCount;
-    }
-
     @Override
     public NodeConnector getNodeConnector(Node node, String nodeConnectorName) {
         if (nodeConnectorNames == null) {
@@ -1339,11 +1526,12 @@ CommandProvider {
                 Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
                 Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
                 if (mapCurr != null) {
-                    for (String s : mapCurr.keySet()) {
+                    for (Map.Entry<String, NodeConnector> entry : mapCurr.entrySet()) {
+                        String s = entry.getKey();
                         try {
-                            map.put(s, new NodeConnector(mapCurr.get(s)));
+                            map.put(s, new NodeConnector(entry.getValue()));
                         } catch (ConstructionException e) {
-                            e.printStackTrace();
+                            log.error("An error occured",e);
                         }
                     }
                 }
@@ -1400,11 +1588,12 @@ CommandProvider {
                     Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
                     if (mapCurr != null) {
                         Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
-                        for (String s : mapCurr.keySet()) {
+                        for (Map.Entry<String, NodeConnector> entry : mapCurr.entrySet()) {
+                            String s = entry.getKey();
                             try {
-                                map.put(s, new NodeConnector(mapCurr.get(s)));
+                                map.put(s, new NodeConnector(entry.getValue()));
                             } catch (ConstructionException e) {
-                                e.printStackTrace();
+                                log.error("An error occured",e);
                             }
                         }
                         map.remove(name.getValue());
@@ -1436,17 +1625,22 @@ CommandProvider {
                 Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
                 if (mapCurr != null) {
                     Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
-                    for (String s : mapCurr.keySet()) {
+                    for (Map.Entry<String, NodeConnector> entry : mapCurr.entrySet()) {
+                        String s = entry.getKey();
                         try {
-                            map.put(s, new NodeConnector(mapCurr.get(s)));
+                            map.put(s, new NodeConnector(entry.getValue()));
                         } catch (ConstructionException e) {
-                            e.printStackTrace();
+                            log.error("An error occured",e);
                         }
                     }
                     map.remove(name.getValue());
-                    if (!nodeConnectorNames.replace(node, mapCurr, map)) {
-                        log.warn("Cluster conflict: Unable remove Name property of nodeconnector {}, skip.",
-                                nodeConnector.getID());
+                    if (map.isEmpty()) {
+                        nodeConnectorNames.remove(node);
+                    } else {
+                        if (!nodeConnectorNames.replace(node, mapCurr, map)) {
+                            log.warn("Cluster conflict: Unable remove Name property of nodeconnector {}, skip.",
+                                    nodeConnector.getID());
+                        }
                     }
                 }
 
@@ -1530,6 +1724,16 @@ CommandProvider {
         clearInventories();
     }
 
+    public void setStatisticsManager(IStatisticsManager statisticsManager) {
+        log.trace("Got statistics manager set request {}", statisticsManager);
+        this.statisticsManager = statisticsManager;
+    }
+
+    public void unsetStatisticsManager(IStatisticsManager statisticsManager) {
+        log.trace("Got statistics manager UNset request");
+        this.statisticsManager = null;
+    }
+
     public void setSwitchManagerAware(ISwitchManagerAware service) {
         log.trace("Got inventory service set request {}", service);
         if (this.switchManagerAware != null) {
@@ -1671,7 +1875,8 @@ CommandProvider {
             service.notifyNode(node, type, propMap);
         }
 
-        for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+        for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+            NodeConnector nodeConnector = entry.getKey();
             propMap = nodeConnectorProps.get(nodeConnector);
             service.notifyNodeConnector(nodeConnector, type, propMap);
         }
@@ -1706,51 +1911,21 @@ CommandProvider {
                 && (state != null) && (state.getValue() == State.EDGE_UP));
     }
 
+    @Override
+    public boolean doesNodeConnectorExist(NodeConnector nc) {
+        return (nc != null && nodeConnectorProps != null
+                && nodeConnectorProps.containsKey(nc));
+    }
+
     @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           MAC            Name      Tier");
-        if (nodeProps == null) {
-            return;
-        }
-        Set<Node> nodeSet = nodeProps.keySet();
-        if (nodeSet == null) {
-            return;
-        }
-        List<String> nodeArray = new ArrayList<String>();
-        for (Node node : nodeSet) {
-            nodeArray.add(node.toString());
-        }
-        Collections.sort(nodeArray);
-        for (String str: nodeArray) {
-            Node node = Node.fromString(str);
-            Description desc = ((Description) getNodeProp(node,
-                    Description.propertyName));
-            Tier tier = ((Tier) getNodeProp(node, Tier.TierPropName));
-            String nodeName = (desc == null) ? "" : desc.getValue();
-            MacAddress mac = (MacAddress) getNodeProp(node,
-                    MacAddress.name);
-            String macAddr = (mac == null) ? "" : HexEncode
-                    .bytesToHexStringFormat(mac.getMacAddress());
-            int tierNum = (tier == null) ? 0 : tier.getValue();
-            ci.println(node + "     " + node.getType() + "     " + macAddr
-                    + "     " + nodeName + "     " + tierNum);
-        }
-        ci.println("Total number of Nodes: " + nodeSet.size());
-    }
-
     public void _pencs(CommandInterpreter ci) {
         String st = ci.nextArgument();
         if (st == null) {
@@ -1777,43 +1952,6 @@ CommandProvider {
         ci.println("Total number of NodeConnectors: " + nodeConnectorSet.size());
     }
 
-    public void _pncs(CommandInterpreter ci) {
-        String st = ci.nextArgument();
-        if (st == null) {
-            ci.println("Please enter node id");
-            return;
-        }
-
-        Node node = Node.fromString(st);
-        if (node == null) {
-            ci.println("Please enter node id");
-            return;
-        }
-
-        ci.println("          NodeConnector               BandWidth(Gbps)     Admin     State");
-        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);
-        }
-        ci.println("Total number of NodeConnectors: " + nodeConnectorSet.size());
-    }
-
     public void _pdm(CommandInterpreter ci) {
         String st = ci.nextArgument();
         if (st == null) {
@@ -1830,9 +1968,7 @@ CommandProvider {
         Switch sw = getSwitchByNode(node);
 
         ci.println("          NodeConnector                        Name");
-        if (sw == null) {
-            return;
-        }
+
         Set<NodeConnector> nodeConnectorSet = sw.getNodeConnectors();
         String nodeConnectorName;
         if (nodeConnectorSet != null && nodeConnectorSet.size() > 0) {
@@ -1869,66 +2005,6 @@ CommandProvider {
         }
     }
 
-    public void _snt(CommandInterpreter ci) {
-        String st = ci.nextArgument();
-        if (st == null) {
-            ci.println("Please enter node id");
-            return;
-        }
-
-        Node node = Node.fromString(st);
-        if (node == null) {
-            ci.println("Please enter node id");
-            return;
-        }
-
-        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) {
         MacAddress mac = (MacAddress) this.getNodeProp(node,
@@ -2022,20 +2098,15 @@ CommandProvider {
         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.
+     * Creates a Name/Tier/Bandwidth/MacAddress(controller property) Property
+     * object based on given property name and value. Other property types are
+     * not supported yet.
      *
      * @param propName
      *            Name of the Property
@@ -2066,7 +2137,10 @@ CommandProvider {
             } else if (propName.equalsIgnoreCase(ForwardingMode.name)) {
                 int mode = Integer.parseInt(propValue);
                 return new ForwardingMode(mode);
-            } else {
+            } else if (propName.equalsIgnoreCase(MacAddress.name)){
+                return new MacAddress(propValue);
+            }
+            else {
                 log.debug("Not able to create {} property", propName);
             }
         } catch (Exception e) {
@@ -2076,6 +2150,8 @@ CommandProvider {
         return null;
     }
 
+
+    @SuppressWarnings("deprecation")
     @Override
     public String getNodeDescription(Node node) {
         // Check first if user configured a name
@@ -2093,4 +2169,25 @@ CommandProvider {
         return (desc == null /* || desc.getValue().equalsIgnoreCase("none") */) ? ""
                 : desc.getValue();
     }
+
+    @Override
+    public Set<Switch> getConfiguredNotConnectedSwitches() {
+        Set<Switch> configuredNotConnectedSwitches = new HashSet<Switch>();
+        if (this.inventoryService == null) {
+            log.trace("inventory service not avaiable");
+            return configuredNotConnectedSwitches;
+        }
+
+        Set<Node> configuredNotConnectedNodes = this.inventoryService.getConfiguredNotConnectedNodes();
+        if (configuredNotConnectedNodes != null) {
+            for (Node node : configuredNotConnectedNodes) {
+                Switch sw = getSwitchByNode(node);
+                if (sw != null) {
+                    configuredNotConnectedSwitches.add(sw);
+                }
+            }
+        }
+        return configuredNotConnectedSwitches;
+    }
+
 }