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.Collections;
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;
23 import java.util.List;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
27 import org.apache.commons.lang3.tuple.ImmutablePair;
28 import org.apache.felix.dm.Component;
29 import org.eclipse.osgi.framework.console.CommandInterpreter;
30 import org.eclipse.osgi.framework.console.CommandProvider;
31 import org.opendaylight.controller.clustering.services.CacheConfigException;
32 import org.opendaylight.controller.clustering.services.CacheExistException;
33 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
34 import org.opendaylight.controller.clustering.services.IClusterServices;
35 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
36 import org.opendaylight.controller.sal.core.ConstructionException;
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.NodeConnector.NodeConnectorIDType;
42 import org.opendaylight.controller.sal.core.Property;
43 import org.opendaylight.controller.sal.core.TimeStamp;
44 import org.opendaylight.controller.sal.core.UpdateType;
45 import org.opendaylight.controller.sal.core.Node.NodeIDType;
46 import org.opendaylight.controller.sal.topology.IListenTopoUpdates;
47 import org.opendaylight.controller.sal.topology.ITopologyService;
48 import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
49 import org.opendaylight.controller.sal.utils.StatusCode;
50 import org.opendaylight.controller.sal.utils.GlobalConstants;
51 import org.opendaylight.controller.sal.utils.IObjectReader;
52 import org.opendaylight.controller.sal.utils.ObjectReader;
53 import org.opendaylight.controller.sal.utils.ObjectWriter;
54 import org.opendaylight.controller.sal.utils.Status;
55 import org.opendaylight.controller.topologymanager.ITopologyManager;
56 import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
57 import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig;
58 import org.osgi.framework.BundleContext;
59 import org.osgi.framework.FrameworkUtil;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 * The class describes TopologyManager which is the central repository of the
65 * network topology. It provides service for applications to interact with
66 * topology database and notifies all the listeners of topology changes.
68 public class TopologyManagerImpl implements ITopologyManager,
69 IConfigurationContainerAware, IListenTopoUpdates, IObjectReader,
71 private static final Logger log = LoggerFactory
72 .getLogger(TopologyManagerImpl.class);
73 private ITopologyService topoService = null;
74 private IClusterContainerServices clusterContainerService = null;
75 // DB of all the Edges with properties which constitute our topology
76 private ConcurrentMap<Edge, Set<Property>> edgesDB = null;
77 // DB of all NodeConnector which are part of Edges, meaning they
78 // are connected to another NodeConnector on the other side
79 private ConcurrentMap<NodeConnector, Set<Property>> nodeConnectorsDB = null;
80 // DB of all the NodeConnectors with an Host attached to it
81 private ConcurrentMap<NodeConnector, ImmutablePair<Host, Set<Property>>> hostsDB = null;
82 // Topology Manager Aware listeners
83 private Set<ITopologyManagerAware> topologyManagerAware = Collections
84 .synchronizedSet(new HashSet<ITopologyManagerAware>());
86 private static String ROOT = GlobalConstants.STARTUPHOME.toString();
87 private String userLinksFileName = null;
88 private ConcurrentMap<String, TopologyUserLinkConfig> userLinks;
90 void nonClusterObjectCreate() {
91 edgesDB = new ConcurrentHashMap<Edge, Set<Property>>();
92 hostsDB = new ConcurrentHashMap<NodeConnector, ImmutablePair<Host, Set<Property>>>();
93 userLinks = new ConcurrentHashMap<String, TopologyUserLinkConfig>();
94 nodeConnectorsDB = new ConcurrentHashMap<NodeConnector, Set<Property>>();
97 void setTopologyManagerAware(ITopologyManagerAware s) {
98 if (this.topologyManagerAware != null) {
99 log.debug("Adding ITopologyManagerAware: {}", s);
100 this.topologyManagerAware.add(s);
104 void unsetTopologyManagerAware(ITopologyManagerAware s) {
105 if (this.topologyManagerAware != null) {
106 log.debug("Removing ITopologyManagerAware: {}", s);
107 this.topologyManagerAware.remove(s);
111 void setTopoService(ITopologyService s) {
112 log.debug("Adding ITopologyService: {}", s);
113 this.topoService = s;
116 void unsetTopoService(ITopologyService s) {
117 if (this.topoService == s) {
118 log.debug("Removing ITopologyService: {}", s);
119 this.topoService = null;
123 void setClusterContainerService(IClusterContainerServices s) {
124 log.debug("Cluster Service set");
125 this.clusterContainerService = s;
128 void unsetClusterContainerService(IClusterContainerServices s) {
129 if (this.clusterContainerService == s) {
130 log.debug("Cluster Service removed!");
131 this.clusterContainerService = null;
136 * Function called by the dependency manager when all the required
137 * dependencies are satisfied
140 void init(Component c) {
141 String containerName = null;
142 Dictionary props = c.getServiceProperties();
144 containerName = (String) props.get("containerName");
146 // In the Global instance case the containerName is empty
147 containerName = "UNKNOWN";
150 if (this.clusterContainerService == null) {
151 log.error("Cluster Services is null, not expected!");
155 if (this.topoService == null) {
156 log.error("Topology Services is null, not expected!");
161 this.edgesDB = (ConcurrentMap<Edge, Set<Property>>) this.clusterContainerService
162 .createCache("topologymanager.edgesDB", EnumSet
163 .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
164 } catch (CacheExistException cee) {
165 log.error("topologymanager.edgesDB Cache already exists - "
166 + "destroy and recreate if needed");
167 } catch (CacheConfigException cce) {
168 log.error("topologymanager.edgesDB Cache configuration invalid - "
169 + "check cache mode");
173 this.hostsDB = (ConcurrentMap<NodeConnector, ImmutablePair<Host, Set<Property>>>) this.clusterContainerService
174 .createCache("topologymanager.hostsDB", EnumSet
175 .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
176 } catch (CacheExistException cee) {
177 log.error("topologymanager.hostsDB Cache already exists - "
178 + "destroy and recreate if needed");
179 } catch (CacheConfigException cce) {
180 log.error("topologymanager.hostsDB Cache configuration invalid - "
181 + "check cache mode");
185 this.nodeConnectorsDB = (ConcurrentMap<NodeConnector, Set<Property>>) this.clusterContainerService
186 .createCache("topologymanager.nodeConnectorDB", EnumSet
187 .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
188 } catch (CacheExistException cee) {
189 log.error("topologymanager.nodeConnectorDB Cache already exists"
190 + " - destroy and recreate if needed");
191 } catch (CacheConfigException cce) {
192 log.error("topologymanager.nodeConnectorDB Cache configuration "
193 + "invalid - check cache mode");
196 userLinks = new ConcurrentHashMap<String, TopologyUserLinkConfig>();
198 userLinksFileName = ROOT + "userTopology_" + containerName + ".conf";
199 registerWithOSGIConsole();
204 * Function called after the topology manager has registered the service in
205 * OSGi service registry.
209 // SollicitRefresh MUST be called here else if called at init
210 // time it may sollicit refresh too soon.
211 log.debug("Sollicit topology refresh");
212 topoService.sollicitRefresh();
216 * Function called by the dependency manager when at least one dependency
217 * become unsatisfied or when the component is shutting down because for
218 * example bundle is being stopped.
222 if (this.clusterContainerService == null) {
223 log.error("Cluster Services is null, not expected!");
226 this.nodeConnectorsDB = null;
229 this.clusterContainerService.destroyCache("topologymanager.edgesDB");
231 this.clusterContainerService.destroyCache("topologymanager.hostsDB");
233 this.clusterContainerService
234 .destroyCache("topologymanager.nodeConnectorDB");
235 this.nodeConnectorsDB = null;
236 log.debug("Topology Manager DB Deallocated");
239 @SuppressWarnings("unchecked")
240 private void loadConfiguration() {
241 ObjectReader objReader = new ObjectReader();
242 ConcurrentMap<String, TopologyUserLinkConfig> confList = (ConcurrentMap<String, TopologyUserLinkConfig>) objReader
243 .read(this, userLinksFileName);
245 if (confList == null) {
249 for (TopologyUserLinkConfig conf : confList.values()) {
255 public Status saveConfig() {
256 // Publish the save config event to the cluster nodes
258 * Get the CLUSTERING SERVICES WORKING BEFORE TRYING THIS
260 * configSaveEvent.put(new Date().getTime(), SAVE);
262 return saveConfigInternal();
265 public Status saveConfigInternal() {
267 ObjectWriter objWriter = new ObjectWriter();
270 .write(new ConcurrentHashMap<String, TopologyUserLinkConfig>(
271 userLinks), userLinksFileName);
273 if (retS.isSuccess()) {
276 return new Status(StatusCode.INTERNALERROR, "Save failed");
281 public Map<Node, Set<Edge>> getNodeEdges() {
282 if (this.edgesDB == null) {
286 HashMap<Node, Set<Edge>> res = new HashMap<Node, Set<Edge>>();
287 for (Edge key : this.edgesDB.keySet()) {
288 // Lets analyze the tail
289 Node node = key.getTailNodeConnector().getNode();
290 Set<Edge> nodeEdges = res.get(node);
291 if (nodeEdges == null) {
292 nodeEdges = new HashSet<Edge>();
295 // We need to re-add to the MAP even if the element was
296 // already there so in case of clustered services the map
297 // gets updated in the cluster
298 res.put(node, nodeEdges);
300 // Lets analyze the head
301 node = key.getHeadNodeConnector().getNode();
302 nodeEdges = res.get(node);
303 if (nodeEdges == null) {
304 nodeEdges = new HashSet<Edge>();
307 // We need to re-add to the MAP even if the element was
308 // already there so in case of clustered services the map
309 // gets updated in the cluster
310 res.put(node, nodeEdges);
317 public boolean isInternal(NodeConnector p) {
318 if (this.nodeConnectorsDB == null) {
322 // This is an internal NodeConnector if is connected to
323 // another Node i.e it's part of the nodeConnectorsDB
324 return (this.nodeConnectorsDB.get(p) != null);
328 * The Map returned is a copy of the current topology hence if the topology
329 * changes the copy doesn't
331 * @return A Map representing the current topology expressed as edges of the
335 public Map<Edge, Set<Property>> getEdges() {
336 if (this.edgesDB == null) {
340 HashMap<Edge, Set<Property>> res = new HashMap<Edge, Set<Property>>();
341 for (Edge key : this.edgesDB.keySet()) {
342 // Sets of props are copied because the composition of
343 // those properties could change with time
344 HashSet<Property> prop = new HashSet<Property>(
345 this.edgesDB.get(key));
346 // We can simply reuse the key because the object is
347 // immutable so doesn't really matter that we are
348 // referencing the only owned by a different table, the
349 // meaning is the same because doesn't change with time.
356 // TODO remove with spring-dm removal
359 * the topologyAware to set
361 public void setTopologyAware(Set<Object> set) {
362 for (Object s : set) {
363 setTopologyManagerAware((ITopologyManagerAware) s);
368 public Set<NodeConnector> getNodeConnectorWithHost() {
369 if (this.hostsDB == null) {
373 return (this.hostsDB.keySet());
377 public Map<Node, Set<NodeConnector>> getNodesWithNodeConnectorHost() {
378 if (this.hostsDB == null) {
381 HashMap<Node, Set<NodeConnector>> res = new HashMap<Node, Set<NodeConnector>>();
383 for (NodeConnector p : this.hostsDB.keySet()) {
384 Node n = p.getNode();
385 Set<NodeConnector> pSet = res.get(n);
387 // Create the HashSet if null
388 pSet = new HashSet<NodeConnector>();
392 // Keep updating the HashSet, given this is not a
393 // clustered map we can just update the set without
394 // worrying to update the hashmap.
402 public Host getHostAttachedToNodeConnector(NodeConnector p) {
403 if (this.hostsDB == null) {
407 return (this.hostsDB.get(p).getLeft());
411 public void updateHostLink(NodeConnector p, Host h, UpdateType t,
412 Set<Property> props) {
413 if (this.hostsDB == null) {
420 // Clone the property set in case non null else just
421 // create an empty one. Caches allocated via infinispan
422 // don't allow null values
424 props = new HashSet<Property>();
426 props = new HashSet<Property>(props);
429 this.hostsDB.put(p, new ImmutablePair(h, props));
432 this.hostsDB.remove(p);
437 private TopoEdgeUpdate edgeUpdate(Edge e, UpdateType type,
438 Set<Property> props) {
441 // Make sure the props are non-null
443 props = (Set<Property>) new HashSet();
445 // Copy the set so noone is going to change the content
446 props = (Set<Property>) new HashSet(props);
449 // Now make sure there is the creation timestamp for the
450 // edge, if not there timestamp with the first update
451 boolean found_create = false;
452 for (Property prop : props) {
453 if (prop instanceof TimeStamp) {
454 TimeStamp t = (TimeStamp) prop;
455 if (t.getTimeStampName().equals("creation")) {
462 TimeStamp t = new TimeStamp(System.currentTimeMillis(),
467 // Now add this in the database eventually overriding
468 // something that may have been already existing
469 this.edgesDB.put(e, props);
471 // Now populate the DB of NodeConnectors
472 // NOTE WELL: properties are empy sets, not really needed
474 this.nodeConnectorsDB.put(e.getHeadNodeConnector(),
475 new HashSet<Property>());
476 this.nodeConnectorsDB.put(e.getTailNodeConnector(),
477 new HashSet<Property>());
478 log.trace("Edge {} {}", e.toString(), type.name());
481 // Now remove the edge from edgesDB
482 this.edgesDB.remove(e);
484 // Now lets update the NodeConnectors DB, the assumption
485 // here is that two NodeConnector are exclusively
486 // connected by 1 and only 1 edge, this is reasonable in
487 // the same plug (virtual of phisical) we can assume two
488 // cables won't be plugged. This could break only in case
489 // of devices in the middle that acts as hubs, but it
490 // should be safe to assume that won't happen.
491 this.nodeConnectorsDB.remove(e.getHeadNodeConnector());
492 this.nodeConnectorsDB.remove(e.getTailNodeConnector());
493 log.trace("Edge {} {}", e.toString(), type.name());
496 Set<Property> old_props = this.edgesDB.get(e);
498 // When property changes lets make sure we can change it
499 // all except the creation time stamp because that should
500 // be changed only when the edge is destroyed and created
503 for (Property prop : old_props) {
504 if (prop instanceof TimeStamp) {
505 TimeStamp t = (TimeStamp) prop;
506 if (t.getTimeStampName().equals("creation")) {
512 // Now lets make sure new properties are non-null
513 // Make sure the props are non-null
515 props = (Set<Property>) new HashSet();
517 // Copy the set so noone is going to change the content
518 props = (Set<Property>) new HashSet(props);
521 // Now lets remove the creation property if exist in the
523 for (Iterator<Property> i = props.iterator(); i.hasNext();) {
524 Property prop = i.next();
525 if (prop instanceof TimeStamp) {
526 TimeStamp t = (TimeStamp) prop;
527 if (t.getTimeStampName().equals("creation")) {
533 // Now lets add the creation timestamp in it
539 this.edgesDB.put(e, props);
540 log.trace("Edge {} {}", e.toString(), type.name());
543 return new TopoEdgeUpdate(e, props, type);
547 public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
548 List<TopoEdgeUpdate> teuList = new ArrayList<TopoEdgeUpdate>();
549 for (int i = 0; i < topoedgeupdateList.size(); i++) {
550 Edge e = topoedgeupdateList.get(i).getEdge();
551 Set<Property> p = topoedgeupdateList.get(i).getProperty();
552 UpdateType type = topoedgeupdateList.get(i).getUpdateType();
553 TopoEdgeUpdate teu = edgeUpdate(e, type, p);
557 // Now update the listeners
558 for (ITopologyManagerAware s : this.topologyManagerAware) {
560 s.edgeUpdate(teuList);
561 } catch (Exception exc) {
562 log.error("Exception on callback", exc);
568 private Edge getReverseLinkTuple(TopologyUserLinkConfig link) {
569 TopologyUserLinkConfig rLink = new TopologyUserLinkConfig(
570 link.getName(), link.getDstNodeIDType(), link.getDstSwitchId(),
571 link.getDstNodeConnectorIDType(), link.getDstPort(),
572 link.getSrcNodeIDType(), link.getSrcSwitchId(),
573 link.getSrcNodeConnectorIDType(), link.getSrcPort());
574 return getLinkTuple(rLink);
577 private Edge getLinkTuple(TopologyUserLinkConfig link) {
578 Edge linkTuple = null;
580 // if atleast 1 link exists for the srcPort and atleast 1 link exists
582 // that makes it ineligible for the Manual link addition
583 // This is just an extra protection to avoid mis-programming.
584 boolean srcLinkExists = false;
585 boolean dstLinkExists = false;
586 // TODO check a way to validate the port with inventory services
587 // if (srcSw.getPorts().contains(srcPort) &&
588 // dstSw.getPorts().contains(srcPort) &&
589 if (!srcLinkExists && !dstLinkExists) {
592 NodeConnector sPort = null;
593 NodeConnector dPort = null;
595 String srcNodeIDType = link.getSrcNodeIDType();
596 String srcNodeConnectorIDType = link.getSrcNodeConnectorIDType();
597 String dstNodeIDType = link.getDstNodeIDType();
598 String dstNodeConnectorIDType = link.getDstNodeConnectorIDType();
600 if (srcNodeIDType.equals(NodeIDType.OPENFLOW)) {
601 sNode = new Node(srcNodeIDType, link.getSrcSwitchIDLong());
603 sNode = new Node(srcNodeIDType, link.getSrcSwitchId());
606 if (dstNodeIDType.equals(NodeIDType.OPENFLOW)) {
607 dNode = new Node(dstNodeIDType, link.getDstSwitchIDLong());
609 dNode = new Node(dstNodeIDType, link.getDstSwitchId());
612 if (srcNodeConnectorIDType.equals(NodeConnectorIDType.OPENFLOW)) {
613 Short srcPort = Short.valueOf((short) 0);
614 if (!link.isSrcPortByName()) {
615 srcPort = Short.parseShort(link.getSrcPort());
617 sPort = new NodeConnector(srcNodeConnectorIDType, srcPort,
620 sPort = new NodeConnector(srcNodeConnectorIDType,
621 link.getSrcPort(), sNode);
624 if (dstNodeConnectorIDType.equals(NodeConnectorIDType.OPENFLOW)) {
625 Short dstPort = Short.valueOf((short) 0);
626 if (!link.isDstPortByName()) {
627 dstPort = Short.parseShort(link.getDstPort());
629 dPort = new NodeConnector(dstNodeConnectorIDType, dstPort,
632 dPort = new NodeConnector(dstNodeConnectorIDType,
633 link.getDstPort(), dNode);
635 linkTuple = new Edge(sPort, dPort);
636 } catch (ConstructionException cex) {
637 log.warn("Caught exception ", cex);
642 if (srcLinkExists && dstLinkExists) {
643 link.setStatus(TopologyUserLinkConfig.STATUS.INCORRECT);
649 public ConcurrentMap<String, TopologyUserLinkConfig> getUserLinks() {
654 public Status addUserLink(TopologyUserLinkConfig link) {
655 if (!link.isValid()) {
656 return new Status(StatusCode.BADREQUEST,
657 "Configuration Invalid. Please check the parameters");
659 if (userLinks.get(link.getName()) != null) {
660 return new Status(StatusCode.CONFLICT, "Link with name : "
662 + " already exists. Please use another name");
664 if (userLinks.containsValue(link)) {
665 return new Status(StatusCode.CONFLICT, "Link configuration exists");
668 link.setStatus(TopologyUserLinkConfig.STATUS.LINKDOWN);
669 userLinks.put(link.getName(), link);
671 Edge linkTuple = getLinkTuple(link);
672 if (linkTuple != null) {
674 linkTuple = getReverseLinkTuple(link);
675 link.setStatus(TopologyUserLinkConfig.STATUS.SUCCESS);
676 } catch (Exception e) {
677 return new Status(StatusCode.INTERNALERROR,
678 "Exception while adding custom link : "
682 return new Status(StatusCode.SUCCESS, null);
686 public Status deleteUserLink(String linkName) {
687 if (linkName == null) {
688 return new Status(StatusCode.BADREQUEST,
689 "A valid linkName is required to Delete a link");
692 TopologyUserLinkConfig link = userLinks.get(linkName);
694 Edge linkTuple = getLinkTuple(link);
695 userLinks.remove(linkName);
696 if (linkTuple != null) {
698 // oneTopology.deleteUserConfiguredLink(linkTuple);
699 } catch (Exception e) {
701 "Harmless : Exception while Deleting User Configured link {} {}",
704 linkTuple = getReverseLinkTuple(link);
706 // oneTopology.deleteUserConfiguredLink(linkTuple);
707 } catch (Exception e) {
709 "Harmless : Exception while Deleting User Configured Reverse link {} {}",
713 return new Status(StatusCode.SUCCESS, null);
716 private void registerWithOSGIConsole() {
717 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
719 bundleContext.registerService(CommandProvider.class.getName(), this,
724 public String getHelp() {
725 StringBuffer help = new StringBuffer();
726 help.append("---Topology Manager---\n");
727 help.append("\t addTopo name <NodeIDType> <src-sw-id> <NodeConnectorIDType> <port-number> <NodeIDType> <dst-sw-id> <NodeConnectorIDType> <port-number>\n");
728 help.append("\t delTopo name\n");
729 help.append("\t printTopo\n");
730 help.append("\t printNodeEdges\n");
731 return help.toString();
734 public void _printTopo(CommandInterpreter ci) {
735 for (String name : this.userLinks.keySet()) {
736 TopologyUserLinkConfig linkConfig = userLinks.get(name);
737 ci.println("Name : " + name);
738 ci.println(linkConfig);
739 ci.println("Edge " + getLinkTuple(linkConfig));
740 ci.println("Reverse Edge " + getReverseLinkTuple(linkConfig));
744 public void _addTopo(CommandInterpreter ci) {
745 String name = ci.nextArgument();
746 if ((name == null)) {
747 ci.println("Please enter a valid Name");
751 String srcNodeIDType = ci.nextArgument();
752 if (srcNodeIDType == null) {
753 ci.println("Null source node ID Type. Example: OF or PR");
757 String dpid = ci.nextArgument();
759 ci.println("Null source node id");
763 String srcNodeConnectorIDType = ci.nextArgument();
764 if (srcNodeConnectorIDType == null) {
765 ci.println("Null source node connector ID Type. Example: OF or PR");
769 String port = ci.nextArgument();
771 ci.println("Null source port number");
775 String dstNodeIDType = ci.nextArgument();
776 if (dstNodeIDType == null) {
777 ci.println("Null destination node ID Type. Example: OF or PR");
781 String ddpid = ci.nextArgument();
783 ci.println("Null destination node ID");
787 String dstNodeConnectorIDType = ci.nextArgument();
788 if (dstNodeConnectorIDType == null) {
789 ci.println("Null destination node connector ID Type. Example: OF or PR");
793 String dport = ci.nextArgument();
795 ci.println("Null destination port number");
798 TopologyUserLinkConfig config = new TopologyUserLinkConfig(name,
799 srcNodeIDType, dpid, srcNodeConnectorIDType, port,
800 dstNodeIDType, ddpid, dstNodeConnectorIDType, dport);
801 ci.println(this.addUserLink(config));
804 public void _delTopo(CommandInterpreter ci) {
805 String name = ci.nextArgument();
806 if ((name == null)) {
807 ci.println("Please enter a valid Name");
810 this.deleteUserLink(name);
813 public void _printNodeEdges(CommandInterpreter ci) {
814 Map<Node, Set<Edge>> nodeEdges = getNodeEdges();
815 if (nodeEdges == null) {
818 Set<Node> nodeSet = nodeEdges.keySet();
819 if (nodeSet == null) {
822 ci.println(" Node Edge");
823 for (Node node : nodeSet) {
824 Set<Edge> edgeSet = nodeEdges.get(node);
825 if (edgeSet == null) {
828 for (Edge edge : edgeSet) {
829 ci.println(node + " " + edge);
835 public Object readObject(ObjectInputStream ois)
836 throws FileNotFoundException, IOException, ClassNotFoundException {
837 // TODO Auto-generated method stub
838 return ois.readObject();
842 public Status saveConfiguration() {
847 public void edgeOverUtilized(Edge edge) {
848 log.warn("Link Utilization above normal: {}", edge);
852 public void edgeUtilBackToNormal(Edge edge) {
853 log.warn("Link Utilization back to normal: {}", edge);