2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.controller.topologymanager.internal;
11 import java.io.FileNotFoundException;
12 import java.io.IOException;
13 import java.io.ObjectInputStream;
14 import java.util.ArrayList;
15 import java.util.Date;
16 import java.util.Dictionary;
17 import java.util.EnumSet;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.CopyOnWriteArraySet;
28 import org.apache.commons.lang3.tuple.ImmutablePair;
29 import org.apache.felix.dm.Component;
30 import org.eclipse.osgi.framework.console.CommandInterpreter;
31 import org.eclipse.osgi.framework.console.CommandProvider;
32 import org.opendaylight.controller.clustering.services.CacheConfigException;
33 import org.opendaylight.controller.clustering.services.CacheExistException;
34 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
35 import org.opendaylight.controller.clustering.services.IClusterServices;
36 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
37 import org.opendaylight.controller.sal.core.Edge;
38 import org.opendaylight.controller.sal.core.Host;
39 import org.opendaylight.controller.sal.core.Node;
40 import org.opendaylight.controller.sal.core.NodeConnector;
41 import org.opendaylight.controller.sal.core.Property;
42 import org.opendaylight.controller.sal.core.TimeStamp;
43 import org.opendaylight.controller.sal.core.UpdateType;
44 import org.opendaylight.controller.sal.topology.IListenTopoUpdates;
45 import org.opendaylight.controller.sal.topology.ITopologyService;
46 import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
47 import org.opendaylight.controller.sal.utils.GlobalConstants;
48 import org.opendaylight.controller.sal.utils.IObjectReader;
49 import org.opendaylight.controller.sal.utils.ObjectReader;
50 import org.opendaylight.controller.sal.utils.ObjectWriter;
51 import org.opendaylight.controller.sal.utils.Status;
52 import org.opendaylight.controller.sal.utils.StatusCode;
53 import org.opendaylight.controller.topologymanager.ITopologyManager;
54 import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
55 import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig;
56 import org.osgi.framework.BundleContext;
57 import org.osgi.framework.FrameworkUtil;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
62 * The class describes TopologyManager which is the central repository of the
63 * network topology. It provides service for applications to interact with
64 * topology database and notifies all the listeners of topology changes.
66 public class TopologyManagerImpl implements ITopologyManager,
67 IConfigurationContainerAware, IListenTopoUpdates, IObjectReader,
69 private static final Logger log = LoggerFactory.getLogger(TopologyManagerImpl.class);
70 private static final String SAVE = "Save";
71 private ITopologyService topoService;
72 private IClusterContainerServices clusterContainerService;
73 // DB of all the Edges with properties which constitute our topology
74 private ConcurrentMap<Edge, Set<Property>> edgesDB;
75 // DB of all NodeConnector which are part of ISL Edges, meaning they
76 // are connected to another NodeConnector on the other side of an ISL link.
77 // NodeConnector of a Production Edge is not part of this DB.
78 private ConcurrentMap<NodeConnector, Set<Property>> nodeConnectorsDB;
79 // DB of all the NodeConnectors with an Host attached to it
80 private ConcurrentMap<NodeConnector, ImmutablePair<Host, Set<Property>>> hostsDB;
81 // Topology Manager Aware listeners
82 private Set<ITopologyManagerAware> topologyManagerAware =
83 new CopyOnWriteArraySet<ITopologyManagerAware>();;
85 private static String ROOT = GlobalConstants.STARTUPHOME.toString();
86 private String userLinksFileName;
87 private ConcurrentMap<String, TopologyUserLinkConfig> userLinksDB;
88 private ConcurrentMap<Long, String> configSaveEvent;
91 void nonClusterObjectCreate() {
92 edgesDB = new ConcurrentHashMap<Edge, Set<Property>>();
93 hostsDB = new ConcurrentHashMap<NodeConnector, ImmutablePair<Host, Set<Property>>>();
94 nodeConnectorsDB = new ConcurrentHashMap<NodeConnector, Set<Property>>();
95 userLinksDB = new ConcurrentHashMap<String, TopologyUserLinkConfig>();
96 configSaveEvent = new ConcurrentHashMap<Long, String>();
99 void setTopologyManagerAware(ITopologyManagerAware s) {
100 if (this.topologyManagerAware != null) {
101 log.debug("Adding ITopologyManagerAware: {}", s);
102 this.topologyManagerAware.add(s);
106 void unsetTopologyManagerAware(ITopologyManagerAware s) {
107 if (this.topologyManagerAware != null) {
108 log.debug("Removing ITopologyManagerAware: {}", s);
109 this.topologyManagerAware.remove(s);
113 void setTopoService(ITopologyService s) {
114 log.debug("Adding ITopologyService: {}", s);
115 this.topoService = s;
118 void unsetTopoService(ITopologyService s) {
119 if (this.topoService == s) {
120 log.debug("Removing ITopologyService: {}", s);
121 this.topoService = null;
125 void setClusterContainerService(IClusterContainerServices s) {
126 log.debug("Cluster Service set");
127 this.clusterContainerService = s;
130 void unsetClusterContainerService(IClusterContainerServices s) {
131 if (this.clusterContainerService == s) {
132 log.debug("Cluster Service removed!");
133 this.clusterContainerService = null;
138 * Function called by the dependency manager when all the required
139 * dependencies are satisfied
142 void init(Component c) {
147 String containerName = null;
148 Dictionary<?, ?> props = c.getServiceProperties();
150 containerName = (String) props.get("containerName");
152 // In the Global instance case the containerName is empty
153 containerName = "UNKNOWN";
156 userLinksFileName = ROOT + "userTopology_" + containerName + ".conf";
157 registerWithOSGIConsole();
161 @SuppressWarnings({ "unchecked", "deprecation" })
162 private void allocateCaches(){
163 if (this.clusterContainerService == null) {
164 nonClusterObjectCreate();
165 log.error("Cluster Services unavailable, allocated non-cluster caches!");
170 this.edgesDB = (ConcurrentMap<Edge, Set<Property>>) this.clusterContainerService.createCache(
171 "topologymanager.edgesDB", EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
172 } catch (CacheExistException cee) {
173 log.debug("topologymanager.edgesDB Cache already exists - destroy and recreate if needed");
174 } catch (CacheConfigException cce) {
175 log.error("topologymanager.edgesDB Cache configuration invalid - check cache mode");
179 this.hostsDB = (ConcurrentMap<NodeConnector, ImmutablePair<Host, Set<Property>>>) this.clusterContainerService
180 .createCache("topologymanager.hostsDB", EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
181 } catch (CacheExistException cee) {
182 log.debug("topologymanager.hostsDB Cache already exists - destroy and recreate if needed");
183 } catch (CacheConfigException cce) {
184 log.error("topologymanager.hostsDB Cache configuration invalid - check cache mode");
188 this.nodeConnectorsDB = (ConcurrentMap<NodeConnector, Set<Property>>) this.clusterContainerService
189 .createCache("topologymanager.nodeConnectorDB", EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
190 } catch (CacheExistException cee) {
191 log.debug("topologymanager.nodeConnectorDB Cache already exists - destroy and recreate if needed");
192 } catch (CacheConfigException cce) {
193 log.error("topologymanager.nodeConnectorDB Cache configuration invalid - check cache mode");
197 this.userLinksDB = (ConcurrentMap<String, TopologyUserLinkConfig>) this.clusterContainerService
198 .createCache("topologymanager.userLinksDB", EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
199 } catch (CacheExistException cee) {
200 log.debug("topologymanager.userLinksDB Cache already exists - destroy and recreate if needed");
201 } catch (CacheConfigException cce) {
202 log.error("topologymanager.userLinksDB Cache configuration invalid - check cache mode");
206 this.configSaveEvent = (ConcurrentMap<Long, String>) this.clusterContainerService
207 .createCache("topologymanager.configSaveEvent", EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
208 } catch (CacheExistException cee) {
209 log.debug("topologymanager.configSaveEvent Cache already exists - destroy and recreate if needed");
210 } catch (CacheConfigException cce) {
211 log.error("topologymanager.configSaveEvent Cache configuration invalid - check cache mode");
216 @SuppressWarnings({ "unchecked", "deprecation" })
217 private void retrieveCaches() {
218 if (this.clusterContainerService == null) {
219 log.error("Cluster Services is null, can't retrieve caches.");
223 this.edgesDB = (ConcurrentMap<Edge, Set<Property>>) this.clusterContainerService
224 .getCache("topologymanager.edgesDB");
225 if (edgesDB == null) {
226 log.error("Failed to get cache for topologymanager.edgesDB");
229 this.hostsDB = (ConcurrentMap<NodeConnector, ImmutablePair<Host, Set<Property>>>) this.clusterContainerService
230 .getCache("topologymanager.hostsDB");
231 if (hostsDB == null) {
232 log.error("Failed to get cache for topologymanager.hostsDB");
235 this.nodeConnectorsDB = (ConcurrentMap<NodeConnector, Set<Property>>) this.clusterContainerService
236 .getCache("topologymanager.nodeConnectorDB");
237 if (nodeConnectorsDB == null) {
238 log.error("Failed to get cache for topologymanager.nodeConnectorDB");
241 this.userLinksDB = (ConcurrentMap<String, TopologyUserLinkConfig>) this.clusterContainerService
242 .getCache("topologymanager.userLinksDB");
243 if (userLinksDB == null) {
244 log.error("Failed to get cache for topologymanager.userLinksDB");
247 this.configSaveEvent = (ConcurrentMap<Long, String>) this.clusterContainerService
248 .getCache("topologymanager.configSaveEvent");
249 if (configSaveEvent == null) {
250 log.error("Failed to get cache for topologymanager.configSaveEvent");
256 * Function called after the topology manager has registered the service in
257 * OSGi service registry.
261 // SollicitRefresh MUST be called here else if called at init
262 // time it may sollicit refresh too soon.
263 log.debug("Sollicit topology refresh");
264 topoService.sollicitRefresh();
268 * Function called by the dependency manager when at least one dependency
269 * become unsatisfied or when the component is shutting down because for
270 * example bundle is being stopped.
276 @SuppressWarnings("unchecked")
277 private void loadConfiguration() {
278 ObjectReader objReader = new ObjectReader();
279 ConcurrentMap<String, TopologyUserLinkConfig> confList =
280 (ConcurrentMap<String, TopologyUserLinkConfig>) objReader.read(this, userLinksFileName);
282 if (confList != null) {
283 for (TopologyUserLinkConfig conf : confList.values()) {
290 public Status saveConfig() {
291 // Publish the save config event to the cluster
292 configSaveEvent.put(new Date().getTime(), SAVE );
293 return saveConfigInternal();
296 public Status saveConfigInternal() {
297 ObjectWriter objWriter = new ObjectWriter();
299 Status saveStatus = objWriter.write(
300 new ConcurrentHashMap<String, TopologyUserLinkConfig>(userLinksDB), userLinksFileName);
302 if (! saveStatus.isSuccess()) {
303 return new Status(StatusCode.INTERNALERROR, "Topology save failed: " + saveStatus.getDescription());
309 public Map<Node, Set<Edge>> getNodeEdges() {
310 if (this.edgesDB == null) {
314 Map<Node, Set<Edge>> res = new HashMap<Node, Set<Edge>>();
315 for (Edge edge : this.edgesDB.keySet()) {
316 // Lets analyze the tail
317 Node node = edge.getTailNodeConnector().getNode();
318 Set<Edge> nodeEdges = res.get(node);
319 if (nodeEdges == null) {
320 nodeEdges = new HashSet<Edge>();
321 res.put(node, nodeEdges);
325 // Lets analyze the head
326 node = edge.getHeadNodeConnector().getNode();
327 nodeEdges = res.get(node);
328 if (nodeEdges == null) {
329 nodeEdges = new HashSet<Edge>();
330 res.put(node, nodeEdges);
339 public boolean isInternal(NodeConnector p) {
340 if (this.nodeConnectorsDB == null) {
344 // This is an internal NodeConnector if is connected to
345 // another Node i.e it's part of the nodeConnectorsDB
346 return (this.nodeConnectorsDB.get(p) != null);
350 * This method returns true if the edge is an ISL link.
354 * @return true if it is an ISL link
356 public boolean isISLink(Edge e) {
357 return (!isProductionLink(e));
361 * This method returns true if the edge is a production link.
365 * @return true if it is a production link
367 public boolean isProductionLink(Edge e) {
368 return (e.getHeadNodeConnector().getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)
369 || e.getTailNodeConnector().getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION));
373 * The Map returned is a copy of the current topology hence if the topology
374 * changes the copy doesn't
376 * @return A Map representing the current topology expressed as edges of the
380 public Map<Edge, Set<Property>> getEdges() {
381 if (this.edgesDB == null) {
385 Map<Edge, Set<Property>> edgeMap = new HashMap<Edge, Set<Property>>();
387 for (Map.Entry<Edge, Set<Property>> edgeEntry : edgesDB.entrySet()) {
388 // Sets of props are copied because the composition of
389 // those properties could change with time
390 props = new HashSet<Property>(edgeEntry.getValue());
391 // We can simply reuse the key because the object is
392 // immutable so doesn't really matter that we are
393 // referencing the only owned by a different table, the
394 // meaning is the same because doesn't change with time.
395 edgeMap.put(edgeEntry.getKey(), props);
402 public Set<NodeConnector> getNodeConnectorWithHost() {
403 if (this.hostsDB == null) {
407 return (new HashSet<NodeConnector>(this.hostsDB.keySet()));
411 public Map<Node, Set<NodeConnector>> getNodesWithNodeConnectorHost() {
412 if (this.hostsDB == null) {
415 HashMap<Node, Set<NodeConnector>> res = new HashMap<Node, Set<NodeConnector>>();
417 Set<NodeConnector> portSet;
418 for (NodeConnector nc : this.hostsDB.keySet()) {
420 portSet = res.get(node);
421 if (portSet == null) {
422 // Create the HashSet if null
423 portSet = new HashSet<NodeConnector>();
424 res.put(node, portSet);
427 // Keep updating the HashSet, given this is not a
428 // clustered map we can just update the set without
429 // worrying to update the hashmap.
437 public Host getHostAttachedToNodeConnector(NodeConnector port) {
438 ImmutablePair<Host, Set<Property>> host;
439 if (this.hostsDB == null || (host = this.hostsDB.get(port)) == null) {
442 return host.getLeft();
446 public void updateHostLink(NodeConnector port, Host h, UpdateType t, Set<Property> props) {
448 // Clone the property set in case non null else just
449 // create an empty one. Caches allocated via infinispan
450 // don't allow null values
452 props = new HashSet<Property>();
454 props = new HashSet<Property>(props);
456 ImmutablePair<Host, Set<Property>> thisHost = new ImmutablePair<Host, Set<Property>>(h, props);
461 this.hostsDB.put(port, thisHost);
464 //remove only if hasn't been concurrently modified
465 this.hostsDB.remove(port, thisHost);
470 private TopoEdgeUpdate edgeUpdate(Edge e, UpdateType type, Set<Property> props) {
473 // Make sure the props are non-null
475 props = new HashSet<Property>();
477 props = new HashSet<Property>(props);
480 //in case of node switch-over to a different cluster controller,
481 //let's retain edge props
482 Set<Property> currentProps = this.edgesDB.get(e);
483 if (currentProps != null){
484 props.addAll(currentProps);
487 // Now make sure there is the creation timestamp for the
488 // edge, if not there, stamp with the first update
489 boolean found_create = false;
490 for (Property prop : props) {
491 if (prop instanceof TimeStamp) {
492 TimeStamp t = (TimeStamp) prop;
493 if (t.getTimeStampName().equals("creation")) {
501 TimeStamp t = new TimeStamp(System.currentTimeMillis(), "creation");
505 // Now add this in the database eventually overriding
506 // something that may have been already existing
507 this.edgesDB.put(e, props);
509 // Now populate the DB of NodeConnectors
510 // NOTE WELL: properties are empty sets, not really needed
512 // The DB only contains ISL ports
514 this.nodeConnectorsDB.put(e.getHeadNodeConnector(), new HashSet<Property>(1));
515 this.nodeConnectorsDB.put(e.getTailNodeConnector(), new HashSet<Property>(1));
517 log.trace("Edge {} {}", e.toString(), type.name());
520 // Now remove the edge from edgesDB
521 this.edgesDB.remove(e);
523 // Now lets update the NodeConnectors DB, the assumption
524 // here is that two NodeConnector are exclusively
525 // connected by 1 and only 1 edge, this is reasonable in
526 // the same plug (virtual of phisical) we can assume two
527 // cables won't be plugged. This could break only in case
528 // of devices in the middle that acts as hubs, but it
529 // should be safe to assume that won't happen.
530 this.nodeConnectorsDB.remove(e.getHeadNodeConnector());
531 this.nodeConnectorsDB.remove(e.getTailNodeConnector());
532 log.trace("Edge {} {}", e.toString(), type.name());
535 Set<Property> oldProps = this.edgesDB.get(e);
537 // When property changes lets make sure we can change it
538 // all except the creation time stamp because that should
539 // be changed only when the edge is destroyed and created
541 TimeStamp timeStamp = null;
542 for (Property prop : oldProps) {
543 if (prop instanceof TimeStamp) {
544 TimeStamp tsProp = (TimeStamp) prop;
545 if (tsProp.getTimeStampName().equals("creation")) {
552 // Now lets make sure new properties are non-null
554 props = new HashSet<Property>();
556 // Copy the set so noone is going to change the content
557 props = new HashSet<Property>(props);
560 // Now lets remove the creation property if exist in the
562 for (Iterator<Property> i = props.iterator(); i.hasNext();) {
563 Property prop = i.next();
564 if (prop instanceof TimeStamp) {
565 TimeStamp t = (TimeStamp) prop;
566 if (t.getTimeStampName().equals("creation")) {
573 // Now lets add the creation timestamp in it
574 if (timeStamp != null) {
575 props.add(timeStamp);
579 this.edgesDB.put(e, props);
580 log.trace("Edge {} {}", e.toString(), type.name());
583 return new TopoEdgeUpdate(e, props, type);
587 public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
588 List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
589 for (int i = 0; i < topoedgeupdateList.size(); i++) {
590 Edge e = topoedgeupdateList.get(i).getEdge();
591 Set<Property> p = topoedgeupdateList.get(i).getProperty();
592 UpdateType type = topoedgeupdateList.get(i).getUpdateType();
593 TopoEdgeUpdate teu = edgeUpdate(e, type, p);
597 // Now update the listeners
598 for (ITopologyManagerAware s : this.topologyManagerAware) {
600 s.edgeUpdate(teuList);
601 } catch (Exception exc) {
602 log.error("Exception on edge update:", exc);
608 private Edge getReverseLinkTuple(TopologyUserLinkConfig link) {
609 TopologyUserLinkConfig rLink = new TopologyUserLinkConfig(
610 link.getName(), link.getDstNodeConnector(), link.getSrcNodeConnector());
611 return getLinkTuple(rLink);
615 private Edge getLinkTuple(TopologyUserLinkConfig link) {
616 NodeConnector srcNodeConnector = NodeConnector.fromString(link.getSrcNodeConnector());
617 NodeConnector dstNodeConnector = NodeConnector.fromString(link.getDstNodeConnector());
619 return new Edge(srcNodeConnector, dstNodeConnector);
620 } catch (Exception e) {
626 public ConcurrentMap<String, TopologyUserLinkConfig> getUserLinks() {
627 return new ConcurrentHashMap<String, TopologyUserLinkConfig>(userLinksDB);
631 public Status addUserLink(TopologyUserLinkConfig userLink) {
632 if (!userLink.isValid()) {
633 return new Status(StatusCode.BADREQUEST,
634 "User link configuration invalid.");
636 userLink.setStatus(TopologyUserLinkConfig.STATUS.LINKDOWN);
638 //Check if this link already configured
639 //NOTE: infinispan cache doesn't support Map.containsValue()
640 // (which is linear time in most ConcurrentMap impl anyway)
641 for (TopologyUserLinkConfig existingLink : userLinksDB.values()) {
642 if (existingLink.equals(userLink)) {
643 return new Status(StatusCode.CONFLICT, "Link configuration exists");
646 //attempt put, if mapping for this key already existed return conflict
647 if (userLinksDB.putIfAbsent(userLink.getName(), userLink) != null) {
648 return new Status(StatusCode.CONFLICT, "Link with name : " + userLink.getName()
649 + " already exists. Please use another name");
652 Edge linkTuple = getLinkTuple(userLink);
653 if (linkTuple != null) {
654 if (!isProductionLink(linkTuple)) {
655 edgeUpdate(linkTuple, UpdateType.ADDED, new HashSet<Property>());
658 linkTuple = getReverseLinkTuple(userLink);
659 if (linkTuple != null) {
660 userLink.setStatus(TopologyUserLinkConfig.STATUS.SUCCESS);
661 if (!isProductionLink(linkTuple)) {
662 edgeUpdate(linkTuple, UpdateType.ADDED, new HashSet<Property>());
666 return new Status(StatusCode.SUCCESS);
670 public Status deleteUserLink(String linkName) {
671 if (linkName == null) {
672 return new Status(StatusCode.BADREQUEST, "User link name cannot be null.");
675 TopologyUserLinkConfig link = userLinksDB.remove(linkName);
677 if (link != null && (linkTuple = getLinkTuple(link)) != null) {
678 if (! isProductionLink(linkTuple)) {
679 edgeUpdate(linkTuple, UpdateType.REMOVED, null);
682 linkTuple = getReverseLinkTuple(link);
683 if (! isProductionLink(linkTuple)) {
684 edgeUpdate(linkTuple, UpdateType.REMOVED, null);
687 return new Status(StatusCode.SUCCESS);
690 private void registerWithOSGIConsole() {
691 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
693 bundleContext.registerService(CommandProvider.class.getName(), this,
698 public String getHelp() {
699 StringBuffer help = new StringBuffer();
700 help.append("---Topology Manager---\n");
701 help.append("\t addUserLink <name> <node connector string> <node connector string>\n");
702 help.append("\t deleteUserLink <name>\n");
703 help.append("\t printUserLink\n");
704 help.append("\t printNodeEdges\n");
705 return help.toString();
708 public void _printUserLink(CommandInterpreter ci) {
709 for (String name : this.userLinksDB.keySet()) {
710 TopologyUserLinkConfig linkConfig = userLinksDB.get(name);
711 ci.println("Name : " + name);
712 ci.println(linkConfig);
713 ci.println("Edge " + getLinkTuple(linkConfig));
714 ci.println("Reverse Edge " + getReverseLinkTuple(linkConfig));
718 public void _addUserLink(CommandInterpreter ci) {
719 String name = ci.nextArgument();
720 if ((name == null)) {
721 ci.println("Please enter a valid Name");
725 String ncStr1 = ci.nextArgument();
726 if (ncStr1 == null) {
727 ci.println("Please enter two node connector strings");
730 String ncStr2 = ci.nextArgument();
731 if (ncStr2 == null) {
732 ci.println("Please enter second node connector string");
736 NodeConnector nc1 = NodeConnector.fromString(ncStr1);
738 ci.println("Invalid input node connector 1 string: " + ncStr1);
741 NodeConnector nc2 = NodeConnector.fromString(ncStr2);
743 ci.println("Invalid input node connector 2 string: " + ncStr2);
747 TopologyUserLinkConfig config = new TopologyUserLinkConfig(name, ncStr1, ncStr2);
748 ci.println(this.addUserLink(config));
751 public void _deleteUserLink(CommandInterpreter ci) {
752 String name = ci.nextArgument();
753 if ((name == null)) {
754 ci.println("Please enter a valid Name");
757 this.deleteUserLink(name);
760 public void _printNodeEdges(CommandInterpreter ci) {
761 Map<Node, Set<Edge>> nodeEdges = getNodeEdges();
762 if (nodeEdges == null) {
765 Set<Node> nodeSet = nodeEdges.keySet();
766 if (nodeSet == null) {
769 ci.println(" Node Edge");
770 for (Node node : nodeSet) {
771 Set<Edge> edgeSet = nodeEdges.get(node);
772 if (edgeSet == null) {
775 for (Edge edge : edgeSet) {
776 ci.println(node + " " + edge);
782 public Object readObject(ObjectInputStream ois)
783 throws FileNotFoundException, IOException, ClassNotFoundException {
784 return ois.readObject();
788 public Status saveConfiguration() {
793 public void edgeOverUtilized(Edge edge) {
794 log.warn("Link Utilization above normal: {}", edge);
798 public void edgeUtilBackToNormal(Edge edge) {
799 log.warn("Link Utilization back to normal: {}", edge);