4d2aea203601dafe6e19fcacc208ae4760819b0f
[controller.git] / opendaylight / switchmanager / implementation / src / main / java / org / opendaylight / controller / switchmanager / internal / SwitchManager.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.switchmanager.internal;
10
11 import java.io.FileNotFoundException;
12 import java.io.IOException;
13 import java.io.ObjectInputStream;
14 import java.net.InetAddress;
15 import java.net.NetworkInterface;
16 import java.net.SocketException;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Dictionary;
20 import java.util.EnumSet;
21 import java.util.Enumeration;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.CopyOnWriteArrayList;
30
31 import org.apache.felix.dm.Component;
32 import org.eclipse.osgi.framework.console.CommandInterpreter;
33 import org.eclipse.osgi.framework.console.CommandProvider;
34 import org.opendaylight.controller.clustering.services.CacheConfigException;
35 import org.opendaylight.controller.clustering.services.CacheExistException;
36 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
37 import org.opendaylight.controller.clustering.services.IClusterServices;
38 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
39 import org.opendaylight.controller.sal.core.Bandwidth;
40 import org.opendaylight.controller.sal.core.Config;
41 import org.opendaylight.controller.sal.core.ConstructionException;
42 import org.opendaylight.controller.sal.core.Description;
43 import org.opendaylight.controller.sal.core.ForwardingMode;
44 import org.opendaylight.controller.sal.core.MacAddress;
45 import org.opendaylight.controller.sal.core.Name;
46 import org.opendaylight.controller.sal.core.Node;
47 import org.opendaylight.controller.sal.core.NodeConnector;
48 import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
49 import org.opendaylight.controller.sal.core.Property;
50 import org.opendaylight.controller.sal.core.State;
51 import org.opendaylight.controller.sal.core.Tier;
52 import org.opendaylight.controller.sal.core.UpdateType;
53 import org.opendaylight.controller.sal.inventory.IInventoryService;
54 import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
55 import org.opendaylight.controller.sal.reader.NodeDescription;
56 import org.opendaylight.controller.sal.utils.GlobalConstants;
57 import org.opendaylight.controller.sal.utils.HexEncode;
58 import org.opendaylight.controller.sal.utils.IObjectReader;
59 import org.opendaylight.controller.sal.utils.ObjectReader;
60 import org.opendaylight.controller.sal.utils.ObjectWriter;
61 import org.opendaylight.controller.sal.utils.Status;
62 import org.opendaylight.controller.sal.utils.StatusCode;
63 import org.opendaylight.controller.statisticsmanager.IStatisticsManager;
64 import org.opendaylight.controller.switchmanager.IInventoryListener;
65 import org.opendaylight.controller.switchmanager.ISpanAware;
66 import org.opendaylight.controller.switchmanager.ISwitchManager;
67 import org.opendaylight.controller.switchmanager.ISwitchManagerAware;
68 import org.opendaylight.controller.switchmanager.SpanConfig;
69 import org.opendaylight.controller.switchmanager.Subnet;
70 import org.opendaylight.controller.switchmanager.SubnetConfig;
71 import org.opendaylight.controller.switchmanager.Switch;
72 import org.opendaylight.controller.switchmanager.SwitchConfig;
73 import org.osgi.framework.BundleContext;
74 import org.osgi.framework.FrameworkUtil;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78 /**
79  * The class describes SwitchManager which is the central repository of all the
80  * inventory data including nodes, node connectors, properties attached, Layer3
81  * configurations, Span configurations, node configurations, network device
82  * representations viewed by Controller Web applications. One SwitchManager
83  * instance per container of the network. All the node/nodeConnector properties
84  * are maintained in the default container only.
85  */
86 public class SwitchManager implements ISwitchManager, IConfigurationContainerAware,
87                                       IObjectReader, IListenInventoryUpdates, CommandProvider {
88     private static Logger log = LoggerFactory.getLogger(SwitchManager.class);
89     private static String ROOT = GlobalConstants.STARTUPHOME.toString();
90     private String subnetFileName, spanFileName, switchConfigFileName;
91     private final List<NodeConnector> spanNodeConnectors = new CopyOnWriteArrayList<NodeConnector>();
92     // Collection of Subnets keyed by the InetAddress
93     private ConcurrentMap<InetAddress, Subnet> subnets;
94     private ConcurrentMap<String, SubnetConfig> subnetsConfigList;
95     private ConcurrentMap<SpanConfig, SpanConfig> spanConfigList;
96     // manually configured parameters for the node such as name, tier, mode
97     private ConcurrentMap<String, SwitchConfig> nodeConfigList;
98     private ConcurrentMap<Node, Map<String, Property>> nodeProps;
99     private ConcurrentMap<NodeConnector, Map<String, Property>> nodeConnectorProps;
100     private ConcurrentMap<Node, Map<String, NodeConnector>> nodeConnectorNames;
101     private ConcurrentMap<String, Property> controllerProps;
102     private IInventoryService inventoryService;
103     private IStatisticsManager statisticsManager;
104     private final Set<ISwitchManagerAware> switchManagerAware = Collections
105             .synchronizedSet(new HashSet<ISwitchManagerAware>());
106     private final Set<IInventoryListener> inventoryListeners = Collections
107             .synchronizedSet(new HashSet<IInventoryListener>());
108     private final Set<ISpanAware> spanAware = Collections.synchronizedSet(new HashSet<ISpanAware>());
109     private IClusterContainerServices clusterContainerService = null;
110     private String containerName = null;
111     private boolean isDefaultContainer = true;
112     private static final int REPLACE_RETRY = 1;
113
114     /* Information about the default subnet. If there have been no configured subnets, i.e.,
115      * subnets.size() == 0 or subnetsConfigList.size() == 0, then this subnet will be the
116      * only subnet returned. As soon as a user-configured subnet is created this one will
117      * vanish.
118      */
119     protected static SubnetConfig DEFAULT_SUBNETCONFIG;
120     protected static Subnet DEFAULT_SUBNET;
121     protected static String DEFAULT_SUBNET_NAME = "default (cannot be modifed)";
122     static{
123         DEFAULT_SUBNETCONFIG = new SubnetConfig(DEFAULT_SUBNET_NAME, "0.0.0.0/0", new ArrayList<String>());
124         DEFAULT_SUBNET = new Subnet(DEFAULT_SUBNETCONFIG);
125     }
126
127     public void notifySubnetChange(Subnet sub, boolean add) {
128         synchronized (switchManagerAware) {
129             for (Object subAware : switchManagerAware) {
130                 try {
131                     ((ISwitchManagerAware) subAware).subnetNotify(sub, add);
132                 } catch (Exception e) {
133                     log.error("Failed to notify Subnet change {}",
134                             e.getMessage());
135                 }
136             }
137         }
138     }
139
140     public void notifySpanPortChange(Node node, List<NodeConnector> ports, boolean add) {
141         synchronized (spanAware) {
142             for (Object sa : spanAware) {
143                 try {
144                     ((ISpanAware) sa).spanUpdate(node, ports, add);
145                 } catch (Exception e) {
146                     log.error("Failed to notify Span Interface change {}",
147                             e.getMessage());
148                 }
149             }
150         }
151     }
152
153     private void notifyModeChange(Node node, boolean proactive) {
154         synchronized (switchManagerAware) {
155             for (ISwitchManagerAware service : switchManagerAware) {
156                 try {
157                     service.modeChangeNotify(node, proactive);
158                 } catch (Exception e) {
159                     log.error("Failed to notify Subnet change {}",
160                             e.getMessage());
161                 }
162             }
163         }
164     }
165
166     public void startUp() {
167         String container = this.getContainerName();
168         // Initialize configuration file names
169         subnetFileName = ROOT + "subnets_" + container + ".conf";
170         spanFileName = ROOT + "spanPorts_" + container + ".conf";
171         switchConfigFileName = ROOT + "switchConfig_" + container + ".conf";
172
173         // Instantiate cluster synced variables
174         allocateCaches();
175         retrieveCaches();
176
177         /*
178          * Read startup and build database if we have not already gotten the
179          * configurations synced from another node
180          */
181         if (subnetsConfigList.isEmpty()) {
182             loadSubnetConfiguration();
183         }
184         if (spanConfigList.isEmpty()) {
185             loadSpanConfiguration();
186         }
187         if (nodeConfigList.isEmpty()) {
188             loadSwitchConfiguration();
189         }
190
191         // Add controller MAC, if first node in the cluster
192         if (!controllerProps.containsKey(MacAddress.name)) {
193             byte controllerMac[] = getHardwareMAC();
194             if (controllerMac != null) {
195                 Property existing = controllerProps.putIfAbsent(MacAddress.name, new MacAddress(controllerMac));
196                 if (existing == null && log.isTraceEnabled()) {
197                     log.trace("Container {}: Setting controller MAC address in the cluster: {}", container,
198                             HexEncode.bytesToHexStringFormat(controllerMac));
199                 }
200             }
201         }
202     }
203
204     public void shutDown() {
205     }
206
207     private void allocateCaches() {
208         if (this.clusterContainerService == null) {
209             this.nonClusterObjectCreate();
210             log.warn("un-initialized clusterContainerService, can't create cache");
211             return;
212         }
213
214         try {
215             clusterContainerService.createCache(
216                     "switchmanager.subnetsConfigList",
217                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
218             clusterContainerService.createCache("switchmanager.spanConfigList",
219                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
220             clusterContainerService.createCache("switchmanager.nodeConfigList",
221                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
222             clusterContainerService.createCache("switchmanager.subnets",
223                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
224             clusterContainerService.createCache("switchmanager.nodeProps",
225                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
226             clusterContainerService.createCache(
227                     "switchmanager.nodeConnectorProps",
228                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
229             clusterContainerService.createCache(
230                     "switchmanager.nodeConnectorNames",
231                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
232             clusterContainerService.createCache(
233                     "switchmanager.controllerProps",
234                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
235         } catch (CacheConfigException cce) {
236             log.error("\nCache configuration invalid - check cache mode");
237         } catch (CacheExistException ce) {
238             log.error("\nCache already exits - destroy and recreate if needed");
239         }
240     }
241
242     @SuppressWarnings({ "unchecked" })
243     private void retrieveCaches() {
244         if (this.clusterContainerService == null) {
245             log.info("un-initialized clusterContainerService, can't create cache");
246             return;
247         }
248
249         subnetsConfigList = (ConcurrentMap<String, SubnetConfig>) clusterContainerService
250                 .getCache("switchmanager.subnetsConfigList");
251         if (subnetsConfigList == null) {
252             log.error("\nFailed to get cache for subnetsConfigList");
253         }
254
255         spanConfigList = (ConcurrentMap<SpanConfig, SpanConfig>) clusterContainerService
256                 .getCache("switchmanager.spanConfigList");
257         if (spanConfigList == null) {
258             log.error("\nFailed to get cache for spanConfigList");
259         }
260
261         nodeConfigList = (ConcurrentMap<String, SwitchConfig>) clusterContainerService
262                 .getCache("switchmanager.nodeConfigList");
263         if (nodeConfigList == null) {
264             log.error("\nFailed to get cache for nodeConfigList");
265         }
266
267         subnets = (ConcurrentMap<InetAddress, Subnet>) clusterContainerService
268                 .getCache("switchmanager.subnets");
269         if (subnets == null) {
270             log.error("\nFailed to get cache for subnets");
271         }
272
273         nodeProps = (ConcurrentMap<Node, Map<String, Property>>) clusterContainerService
274                 .getCache("switchmanager.nodeProps");
275         if (nodeProps == null) {
276             log.error("\nFailed to get cache for nodeProps");
277         }
278
279         nodeConnectorProps = (ConcurrentMap<NodeConnector, Map<String, Property>>) clusterContainerService
280                 .getCache("switchmanager.nodeConnectorProps");
281         if (nodeConnectorProps == null) {
282             log.error("\nFailed to get cache for nodeConnectorProps");
283         }
284
285         nodeConnectorNames = (ConcurrentMap<Node, Map<String, NodeConnector>>) clusterContainerService
286                 .getCache("switchmanager.nodeConnectorNames");
287         if (nodeConnectorNames == null) {
288             log.error("\nFailed to get cache for nodeConnectorNames");
289         }
290
291         controllerProps = (ConcurrentMap<String, Property>) clusterContainerService
292                 .getCache("switchmanager.controllerProps");
293         if (controllerProps == null) {
294             log.error("\nFailed to get cache for controllerProps");
295         }
296     }
297
298     private void nonClusterObjectCreate() {
299         subnetsConfigList = new ConcurrentHashMap<String, SubnetConfig>();
300         spanConfigList = new ConcurrentHashMap<SpanConfig, SpanConfig>();
301         nodeConfigList = new ConcurrentHashMap<String, SwitchConfig>();
302         subnets = new ConcurrentHashMap<InetAddress, Subnet>();
303         nodeProps = new ConcurrentHashMap<Node, Map<String, Property>>();
304         nodeConnectorProps = new ConcurrentHashMap<NodeConnector, Map<String, Property>>();
305         nodeConnectorNames = new ConcurrentHashMap<Node, Map<String, NodeConnector>>();
306         controllerProps = new ConcurrentHashMap<String, Property>();
307     }
308
309     @Override
310     public List<SubnetConfig> getSubnetsConfigList() {
311         // if there are no subnets, return the default subnet
312         if(subnetsConfigList.size() == 0){
313             return Collections.singletonList(DEFAULT_SUBNETCONFIG);
314         }else{
315             return new ArrayList<SubnetConfig>(subnetsConfigList.values());
316         }
317     }
318
319     @Override
320     public SubnetConfig getSubnetConfig(String subnet) {
321         // if there are no subnets, return the default subnet
322         if(subnetsConfigList.size() == 0 && subnet == DEFAULT_SUBNET_NAME){
323             return DEFAULT_SUBNETCONFIG;
324         }else{
325             return subnetsConfigList.get(subnet);
326         }
327     }
328
329     private List<SpanConfig> getSpanConfigList(Node node) {
330         List<SpanConfig> confList = new ArrayList<SpanConfig>();
331         String nodeId = node.toString();
332         for (SpanConfig conf : spanConfigList.values()) {
333             if (conf.matchNode(nodeId)) {
334                 confList.add(conf);
335             }
336         }
337         return confList;
338     }
339
340     public List<SwitchConfig> getNodeConfigList() {
341         return new ArrayList<SwitchConfig>(nodeConfigList.values());
342     }
343
344     @Override
345     public SwitchConfig getSwitchConfig(String switchId) {
346         return nodeConfigList.get(switchId);
347     }
348
349     public Switch getSwitchByNode(Node node) {
350         Switch sw = new Switch(node);
351         sw.setNode(node);
352         MacAddress mac = (MacAddress) this.getNodeProp(node,
353                 MacAddress.name);
354         if (mac != null) {
355             sw.setDataLayerAddress(mac.getMacAddress());
356         }
357         Set<NodeConnector> ncSet = getPhysicalNodeConnectors(node);
358         sw.setNodeConnectors(ncSet);
359
360         List<NodeConnector> ncList = new ArrayList<NodeConnector>();
361         for (NodeConnector nodeConnector : ncSet) {
362             if (spanNodeConnectors.contains(nodeConnector)) {
363                 ncList.add(nodeConnector);
364             }
365         }
366         sw.addSpanPorts(ncList);
367
368         return sw;
369     }
370
371     @Override
372     public List<Switch> getNetworkDevices() {
373         Set<Node> nodeSet = getNodes();
374         List<Switch> swList = new ArrayList<Switch>();
375         if (nodeSet != null) {
376             for (Node node : nodeSet) {
377                 swList.add(getSwitchByNode(node));
378             }
379         }
380
381         return swList;
382     }
383
384     private Status updateConfig(SubnetConfig conf, boolean add) {
385         if (add) {
386             if(subnetsConfigList.putIfAbsent(conf.getName(), conf) != null) {
387                 String msg = "Cluster conflict: Subnet with name " + conf.getName() + "already exists.";
388                 return new Status(StatusCode.CONFLICT, msg);
389             }
390         } else {
391             subnetsConfigList.remove(conf.getName());
392         }
393         return new Status(StatusCode.SUCCESS);
394     }
395
396     private Status updateDatabase(SubnetConfig conf, boolean add) {
397         if (add) {
398             Subnet subnetCurr = subnets.get(conf.getIPAddress());
399             Subnet subnet;
400             if (subnetCurr == null) {
401                 subnet = new Subnet(conf);
402             } else {
403                 subnet = subnetCurr.clone();
404             }
405             // In case of API3 call we may receive the ports along with the
406             // subnet creation
407             if (!conf.isGlobal()) {
408                 subnet.addNodeConnectors(conf.getNodeConnectors());
409             }
410             boolean putNewSubnet = false;
411             if(subnetCurr == null) {
412                 if(subnets.putIfAbsent(conf.getIPAddress(), subnet) == null) {
413                     putNewSubnet = true;
414                 }
415             } else {
416                 putNewSubnet = subnets.replace(conf.getIPAddress(), subnetCurr, subnet);
417             }
418             if(!putNewSubnet) {
419                 String msg = "Cluster conflict: Conflict while adding the subnet " + conf.getIPAddress();
420                 return new Status(StatusCode.CONFLICT, msg);
421             }
422
423         // Subnet removal case
424         } else {
425             subnets.remove(conf.getIPAddress());
426         }
427         return new Status(StatusCode.SUCCESS);
428     }
429
430     private Status semanticCheck(SubnetConfig conf) {
431         Subnet newSubnet = new Subnet(conf);
432         Set<InetAddress> IPs = subnets.keySet();
433         if (IPs == null) {
434             return new Status(StatusCode.SUCCESS);
435         }
436         for (InetAddress i : IPs) {
437             Subnet existingSubnet = subnets.get(i);
438             if ((existingSubnet != null) && !existingSubnet.isMutualExclusive(newSubnet)) {
439                 return new Status(StatusCode.CONFLICT, "This subnet conflicts with an existing one.");
440             }
441         }
442         return new Status(StatusCode.SUCCESS);
443     }
444
445     private Status addRemoveSubnet(SubnetConfig conf, boolean isAdding) {
446         // Valid configuration check
447         Status status = conf.validate();
448         if (!status.isSuccess()) {
449             log.warn(status.getDescription());
450             return status;
451         }
452
453         if (isAdding) {
454             // Presence check
455             if (subnetsConfigList.containsKey(conf.getName())) {
456                 return new Status(StatusCode.CONFLICT,
457                         "Subnet with the specified name already exists.");
458             }
459             // Semantic check
460             status = semanticCheck(conf);
461             if (!status.isSuccess()) {
462                 return status;
463             }
464         }
465
466         // Update Database
467         status = updateDatabase(conf, isAdding);
468
469         if (status.isSuccess()) {
470             // Update Configuration
471             status = updateConfig(conf, isAdding);
472             if(!status.isSuccess()) {
473                 updateDatabase(conf, (!isAdding));
474             }
475         }
476
477         return status;
478     }
479
480     /**
481      * Adds Subnet configured in GUI or API3
482      */
483     @Override
484     public Status addSubnet(SubnetConfig conf) {
485         return this.addRemoveSubnet(conf, true);
486     }
487
488     @Override
489     public Status removeSubnet(SubnetConfig conf) {
490         return this.addRemoveSubnet(conf, false);
491     }
492
493     @Override
494     public Status removeSubnet(String name) {
495         SubnetConfig conf = subnetsConfigList.get(name);
496         if (conf == null) {
497             return new Status(StatusCode.SUCCESS, "Subnet not present");
498         }
499         return this.addRemoveSubnet(conf, false);
500     }
501
502     @Override
503     public Status modifySubnet(SubnetConfig conf) {
504         // Sanity check
505         if (conf == null) {
506             return new Status(StatusCode.BADREQUEST, "Invalid Subnet configuration: null");
507         }
508
509         // Valid configuration check
510         Status status = conf.validate();
511         if (!status.isSuccess()) {
512             log.warn(status.getDescription());
513             return status;
514         }
515
516         // If a subnet configuration with this name does not exist, consider this is a creation
517         SubnetConfig target = subnetsConfigList.get(conf.getName());
518         if (target == null) {
519             return this.addSubnet(conf);
520         }
521
522         // No change
523         if (target.equals(conf)) {
524             return new Status(StatusCode.SUCCESS);
525         }
526
527         // Check not allowed modifications
528         if (!target.getSubnet().equals(conf.getSubnet())) {
529             return new Status(StatusCode.BADREQUEST, "IP address change is not allowed");
530         }
531
532         // Derive the set of node connectors that are being removed
533         Set<NodeConnector> toRemove = target.getNodeConnectors();
534         toRemove.removeAll(conf.getNodeConnectors());
535         List<String> nodeConnectorStrings = null;
536         if (!toRemove.isEmpty()) {
537             nodeConnectorStrings = new ArrayList<String>();
538             for (NodeConnector nc : toRemove) {
539                 nodeConnectorStrings.add(nc.toString());
540             }
541             status = this.removePortsFromSubnet(conf.getName(), nodeConnectorStrings);
542             if (!status.isSuccess()) {
543                 return status;
544             }
545         }
546
547         // Derive the set of node connectors that are being added
548         Set<NodeConnector> toAdd = conf.getNodeConnectors();
549         toAdd.removeAll(target.getNodeConnectors());
550         if (!toAdd.isEmpty()) {
551             List<String> nodeConnectorStringRemoved = nodeConnectorStrings;
552             nodeConnectorStrings = new ArrayList<String>();
553             for (NodeConnector nc : toAdd) {
554                 nodeConnectorStrings.add(nc.toString());
555             }
556             status = this.addPortsToSubnet(conf.getName(), nodeConnectorStrings);
557             if (!status.isSuccess()) {
558                 // If any port was removed, add it back as a best recovery effort
559                 if (!toRemove.isEmpty()) {
560                     this.addPortsToSubnet(conf.getName(), nodeConnectorStringRemoved);
561                 }
562                 return status;
563             }
564         }
565
566         // Update Configuration
567         subnetsConfigList.put(conf.getName(), conf);
568
569         return new Status(StatusCode.SUCCESS);
570     }
571
572     @Override
573     public Status addPortsToSubnet(String name, List<String> switchPorts) {
574         if (name == null) {
575             return new Status(StatusCode.BADREQUEST, "Null subnet name");
576         }
577         SubnetConfig confCurr = subnetsConfigList.get(name);
578         if (confCurr == null) {
579             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
580         }
581
582         if (switchPorts == null || switchPorts.isEmpty()) {
583             return new Status(StatusCode.BADREQUEST, "Null or empty port set");
584         }
585
586         Subnet subCurr = subnets.get(confCurr.getIPAddress());
587         if (subCurr == null) {
588             log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPAddress());
589             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
590         }
591
592         // Update Database
593         Subnet sub = subCurr.clone();
594         Set<NodeConnector> sp = NodeConnector.fromString(switchPorts);
595         sub.addNodeConnectors(sp);
596         boolean subnetsReplaced = subnets.replace(confCurr.getIPAddress(), subCurr, sub);
597         if (!subnetsReplaced) {
598             String msg = "Cluster conflict: Conflict while adding ports to the subnet " + name;
599             return new Status(StatusCode.CONFLICT, msg);
600         }
601
602         // Update Configuration
603         SubnetConfig conf = confCurr.clone();
604         conf.addNodeConnectors(switchPorts);
605         boolean configReplaced = subnetsConfigList.replace(name, confCurr, conf);
606         if (!configReplaced) {
607             // TODO: recovery using Transactionality
608             String msg = "Cluster conflict: Conflict while adding ports to the subnet " + name;
609             return new Status(StatusCode.CONFLICT, msg);
610         }
611
612         return new Status(StatusCode.SUCCESS);
613     }
614
615     @Override
616     public Status removePortsFromSubnet(String name, List<String> switchPorts) {
617         if (name == null) {
618             return new Status(StatusCode.BADREQUEST, "Null subnet name");
619         }
620         SubnetConfig confCurr = subnetsConfigList.get(name);
621         if (confCurr == null) {
622             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
623         }
624
625         if (switchPorts == null || switchPorts.isEmpty()) {
626             return new Status(StatusCode.BADREQUEST, "Null or empty port set");
627         }
628
629         Subnet subCurr = subnets.get(confCurr.getIPAddress());
630         if (subCurr == null) {
631             log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPAddress());
632             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
633         }
634
635         // Validation check
636         Status status = SubnetConfig.validatePorts(switchPorts);
637         if (!status.isSuccess()) {
638             return status;
639         }
640         // Update Database
641         Subnet sub = subCurr.clone();
642         Set<NodeConnector> sp = NodeConnector.fromString(switchPorts);
643         sub.deleteNodeConnectors(sp);
644         boolean subnetsReplace = subnets.replace(confCurr.getIPAddress(), subCurr, sub);
645         if (!subnetsReplace) {
646             String msg = "Cluster conflict: Conflict while removing ports from the subnet " + name;
647             return new Status(StatusCode.CONFLICT, msg);
648         }
649
650         // Update Configuration
651         SubnetConfig conf = confCurr.clone();
652         conf.removeNodeConnectors(switchPorts);
653         boolean result = subnetsConfigList.replace(name, confCurr, conf);
654         if (!result) {
655             // TODO: recovery using Transactionality
656             String msg = "Cluster conflict: Conflict while removing ports from " + conf;
657             return new Status(StatusCode.CONFLICT, msg);
658         }
659
660         return new Status(StatusCode.SUCCESS);
661     }
662
663     public String getContainerName() {
664         if (containerName == null) {
665             return GlobalConstants.DEFAULT.toString();
666         }
667         return containerName;
668     }
669
670     @Override
671     public Subnet getSubnetByNetworkAddress(InetAddress networkAddress) {
672         // if there are no subnets, return the default subnet
673         if (subnets.size() == 0) {
674             return DEFAULT_SUBNET;
675         }
676
677         Subnet sub;
678         Set<InetAddress> indices = subnets.keySet();
679         for (InetAddress i : indices) {
680             sub = subnets.get(i);
681             if (sub.isSubnetOf(networkAddress)) {
682                 return sub;
683             }
684         }
685         return null;
686     }
687
688     @Override
689     public Object readObject(ObjectInputStream ois)
690             throws FileNotFoundException, IOException, ClassNotFoundException {
691         // Perform the class deserialization locally, from inside the package
692         // where the class is defined
693         return ois.readObject();
694     }
695
696     @SuppressWarnings("unchecked")
697     private void loadSubnetConfiguration() {
698         ObjectReader objReader = new ObjectReader();
699         ConcurrentMap<String, SubnetConfig> confList = (ConcurrentMap<String, SubnetConfig>) objReader
700                 .read(this, subnetFileName);
701
702         if (confList == null) {
703             return;
704         }
705
706         for (SubnetConfig conf : confList.values()) {
707             addSubnet(conf);
708         }
709     }
710
711     @SuppressWarnings("unchecked")
712     private void loadSpanConfiguration() {
713         ObjectReader objReader = new ObjectReader();
714         ConcurrentMap<Integer, SpanConfig> confList = (ConcurrentMap<Integer, SpanConfig>) objReader
715                 .read(this, spanFileName);
716
717         if (confList == null) {
718             return;
719         }
720
721         for (SpanConfig conf : confList.values()) {
722             addSpanConfig(conf);
723         }
724     }
725
726     @SuppressWarnings("unchecked")
727     private void loadSwitchConfiguration() {
728         ObjectReader objReader = new ObjectReader();
729         ConcurrentMap<String, SwitchConfig> confList = (ConcurrentMap<String, SwitchConfig>) objReader
730                 .read(this, switchConfigFileName);
731
732         if (confList == null) {
733             return;
734         }
735
736         for (SwitchConfig conf : confList.values()) {
737             updateNodeConfig(conf);
738         }
739     }
740
741     @SuppressWarnings("deprecation")
742     @Override
743     public void updateSwitchConfig(SwitchConfig cfgObject) {
744         // update default container only
745         if (!isDefaultContainer) {
746             return;
747         }
748
749         SwitchConfig sc = nodeConfigList.get(cfgObject.getNodeId());
750         if (sc == null) {
751             if (nodeConfigList.putIfAbsent(cfgObject.getNodeId(), cfgObject) != null) {
752                 return;
753             }
754         } else {
755             if (!nodeConfigList.replace(cfgObject.getNodeId(), sc, cfgObject)) {
756                 return;
757             }
758         }
759
760         boolean modeChange = false;
761
762         if ((sc == null) || !cfgObject.getMode().equals(sc.getMode())) {
763             modeChange = true;
764         }
765
766         String nodeId = cfgObject.getNodeId();
767         Node node = Node.fromString(nodeId);
768         Map<String, Property> propMapCurr = nodeProps.get(node);
769         if (propMapCurr == null) {
770             return;
771         }
772         Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
773         Property desc = new Description(cfgObject.getNodeDescription());
774         propMap.put(desc.getName(), desc);
775         Property tier = new Tier(Integer.parseInt(cfgObject.getTier()));
776         propMap.put(tier.getName(), tier);
777
778         if (!nodeProps.replace(node, propMapCurr, propMap)) {
779             // TODO rollback using Transactionality
780             return;
781         }
782
783         log.info("Set Node {}'s Mode to {}", nodeId, cfgObject.getMode());
784
785         if (modeChange) {
786             notifyModeChange(node, cfgObject.isProactive());
787         }
788     }
789
790     @Override
791     public Status updateNodeConfig(SwitchConfig switchConfig) {
792         Status status = switchConfig.validate();
793         if (!status.isSuccess()) {
794             return status;
795         }
796
797         Map<String, Property> updateProperties = switchConfig.getNodeProperties();
798         ForwardingMode mode = (ForwardingMode) updateProperties.get(ForwardingMode.name);
799         if (mode != null) {
800             if (isDefaultContainer) {
801                 if (!mode.isValid()) {
802                     return new Status(StatusCode.BADREQUEST, "Invalid Forwarding Mode Value");
803                 }
804             } else {
805                 return new Status(StatusCode.NOTACCEPTABLE,
806                         "Forwarding Mode modification is allowed only in default container");
807             }
808         }
809
810         Description description = (Description) switchConfig.getProperty(Description.propertyName);
811         String nodeId = switchConfig.getNodeId();
812         Node node = Node.fromString(nodeId);
813         NodeDescription nodeDesc = (this.statisticsManager == null) ? null : this.statisticsManager
814                 .getNodeDescription(node);
815         String advertisedDesc = (nodeDesc == null) ? "" : nodeDesc.getDescription();
816         if (description != null && description.getValue() != null) {
817             if (description.getValue().isEmpty() || description.getValue().equals(advertisedDesc)) {
818                 updateProperties.remove(Description.propertyName);
819                 switchConfig = new SwitchConfig(nodeId, updateProperties);
820             } else {
821                 // check if description is configured or was published by any other node
822                 for (Map.Entry<Node, Map<String, Property>> entry : nodeProps.entrySet()) {
823                     Node n = entry.getKey();
824                     Description desc = (Description) getNodeProp(n, Description.propertyName);
825                     NodeDescription nDesc = (this.statisticsManager == null) ? null : this.statisticsManager
826                             .getNodeDescription(n);
827                     String advDesc = (nDesc == null) ? "" : nDesc.getDescription();
828                     if ((description.equals(desc) || description.getValue().equals(advDesc)) && !node.equals(n)) {
829                         return new Status(StatusCode.CONFLICT, "Node name already in use");
830                     }
831                 }
832             }
833         }
834
835         boolean modeChange = false;
836         SwitchConfig sc = nodeConfigList.get(nodeId);
837         Map<String, Property> prevNodeProperties = new HashMap<String, Property>();
838         if (sc == null) {
839             if ((mode != null) && mode.isProactive()) {
840                 modeChange = true;
841             }
842             if (!updateProperties.isEmpty()) {
843                 if (nodeConfigList.putIfAbsent(nodeId, switchConfig) != null) {
844                     return new Status(StatusCode.CONFLICT, "Cluster conflict: Unable to update node configuration");
845                 }
846             }
847         } else {
848             prevNodeProperties = new HashMap<String, Property>(sc.getNodeProperties());
849             ForwardingMode prevMode = (ForwardingMode) sc.getProperty(ForwardingMode.name);
850             if (mode == null) {
851                 if ((prevMode != null) && (prevMode.isProactive())) {
852                     modeChange = true;
853                 }
854             } else {
855                 if (((prevMode != null) && (prevMode.getValue() != mode.getValue()))
856                         || (prevMode == null && mode.isProactive())) {
857                     modeChange = true;
858                 }
859             }
860             if (updateProperties.isEmpty()) {
861                 nodeConfigList.remove(nodeId);
862             } else {
863                 if (!nodeConfigList.replace(nodeId, sc, switchConfig)) {
864                     return new Status(StatusCode.CONFLICT, "Cluster conflict: Unable to update node configuration");
865                 }
866             }
867         }
868         Map<String, Property> propMapCurr = nodeProps.get(node);
869         if (propMapCurr == null) {
870             return new Status(StatusCode.SUCCESS);
871         }
872         Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
873         for (Map.Entry<String, Property> entry : prevNodeProperties.entrySet()) {
874             String prop = entry.getKey();
875             if (!updateProperties.containsKey(prop)) {
876                 if (prop.equals(Description.propertyName)) {
877                     if (!advertisedDesc.isEmpty()) {
878                         Property desc = new Description(advertisedDesc);
879                         propMap.put(Description.propertyName, desc);
880                     }
881                     continue;
882                 } else if (prop.equals(ForwardingMode.name)) {
883                     Property defaultMode = new ForwardingMode(ForwardingMode.REACTIVE_FORWARDING);
884                     propMap.put(ForwardingMode.name, defaultMode);
885                     continue;
886                 }
887                 propMap.remove(prop);
888             }
889         }
890         propMap.putAll(updateProperties);
891         if (!nodeProps.replace(node, propMapCurr, propMap)) {
892             // TODO rollback using Transactionality
893             return new Status(StatusCode.CONFLICT, "Cluster conflict: Unable to update node configuration");
894         }
895         if (modeChange) {
896             notifyModeChange(node, (mode == null) ? false : mode.isProactive());
897         }
898         return new Status(StatusCode.SUCCESS);
899     }
900
901     @Override
902     public Status removeNodeConfig(String nodeId) {
903         if ((nodeId == null) || (nodeId.isEmpty())) {
904             return new Status(StatusCode.BADREQUEST, "nodeId cannot be empty.");
905         }
906         Map<String, Property> nodeProperties = getSwitchConfig(nodeId).getNodeProperties();
907         Node node = Node.fromString(nodeId);
908         Map<String, Property> propMapCurr = nodeProps.get(node);
909         if ((propMapCurr != null) && (nodeProperties != null) && (!nodeProperties.isEmpty())) {
910             Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
911             for (Map.Entry<String, Property> entry : nodeProperties.entrySet()) {
912                 String prop = entry.getKey();
913                 if (prop.equals(Description.propertyName)) {
914                     Map<Node, Map<String, Property>> nodeProp = this.inventoryService.getNodeProps();
915                     if (nodeProp.get(node) != null) {
916                         propMap.put(Description.propertyName, nodeProp.get(node).get(Description.propertyName));
917                         continue;
918                     }
919                 }
920                 propMap.remove(prop);
921             }
922             if (!nodeProps.replace(node, propMapCurr, propMap)) {
923                 return new Status(StatusCode.CONFLICT, "Cluster conflict: Unable to update node configuration.");
924             }
925         }
926         if (nodeConfigList != null) {
927             nodeConfigList.remove(nodeId);
928         }
929         return new Status(StatusCode.SUCCESS);
930     }
931
932     @Override
933     public Status saveSwitchConfig() {
934         return saveSwitchConfigInternal();
935     }
936
937     public Status saveSwitchConfigInternal() {
938         Status retS = null, retP = null;
939         ObjectWriter objWriter = new ObjectWriter();
940
941         retS = objWriter.write(new ConcurrentHashMap<String, SubnetConfig>(
942                 subnetsConfigList), subnetFileName);
943         retP = objWriter.write(new ConcurrentHashMap<SpanConfig, SpanConfig>(
944                 spanConfigList), spanFileName);
945         retS = objWriter.write(new ConcurrentHashMap<String, SwitchConfig>(
946                 nodeConfigList), switchConfigFileName);
947         if (retS.equals(retP)) {
948             if (retS.isSuccess()) {
949                 return retS;
950             } else {
951                 return new Status(StatusCode.INTERNALERROR, "Save failed");
952             }
953         } else {
954             return new Status(StatusCode.INTERNALERROR, "Partial save failure");
955         }
956     }
957
958     @Override
959     public List<SpanConfig> getSpanConfigList() {
960         return new ArrayList<SpanConfig>(spanConfigList.values());
961     }
962
963     @Override
964     public Status addSpanConfig(SpanConfig conf) {
965         // Valid config check
966         if (!conf.isValidConfig()) {
967             String msg = "Invalid Span configuration";
968             log.warn(msg);
969             return new Status(StatusCode.BADREQUEST, msg);
970         }
971
972         // Presence check
973         if (spanConfigList.containsKey(conf)) {
974             return new Status(StatusCode.CONFLICT, "Same span config exists");
975         }
976
977         // Update configuration
978         if (spanConfigList.putIfAbsent(conf, conf) == null) {
979             // Update database and notify clients
980             addSpanPorts(conf.getNode(), conf.getPortArrayList());
981         }
982
983         return new Status(StatusCode.SUCCESS);
984     }
985
986     @Override
987     public Status removeSpanConfig(SpanConfig conf) {
988         removeSpanPorts(conf.getNode(), conf.getPortArrayList());
989
990         // Update configuration
991         spanConfigList.remove(conf);
992
993         return new Status(StatusCode.SUCCESS);
994     }
995
996     @Override
997     public List<NodeConnector> getSpanPorts(Node node) {
998         List<NodeConnector> ncList = new ArrayList<NodeConnector>();
999
1000         for (NodeConnector nodeConnector : spanNodeConnectors) {
1001             if (nodeConnector.getNode().equals(node)) {
1002                 ncList.add(nodeConnector);
1003             }
1004         }
1005         return ncList;
1006     }
1007
1008     private void addNode(Node node, Set<Property> props) {
1009         log.trace("{} added, props: {}", node, props);
1010         if (nodeProps == null) {
1011             return;
1012         }
1013
1014         Map<String, Property> propMapCurr = nodeProps.get(node);
1015         Map<String, Property> propMap = (propMapCurr == null) ? new HashMap<String, Property>()
1016                 : new HashMap<String, Property>(propMapCurr);
1017
1018         // copy node properties from plugin
1019         if (props != null) {
1020             for (Property prop : props) {
1021                 propMap.put(prop.getName(), prop);
1022             }
1023         }
1024
1025         boolean proactiveForwarding = false;
1026         // copy node properties from config
1027         if (nodeConfigList != null) {
1028             String nodeId = node.toString();
1029             SwitchConfig conf = nodeConfigList.get(nodeId);
1030             if (conf != null && (conf.getNodeProperties() != null)) {
1031                 Map<String, Property> nodeProperties = conf.getNodeProperties();
1032                 propMap.putAll(nodeProperties);
1033                 if (nodeProperties.get(ForwardingMode.name) != null) {
1034                     ForwardingMode mode = (ForwardingMode) nodeProperties.get(ForwardingMode.name);
1035                     proactiveForwarding = mode.isProactive();
1036                 }
1037             }
1038         }
1039
1040         if (!propMap.containsKey(ForwardingMode.name)) {
1041             Property defaultMode = new ForwardingMode(ForwardingMode.REACTIVE_FORWARDING);
1042             propMap.put(ForwardingMode.name, defaultMode);
1043         }
1044         boolean result = false;
1045         if (propMapCurr == null) {
1046             if (nodeProps.putIfAbsent(node, propMap) == null) {
1047                 result = true;
1048             }
1049         } else {
1050             result = nodeProps.replace(node, propMapCurr, propMap);
1051         }
1052         if (!result) {
1053             log.debug("Cluster conflict: Conflict while adding the node properties. Node: {}  Properties: {}",
1054                     node.getID(), props);
1055             addNodeProps(node, propMap);
1056         }
1057
1058         // check if span ports are configed
1059         addSpanPorts(node);
1060
1061         // notify node listeners
1062         notifyNode(node, UpdateType.ADDED, propMap);
1063
1064         // notify proactive mode forwarding
1065         if (proactiveForwarding) {
1066             notifyModeChange(node, true);
1067         }
1068     }
1069
1070     private void removeNode(Node node) {
1071         log.trace("{} removed", node);
1072         if (nodeProps == null) {
1073             return;
1074         }
1075         nodeProps.remove(node);
1076         nodeConnectorNames.remove(node);
1077         Set<NodeConnector> removeNodeConnectorSet = new HashSet<NodeConnector>();
1078         for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
1079             NodeConnector nodeConnector = entry.getKey();
1080             if (nodeConnector.getNode().equals(node)) {
1081                 removeNodeConnectorSet.add(nodeConnector);
1082             }
1083         }
1084         for (NodeConnector nc : removeNodeConnectorSet) {
1085             nodeConnectorProps.remove(nc);
1086         }
1087
1088         // check if span ports need to be cleaned up
1089         removeSpanPorts(node);
1090
1091         /* notify node listeners */
1092         notifyNode(node, UpdateType.REMOVED, null);
1093     }
1094
1095     private void updateNode(Node node, Set<Property> props) {
1096         log.trace("{} updated, props: {}", node, props);
1097         if (nodeProps == null || !nodeProps.containsKey(node) ||
1098                 props == null || props.isEmpty()) {
1099             return;
1100         }
1101
1102         Map<String, Property> propMapCurr = nodeProps.get(node);
1103         Map<String, Property> propMap = (propMapCurr == null) ? new HashMap<String, Property>()
1104                 : new HashMap<String, Property>(propMapCurr);
1105
1106         // copy node properties from plugin
1107         String nodeId = node.toString();
1108         for (Property prop : props) {
1109             if (nodeConfigList != null) {
1110                 SwitchConfig conf = nodeConfigList.get(nodeId);
1111                 if (conf != null && (conf.getNodeProperties() != null)
1112                         && conf.getNodeProperties().containsKey(prop.getName())) {
1113                     continue;
1114                 }
1115             }
1116             propMap.put(prop.getName(), prop);
1117         }
1118
1119         if (propMapCurr == null) {
1120             if (nodeProps.putIfAbsent(node, propMap) != null) {
1121                 log.debug("Cluster conflict: Conflict while updating the node. Node: {}  Properties: {}",
1122                         node.getID(), props);
1123                 addNodeProps(node, propMap);
1124             }
1125         } else {
1126             if (!nodeProps.replace(node, propMapCurr, propMap)) {
1127                 log.debug("Cluster conflict: Conflict while updating the node. Node: {}  Properties: {}",
1128                         node.getID(), props);
1129                 addNodeProps(node, propMap);
1130             }
1131         }
1132
1133         /* notify node listeners */
1134         notifyNode(node, UpdateType.CHANGED, propMap);
1135     }
1136
1137     @Override
1138     public void updateNode(Node node, UpdateType type, Set<Property> props) {
1139         log.debug("updateNode: {} type {} props {} for container {}",
1140                 new Object[] { node, type, props, containerName });
1141         switch (type) {
1142         case ADDED:
1143             addNode(node, props);
1144             break;
1145         case CHANGED:
1146             updateNode(node, props);
1147             break;
1148         case REMOVED:
1149             removeNode(node);
1150             break;
1151         default:
1152             break;
1153         }
1154     }
1155
1156     @Override
1157     public void updateNodeConnector(NodeConnector nodeConnector,
1158             UpdateType type, Set<Property> props) {
1159         Map<String, Property> propMap = new HashMap<String, Property>();
1160         boolean update = true;
1161
1162         log.debug("updateNodeConnector: {} type {} props {} for container {}",
1163                 new Object[] { nodeConnector, type, props, containerName });
1164
1165         if (nodeConnectorProps == null) {
1166             return;
1167         }
1168
1169         switch (type) {
1170         case ADDED:
1171             if (props != null) {
1172                 for (Property prop : props) {
1173                     addNodeConnectorProp(nodeConnector, prop);
1174                     propMap.put(prop.getName(), prop);
1175                 }
1176             } else {
1177                 addNodeConnectorProp(nodeConnector, null);
1178             }
1179
1180             addSpanPort(nodeConnector);
1181             break;
1182         case CHANGED:
1183             if (!nodeConnectorProps.containsKey(nodeConnector) || (props == null)) {
1184                 update = false;
1185             } else {
1186                 for (Property prop : props) {
1187                     addNodeConnectorProp(nodeConnector, prop);
1188                     propMap.put(prop.getName(), prop);
1189                 }
1190             }
1191             break;
1192         case REMOVED:
1193             if (!nodeConnectorProps.containsKey(nodeConnector)) {
1194                 update = false;
1195             }
1196             removeNodeConnectorAllProps(nodeConnector);
1197
1198             // clean up span config
1199             removeSpanPort(nodeConnector);
1200             break;
1201         default:
1202             update = false;
1203             break;
1204         }
1205
1206         if (update) {
1207             notifyNodeConnector(nodeConnector, type, propMap);
1208         }
1209     }
1210
1211     @Override
1212     public Set<Node> getNodes() {
1213         return (nodeProps != null) ? new HashSet<Node>(nodeProps.keySet()) : new HashSet<Node>();
1214     }
1215
1216     @Override
1217     public Map<String, Property> getControllerProperties() {
1218         return new HashMap<String, Property>(this.controllerProps);
1219     }
1220
1221     @Override
1222     public Property getControllerProperty(String propertyName) {
1223         if (propertyName != null) {
1224             HashMap<String, Property> propertyMap =  new HashMap<String, Property>(this.controllerProps);
1225             return propertyMap.get(propertyName);
1226         }
1227         return null;
1228     }
1229
1230     @Override
1231     public Status setControllerProperty(Property property) {
1232         if (property != null) {
1233             this.controllerProps.put(property.getName(), property);
1234             return new Status(StatusCode.SUCCESS);
1235         }
1236         return new Status(StatusCode.BADREQUEST, "Invalid property provided when setting property");
1237     }
1238
1239     @Override
1240     public Status removeControllerProperty(String propertyName) {
1241         if (propertyName != null) {
1242             if (this.controllerProps.containsKey(propertyName)) {
1243                 this.controllerProps.remove(propertyName);
1244                 if (!this.controllerProps.containsKey(propertyName)) {
1245                     return new Status(StatusCode.SUCCESS);
1246                 }
1247             }
1248             String msg = "Unable to remove property " + propertyName + " from Controller";
1249             return new Status(StatusCode.BADREQUEST, msg);
1250         }
1251         String msg = "Invalid property provided when removing property from Controller";
1252         return new Status(StatusCode.BADREQUEST, msg);
1253     }
1254
1255     /*
1256      * Returns a copy of a list of properties for a given node
1257      *
1258      * (non-Javadoc)
1259      *
1260      * @see
1261      * org.opendaylight.controller.switchmanager.ISwitchManager#getNodeProps
1262      * (org.opendaylight.controller.sal.core.Node)
1263      */
1264     @Override
1265     public Map<String, Property> getNodeProps(Node node) {
1266         Map<String, Property> rv = new HashMap<String, Property>();
1267         if (this.nodeProps != null) {
1268             rv = this.nodeProps.get(node);
1269             if (rv != null) {
1270                 /* make a copy of it */
1271                 rv = new HashMap<String, Property>(rv);
1272             }
1273         }
1274         return rv;
1275     }
1276
1277     @Override
1278     public Property getNodeProp(Node node, String propName) {
1279         Map<String, Property> propMap = getNodeProps(node);
1280         return (propMap != null) ? propMap.get(propName) : null;
1281     }
1282
1283     @Override
1284     public void setNodeProp(Node node, Property prop) {
1285
1286         for (int i = 0; i <= REPLACE_RETRY; i++) {
1287             /* Get a copy of the property map */
1288             Map<String, Property> propMapCurr = getNodeProps(node);
1289             if (propMapCurr == null) {
1290                 return;
1291             }
1292
1293             Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
1294             propMap.put(prop.getName(), prop);
1295
1296             if (nodeProps.replace(node, propMapCurr, propMap)) {
1297                 return;
1298             }
1299             if (!propMapCurr.get(prop.getName()).equals(nodeProps.get(node).get(prop.getName()))) {
1300                 log.debug("Cluster conflict: Unable to add property {} to node {}.", prop.getName(), node.getID());
1301                 return;
1302             }
1303         }
1304         log.warn("Cluster conflict: Unable to add property {} to node {}.", prop.getName(), node.getID());
1305     }
1306
1307     @Override
1308     public Status removeNodeProp(Node node, String propName) {
1309         for (int i = 0; i <= REPLACE_RETRY; i++) {
1310             Map<String, Property> propMapCurr = getNodeProps(node);
1311             if (propMapCurr != null) {
1312                 if (!propMapCurr.containsKey(propName)) {
1313                     return new Status(StatusCode.SUCCESS);
1314                 }
1315                 Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
1316                 propMap.remove(propName);
1317                 if (nodeProps.replace(node, propMapCurr, propMap)) {
1318                     return new Status(StatusCode.SUCCESS);
1319                 }
1320                 if (!propMapCurr.get(propName).equals(nodeProps.get(node).get(propName))) {
1321                     String msg = "Cluster conflict: Unable to remove property " + propName + " for node "
1322                             + node.getID();
1323                     return new Status(StatusCode.CONFLICT, msg);
1324                 }
1325
1326             } else {
1327                 return new Status(StatusCode.SUCCESS);
1328             }
1329         }
1330         String msg = "Cluster conflict: Unable to remove property " + propName + " for node " + node.getID();
1331         return new Status(StatusCode.CONFLICT, msg);
1332     }
1333
1334     @Override
1335     public Status removeNodeAllProps(Node node) {
1336         this.nodeProps.remove(node);
1337         return new Status(StatusCode.SUCCESS);
1338     }
1339
1340     @Override
1341     public Set<NodeConnector> getUpNodeConnectors(Node node) {
1342         if (nodeConnectorProps == null) {
1343             return null;
1344         }
1345
1346         Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
1347         for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
1348             NodeConnector nodeConnector = entry.getKey();
1349             if (!nodeConnector.getNode().equals(node)) {
1350                 continue;
1351             }
1352             if (isNodeConnectorEnabled(nodeConnector)) {
1353                 nodeConnectorSet.add(nodeConnector);
1354             }
1355         }
1356
1357         return nodeConnectorSet;
1358     }
1359
1360     @Override
1361     public Set<NodeConnector> getNodeConnectors(Node node) {
1362         if (nodeConnectorProps == null) {
1363             return null;
1364         }
1365
1366         Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
1367         for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
1368             NodeConnector nodeConnector = entry.getKey();
1369             if (!nodeConnector.getNode().equals(node)) {
1370                 continue;
1371             }
1372             nodeConnectorSet.add(nodeConnector);
1373         }
1374
1375         return nodeConnectorSet;
1376     }
1377
1378     @Override
1379     public Set<NodeConnector> getPhysicalNodeConnectors(Node node) {
1380         if (nodeConnectorProps == null) {
1381             return null;
1382         }
1383
1384         Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
1385         for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
1386             NodeConnector nodeConnector = entry.getKey();
1387             if (!nodeConnector.getNode().equals(node)
1388                     || isSpecial(nodeConnector)) {
1389                 continue;
1390             }
1391             nodeConnectorSet.add(nodeConnector);
1392         }
1393
1394         return nodeConnectorSet;
1395     }
1396
1397     @Override
1398     public Map<String, Property> getNodeConnectorProps(NodeConnector nodeConnector) {
1399         Map<String, Property> rv = new HashMap<String, Property>();
1400         if (this.nodeConnectorProps != null) {
1401             rv = this.nodeConnectorProps.get(nodeConnector);
1402             if (rv != null) {
1403                 rv = new HashMap<String, Property>(rv);
1404             }
1405         }
1406         return rv;
1407     }
1408
1409     @Override
1410     public Property getNodeConnectorProp(NodeConnector nodeConnector,
1411             String propName) {
1412         Map<String, Property> propMap = getNodeConnectorProps(nodeConnector);
1413         return (propMap != null) ? propMap.get(propName) : null;
1414     }
1415
1416     private byte[] getHardwareMAC() {
1417         Enumeration<NetworkInterface> nis;
1418         byte[] macAddress = null;
1419
1420         try {
1421             nis = NetworkInterface.getNetworkInterfaces();
1422         } catch (SocketException e) {
1423             log.error("Failed to acquire controller MAC: ", e);
1424             return macAddress;
1425         }
1426
1427         while (nis.hasMoreElements()) {
1428             NetworkInterface ni = nis.nextElement();
1429             try {
1430                 macAddress = ni.getHardwareAddress();
1431             } catch (SocketException e) {
1432                 log.error("Failed to acquire controller MAC: ", e);
1433             }
1434             if (macAddress != null) {
1435                 break;
1436             }
1437         }
1438         if (macAddress == null) {
1439             log.warn("Failed to acquire controller MAC: No physical interface found");
1440             // This happens when running controller on windows VM, for example
1441             // Try parsing the OS command output
1442         }
1443         return macAddress;
1444     }
1445
1446     @Override
1447     public byte[] getControllerMAC() {
1448         MacAddress macProperty = (MacAddress)controllerProps.get(MacAddress.name);
1449         return (macProperty == null) ? null : macProperty.getMacAddress();
1450     }
1451
1452     @Override
1453     public NodeConnector getNodeConnector(Node node, String nodeConnectorName) {
1454         if (nodeConnectorNames == null) {
1455             return null;
1456         }
1457
1458         Map<String, NodeConnector> map = nodeConnectorNames.get(node);
1459         if (map == null) {
1460             return null;
1461         }
1462
1463         return map.get(nodeConnectorName);
1464     }
1465
1466     /**
1467      * Adds a node connector and its property if any
1468      *
1469      * @param nodeConnector
1470      *            {@link org.opendaylight.controller.sal.core.NodeConnector}
1471      * @param propName
1472      *            name of {@link org.opendaylight.controller.sal.core.Property}
1473      * @return success or failed reason
1474      */
1475     @Override
1476     public Status addNodeConnectorProp(NodeConnector nodeConnector,
1477             Property prop) {
1478         Map<String, Property> propMapCurr = getNodeConnectorProps(nodeConnector);
1479         Map<String, Property> propMap = (propMapCurr == null) ? new HashMap<String, Property>()
1480                 : new HashMap<String, Property>(propMapCurr);
1481
1482         String msg = "Cluster conflict: Unable to add NodeConnector Property.";
1483         // Just add the nodeConnector if prop is not available (in a non-default
1484         // container)
1485         if (prop == null) {
1486             if (propMapCurr == null) {
1487                 if (nodeConnectorProps.putIfAbsent(nodeConnector, propMap) != null) {
1488                     return new Status(StatusCode.CONFLICT, msg);
1489                 }
1490             } else {
1491                 if (!nodeConnectorProps.replace(nodeConnector, propMapCurr, propMap)) {
1492                     return new Status(StatusCode.CONFLICT, msg);
1493                 }
1494             }
1495             return new Status(StatusCode.SUCCESS);
1496         }
1497
1498         propMap.put(prop.getName(), prop);
1499         if (propMapCurr == null) {
1500             if (nodeConnectorProps.putIfAbsent(nodeConnector, propMap) != null) {
1501                 return new Status(StatusCode.CONFLICT, msg);
1502             }
1503         } else {
1504             if (!nodeConnectorProps.replace(nodeConnector, propMapCurr, propMap)) {
1505                 return new Status(StatusCode.CONFLICT, msg);
1506             }
1507         }
1508
1509         if (prop.getName().equals(Name.NamePropName)) {
1510             if (nodeConnectorNames != null) {
1511                 Node node = nodeConnector.getNode();
1512                 Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
1513                 Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
1514                 if (mapCurr != null) {
1515                     for (Map.Entry<String, NodeConnector> entry : mapCurr.entrySet()) {
1516                         String s = entry.getKey();
1517                         try {
1518                             map.put(s, new NodeConnector(entry.getValue()));
1519                         } catch (ConstructionException e) {
1520                             log.error("An error occured",e);
1521                         }
1522                     }
1523                 }
1524
1525                 map.put(((Name) prop).getValue(), nodeConnector);
1526                 if (mapCurr == null) {
1527                     if (nodeConnectorNames.putIfAbsent(node, map) != null) {
1528                         // TODO: recovery using Transactionality
1529                         return new Status(StatusCode.CONFLICT, msg);
1530                     }
1531                 } else {
1532                     if (!nodeConnectorNames.replace(node, mapCurr, map)) {
1533                         // TODO: recovery using Transactionality
1534                         return new Status(StatusCode.CONFLICT, msg);
1535                     }
1536                 }
1537             }
1538         }
1539
1540         return new Status(StatusCode.SUCCESS);
1541     }
1542
1543     /**
1544      * Removes one property of a node connector
1545      *
1546      * @param nodeConnector
1547      *            {@link org.opendaylight.controller.sal.core.NodeConnector}
1548      * @param propName
1549      *            name of {@link org.opendaylight.controller.sal.core.Property}
1550      * @return success or failed reason
1551      */
1552     @Override
1553     public Status removeNodeConnectorProp(NodeConnector nodeConnector, String propName) {
1554         Map<String, Property> propMapCurr = getNodeConnectorProps(nodeConnector);
1555
1556         if (propMapCurr == null) {
1557             /* Nothing to remove */
1558             return new Status(StatusCode.SUCCESS);
1559         }
1560
1561         Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
1562         propMap.remove(propName);
1563         boolean result = nodeConnectorProps.replace(nodeConnector, propMapCurr, propMap);
1564         String msg = "Cluster conflict: Unable to remove NodeConnector property.";
1565         if (!result) {
1566             return new Status(StatusCode.CONFLICT, msg);
1567         }
1568
1569         if (propName.equals(Name.NamePropName)) {
1570             if (nodeConnectorNames != null) {
1571                 Name name = ((Name) getNodeConnectorProp(nodeConnector, Name.NamePropName));
1572                 if (name != null) {
1573                     Node node = nodeConnector.getNode();
1574                     Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
1575                     if (mapCurr != null) {
1576                         Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
1577                         for (Map.Entry<String, NodeConnector> entry : mapCurr.entrySet()) {
1578                             String s = entry.getKey();
1579                             try {
1580                                 map.put(s, new NodeConnector(entry.getValue()));
1581                             } catch (ConstructionException e) {
1582                                 log.error("An error occured",e);
1583                             }
1584                         }
1585                         map.remove(name.getValue());
1586                         if (!nodeConnectorNames.replace(node, mapCurr, map)) {
1587                             // TODO: recovery using Transactionality
1588                             return new Status(StatusCode.CONFLICT, msg);
1589                         }
1590                     }
1591                 }
1592             }
1593         }
1594
1595         return new Status(StatusCode.SUCCESS);
1596     }
1597
1598     /**
1599      * Removes all the properties of a node connector
1600      *
1601      * @param nodeConnector
1602      *            {@link org.opendaylight.controller.sal.core.NodeConnector}
1603      * @return success or failed reason
1604      */
1605     @Override
1606     public Status removeNodeConnectorAllProps(NodeConnector nodeConnector) {
1607         if (nodeConnectorNames != null) {
1608             Name name = ((Name) getNodeConnectorProp(nodeConnector, Name.NamePropName));
1609             if (name != null) {
1610                 Node node = nodeConnector.getNode();
1611                 Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
1612                 if (mapCurr != null) {
1613                     Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
1614                     for (Map.Entry<String, NodeConnector> entry : mapCurr.entrySet()) {
1615                         String s = entry.getKey();
1616                         try {
1617                             map.put(s, new NodeConnector(entry.getValue()));
1618                         } catch (ConstructionException e) {
1619                             log.error("An error occured",e);
1620                         }
1621                     }
1622                     map.remove(name.getValue());
1623                     if (map.isEmpty()) {
1624                         nodeConnectorNames.remove(node);
1625                     } else {
1626                         if (!nodeConnectorNames.replace(node, mapCurr, map)) {
1627                             log.warn("Cluster conflict: Unable remove Name property of nodeconnector {}, skip.",
1628                                     nodeConnector.getID());
1629                         }
1630                     }
1631                 }
1632
1633             }
1634         }
1635         nodeConnectorProps.remove(nodeConnector);
1636
1637         return new Status(StatusCode.SUCCESS);
1638     }
1639
1640     /**
1641      * Function called by the dependency manager when all the required
1642      * dependencies are satisfied
1643      *
1644      */
1645     void init(Component c) {
1646         Dictionary<?, ?> props = c.getServiceProperties();
1647         if (props != null) {
1648             this.containerName = (String) props.get("containerName");
1649             log.trace("Running containerName: {}", this.containerName);
1650         } else {
1651             // In the Global instance case the containerName is empty
1652             this.containerName = "";
1653         }
1654         isDefaultContainer = containerName.equals(GlobalConstants.DEFAULT
1655                 .toString());
1656
1657         startUp();
1658     }
1659
1660     /**
1661      * Function called by the dependency manager when at least one dependency
1662      * become unsatisfied or when the component is shutting down because for
1663      * example bundle is being stopped.
1664      *
1665      */
1666     void destroy() {
1667         shutDown();
1668     }
1669
1670     /**
1671      * Function called by dependency manager after "init ()" is called and after
1672      * the services provided by the class are registered in the service registry
1673      *
1674      */
1675     void start() {
1676         // OSGI console
1677         registerWithOSGIConsole();
1678     }
1679
1680     /**
1681      * Function called after registered the service in OSGi service registry.
1682      */
1683     void started() {
1684         // solicit for existing inventories
1685         getInventories();
1686     }
1687
1688     /**
1689      * Function called by the dependency manager before the services exported by
1690      * the component are unregistered, this will be followed by a "destroy ()"
1691      * calls
1692      *
1693      */
1694     void stop() {
1695     }
1696
1697     public void setInventoryService(IInventoryService service) {
1698         log.trace("Got inventory service set request {}", service);
1699         this.inventoryService = service;
1700
1701         // solicit for existing inventories
1702         getInventories();
1703     }
1704
1705     public void unsetInventoryService(IInventoryService service) {
1706         log.trace("Got a service UNset request");
1707         this.inventoryService = null;
1708
1709         // clear existing inventories
1710         clearInventories();
1711     }
1712
1713     public void setStatisticsManager(IStatisticsManager statisticsManager) {
1714         log.trace("Got statistics manager set request {}", statisticsManager);
1715         this.statisticsManager = statisticsManager;
1716     }
1717
1718     public void unsetStatisticsManager(IStatisticsManager statisticsManager) {
1719         log.trace("Got statistics manager UNset request");
1720         this.statisticsManager = null;
1721     }
1722
1723     public void setSwitchManagerAware(ISwitchManagerAware service) {
1724         log.trace("Got inventory service set request {}", service);
1725         if (this.switchManagerAware != null) {
1726             this.switchManagerAware.add(service);
1727         }
1728
1729         // bulk update for newly joined
1730         switchManagerAwareNotify(service);
1731     }
1732
1733     public void unsetSwitchManagerAware(ISwitchManagerAware service) {
1734         log.trace("Got a service UNset request");
1735         if (this.switchManagerAware != null) {
1736             this.switchManagerAware.remove(service);
1737         }
1738     }
1739
1740     public void setInventoryListener(IInventoryListener service) {
1741         log.trace("Got inventory listener set request {}", service);
1742         if (this.inventoryListeners != null) {
1743             this.inventoryListeners.add(service);
1744         }
1745
1746         // bulk update for newly joined
1747         bulkUpdateService(service);
1748     }
1749
1750     public void unsetInventoryListener(IInventoryListener service) {
1751         log.trace("Got a service UNset request");
1752         if (this.inventoryListeners != null) {
1753             this.inventoryListeners.remove(service);
1754         }
1755     }
1756
1757     public void setSpanAware(ISpanAware service) {
1758         log.trace("Got SpanAware set request {}", service);
1759         if (this.spanAware != null) {
1760             this.spanAware.add(service);
1761         }
1762
1763         // bulk update for newly joined
1764         spanAwareNotify(service);
1765     }
1766
1767     public void unsetSpanAware(ISpanAware service) {
1768         log.trace("Got a service UNset request");
1769         if (this.spanAware != null) {
1770             this.spanAware.remove(service);
1771         }
1772     }
1773
1774     void setClusterContainerService(IClusterContainerServices s) {
1775         log.trace("Cluster Service set");
1776         this.clusterContainerService = s;
1777     }
1778
1779     void unsetClusterContainerService(IClusterContainerServices s) {
1780         if (this.clusterContainerService == s) {
1781             log.trace("Cluster Service removed!");
1782             this.clusterContainerService = null;
1783         }
1784     }
1785
1786     private void getInventories() {
1787         if (inventoryService == null) {
1788             log.trace("inventory service not avaiable");
1789             return;
1790         }
1791
1792         Map<Node, Map<String, Property>> nodeProp = this.inventoryService.getNodeProps();
1793         for (Map.Entry<Node, Map<String, Property>> entry : nodeProp.entrySet()) {
1794             Node node = entry.getKey();
1795             log.debug("getInventories: {} added for container {}", new Object[] { node, containerName });
1796             Map<String, Property> propMap = entry.getValue();
1797             Set<Property> props = new HashSet<Property>();
1798             for (Property property : propMap.values()) {
1799                 props.add(property);
1800             }
1801             addNode(node, props);
1802         }
1803
1804         Map<NodeConnector, Map<String, Property>> nodeConnectorProp = this.inventoryService.getNodeConnectorProps();
1805         for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProp.entrySet()) {
1806             Map<String, Property> propMap = entry.getValue();
1807             for (Property property : propMap.values()) {
1808                 addNodeConnectorProp(entry.getKey(), property);
1809             }
1810         }
1811     }
1812
1813     private void clearInventories() {
1814         nodeProps.clear();
1815         nodeConnectorProps.clear();
1816         nodeConnectorNames.clear();
1817         spanNodeConnectors.clear();
1818     }
1819
1820     private void notifyNode(Node node, UpdateType type,
1821             Map<String, Property> propMap) {
1822         synchronized (inventoryListeners) {
1823             for (IInventoryListener service : inventoryListeners) {
1824                 service.notifyNode(node, type, propMap);
1825             }
1826         }
1827     }
1828
1829     private void notifyNodeConnector(NodeConnector nodeConnector,
1830             UpdateType type, Map<String, Property> propMap) {
1831         synchronized (inventoryListeners) {
1832             for (IInventoryListener service : inventoryListeners) {
1833                 service.notifyNodeConnector(nodeConnector, type, propMap);
1834             }
1835         }
1836     }
1837
1838     /*
1839      * For those joined late, bring them up-to-date.
1840      */
1841     private void switchManagerAwareNotify(ISwitchManagerAware service) {
1842         for (Subnet sub : subnets.values()) {
1843             service.subnetNotify(sub, true);
1844         }
1845
1846         for (Node node : getNodes()) {
1847             SwitchConfig sc = getSwitchConfig(node.toString());
1848             if ((sc != null) && isDefaultContainer) {
1849                 ForwardingMode mode = (ForwardingMode) sc.getProperty(ForwardingMode.name);
1850                 service.modeChangeNotify(node, (mode == null) ? false : mode.isProactive());
1851             }
1852         }
1853     }
1854
1855     private void bulkUpdateService(IInventoryListener service) {
1856         Map<String, Property> propMap;
1857         UpdateType type = UpdateType.ADDED;
1858
1859         for (Node node : getNodes()) {
1860             propMap = nodeProps.get(node);
1861             service.notifyNode(node, type, propMap);
1862         }
1863
1864         for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
1865             NodeConnector nodeConnector = entry.getKey();
1866             propMap = nodeConnectorProps.get(nodeConnector);
1867             service.notifyNodeConnector(nodeConnector, type, propMap);
1868         }
1869     }
1870
1871     private void spanAwareNotify(ISpanAware service) {
1872         for (Node node : getNodes()) {
1873             for (SpanConfig conf : getSpanConfigList(node)) {
1874                 service.spanUpdate(node, conf.getPortArrayList(), true);
1875             }
1876         }
1877     }
1878
1879     private void registerWithOSGIConsole() {
1880         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
1881                 .getBundleContext();
1882         bundleContext.registerService(CommandProvider.class.getName(), this,
1883                 null);
1884     }
1885
1886     @Override
1887     public Boolean isNodeConnectorEnabled(NodeConnector nodeConnector) {
1888         if (nodeConnector == null) {
1889             return false;
1890         }
1891
1892         Config config = (Config) getNodeConnectorProp(nodeConnector,
1893                 Config.ConfigPropName);
1894         State state = (State) getNodeConnectorProp(nodeConnector,
1895                 State.StatePropName);
1896         return ((config != null) && (config.getValue() == Config.ADMIN_UP)
1897                 && (state != null) && (state.getValue() == State.EDGE_UP));
1898     }
1899
1900     @Override
1901     public boolean doesNodeConnectorExist(NodeConnector nc) {
1902         return (nc != null && nodeConnectorProps != null
1903                 && nodeConnectorProps.containsKey(nc));
1904     }
1905
1906     @Override
1907     public String getHelp() {
1908         StringBuffer help = new StringBuffer();
1909         help.append("---Switch Manager---\n");
1910         help.append("\t pencs <node id>        - Print enabled node connectors for a given node\n");
1911         help.append("\t pdm <node id>          - Print switch ports in device map\n");
1912         return help.toString();
1913     }
1914
1915     public void _pencs(CommandInterpreter ci) {
1916         String st = ci.nextArgument();
1917         if (st == null) {
1918             ci.println("Please enter node id");
1919             return;
1920         }
1921
1922         Node node = Node.fromString(st);
1923         if (node == null) {
1924             ci.println("Please enter node id");
1925             return;
1926         }
1927
1928         Set<NodeConnector> nodeConnectorSet = getUpNodeConnectors(node);
1929         if (nodeConnectorSet == null) {
1930             return;
1931         }
1932         for (NodeConnector nodeConnector : nodeConnectorSet) {
1933             if (nodeConnector == null) {
1934                 continue;
1935             }
1936             ci.println(nodeConnector);
1937         }
1938         ci.println("Total number of NodeConnectors: " + nodeConnectorSet.size());
1939     }
1940
1941     public void _pdm(CommandInterpreter ci) {
1942         String st = ci.nextArgument();
1943         if (st == null) {
1944             ci.println("Please enter node id");
1945             return;
1946         }
1947
1948         Node node = Node.fromString(st);
1949         if (node == null) {
1950             ci.println("Please enter node id");
1951             return;
1952         }
1953
1954         Switch sw = getSwitchByNode(node);
1955
1956         ci.println("          NodeConnector                        Name");
1957
1958         Set<NodeConnector> nodeConnectorSet = sw.getNodeConnectors();
1959         String nodeConnectorName;
1960         if (nodeConnectorSet != null && nodeConnectorSet.size() > 0) {
1961             for (NodeConnector nodeConnector : nodeConnectorSet) {
1962                 Map<String, Property> propMap = getNodeConnectorProps(nodeConnector);
1963                 nodeConnectorName = (propMap == null) ? null : ((Name) propMap
1964                         .get(Name.NamePropName)).getValue();
1965                 if (nodeConnectorName != null) {
1966                     Node nd = nodeConnector.getNode();
1967                     if (!nd.equals(node)) {
1968                         log.debug("node not match {} {}", nd, node);
1969                     }
1970                     Map<String, NodeConnector> map = nodeConnectorNames
1971                             .get(node);
1972                     if (map != null) {
1973                         NodeConnector nc = map.get(nodeConnectorName);
1974                         if (nc == null) {
1975                             log.debug("no nodeConnector named {}",
1976                                     nodeConnectorName);
1977                         } else if (!nc.equals(nodeConnector)) {
1978                             log.debug("nodeConnector not match {} {}", nc,
1979                                     nodeConnector);
1980                         }
1981                     }
1982                 }
1983
1984                 ci.println(nodeConnector
1985                         + "            "
1986                         + ((nodeConnectorName == null) ? "" : nodeConnectorName)
1987                         + "(" + nodeConnector.getID() + ")");
1988             }
1989             ci.println("Total number of NodeConnectors: "
1990                     + nodeConnectorSet.size());
1991         }
1992     }
1993
1994     @Override
1995     public byte[] getNodeMAC(Node node) {
1996         MacAddress mac = (MacAddress) this.getNodeProp(node,
1997                 MacAddress.name);
1998         return (mac != null) ? mac.getMacAddress() : null;
1999     }
2000
2001     @Override
2002     public boolean isSpecial(NodeConnector p) {
2003         if (p.getType().equals(NodeConnectorIDType.CONTROLLER)
2004                 || p.getType().equals(NodeConnectorIDType.ALL)
2005                 || p.getType().equals(NodeConnectorIDType.SWSTACK)
2006                 || p.getType().equals(NodeConnectorIDType.HWPATH)) {
2007             return true;
2008         }
2009         return false;
2010     }
2011
2012     /*
2013      * Add span configuration to local cache and notify clients
2014      */
2015     private void addSpanPorts(Node node, List<NodeConnector> nodeConnectors) {
2016         List<NodeConnector> ncLists = new ArrayList<NodeConnector>();
2017
2018         for (NodeConnector nodeConnector : nodeConnectors) {
2019             if (!spanNodeConnectors.contains(nodeConnector)) {
2020                 ncLists.add(nodeConnector);
2021             }
2022         }
2023
2024         if (ncLists.size() > 0) {
2025             spanNodeConnectors.addAll(ncLists);
2026             notifySpanPortChange(node, ncLists, true);
2027         }
2028     }
2029
2030     private void addSpanPorts(Node node) {
2031         for (SpanConfig conf : getSpanConfigList(node)) {
2032             addSpanPorts(node, conf.getPortArrayList());
2033         }
2034     }
2035
2036     private void addSpanPort(NodeConnector nodeConnector) {
2037         // only add if span is configured on this nodeConnector
2038         for (SpanConfig conf : getSpanConfigList(nodeConnector.getNode())) {
2039             if (conf.getPortArrayList().contains(nodeConnector)) {
2040                 List<NodeConnector> ncLists = new ArrayList<NodeConnector>();
2041                 ncLists.add(nodeConnector);
2042                 addSpanPorts(nodeConnector.getNode(), ncLists);
2043                 return;
2044             }
2045         }
2046     }
2047
2048     /*
2049      * Remove span configuration to local cache and notify clients
2050      */
2051     private void removeSpanPorts(Node node, List<NodeConnector> nodeConnectors) {
2052         List<NodeConnector> ncLists = new ArrayList<NodeConnector>();
2053
2054         for (NodeConnector nodeConnector : nodeConnectors) {
2055             if (spanNodeConnectors.contains(nodeConnector)) {
2056                 ncLists.add(nodeConnector);
2057             }
2058         }
2059
2060         if (ncLists.size() > 0) {
2061             spanNodeConnectors.removeAll(ncLists);
2062             notifySpanPortChange(node, ncLists, false);
2063         }
2064     }
2065
2066     private void removeSpanPorts(Node node) {
2067         for (SpanConfig conf : getSpanConfigList(node)) {
2068             addSpanPorts(node, conf.getPortArrayList());
2069         }
2070     }
2071
2072     private void removeSpanPort(NodeConnector nodeConnector) {
2073         if (spanNodeConnectors.contains(nodeConnector)) {
2074             List<NodeConnector> ncLists = new ArrayList<NodeConnector>();
2075             ncLists.add(nodeConnector);
2076             removeSpanPorts(nodeConnector.getNode(), ncLists);
2077         }
2078     }
2079
2080     private void addNodeProps(Node node, Map<String, Property> propMap) {
2081         if (propMap == null) {
2082             propMap = new HashMap<String, Property>();
2083         }
2084         nodeProps.put(node, propMap);
2085     }
2086
2087     @Override
2088     public Status saveConfiguration() {
2089         return saveSwitchConfig();
2090     }
2091
2092     /**
2093      * Creates a Name/Tier/Bandwidth/MacAddress(controller property) Property
2094      * object based on given property name and value. Other property types are
2095      * not supported yet.
2096      *
2097      * @param propName
2098      *            Name of the Property
2099      * @param propValue
2100      *            Value of the Property
2101      * @return {@link org.opendaylight.controller.sal.core.Property}
2102      */
2103     @Override
2104     public Property createProperty(String propName, String propValue) {
2105         if (propName == null) {
2106             log.debug("propName is null");
2107             return null;
2108         }
2109         if (propValue == null) {
2110             log.debug("propValue is null");
2111             return null;
2112         }
2113
2114         try {
2115             if (propName.equalsIgnoreCase(Description.propertyName)) {
2116                 return new Description(propValue);
2117             } else if (propName.equalsIgnoreCase(Tier.TierPropName)) {
2118                 int tier = Integer.parseInt(propValue);
2119                 return new Tier(tier);
2120             } else if (propName.equalsIgnoreCase(Bandwidth.BandwidthPropName)) {
2121                 long bw = Long.parseLong(propValue);
2122                 return new Bandwidth(bw);
2123             } else if (propName.equalsIgnoreCase(ForwardingMode.name)) {
2124                 int mode = Integer.parseInt(propValue);
2125                 return new ForwardingMode(mode);
2126             } else if (propName.equalsIgnoreCase(MacAddress.name)){
2127                 return new MacAddress(propValue);
2128             }
2129             else {
2130                 log.debug("Not able to create {} property", propName);
2131             }
2132         } catch (Exception e) {
2133             log.debug("createProperty caught exception {}", e.getMessage());
2134         }
2135
2136         return null;
2137     }
2138
2139
2140     @SuppressWarnings("deprecation")
2141     @Override
2142     public String getNodeDescription(Node node) {
2143         // Check first if user configured a name
2144         SwitchConfig config = getSwitchConfig(node.toString());
2145         if (config != null) {
2146             String configuredDesc = config.getNodeDescription();
2147             if (configuredDesc != null && !configuredDesc.isEmpty()) {
2148                 return configuredDesc;
2149             }
2150         }
2151
2152         // No name configured by user, get the node advertised name
2153         Description desc = (Description) getNodeProp(node,
2154                 Description.propertyName);
2155         return (desc == null /* || desc.getValue().equalsIgnoreCase("none") */) ? ""
2156                 : desc.getValue();
2157     }
2158 }