Merge "Logging improvements to improve efficiency - specifically in critical path...
[controller.git] / opendaylight / samples / simpleforwarding / src / main / java / org / opendaylight / controller / samples / simpleforwarding / internal / SimpleForwardingImpl.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.controller.samples.simpleforwarding.internal;
11
12 import java.util.ArrayList;
13 import java.util.EnumSet;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.concurrent.ConcurrentMap;
22
23 import org.opendaylight.controller.clustering.services.CacheConfigException;
24 import org.opendaylight.controller.clustering.services.CacheExistException;
25 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
26 import org.opendaylight.controller.clustering.services.IClusterServices;
27 import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
28 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
29 import org.opendaylight.controller.hosttracker.IfIptoHost;
30 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
31 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
32 import org.opendaylight.controller.sal.action.Action;
33 import org.opendaylight.controller.sal.action.Output;
34 import org.opendaylight.controller.sal.action.PopVlan;
35 import org.opendaylight.controller.sal.action.SetDlDst;
36 import org.opendaylight.controller.sal.action.SetVlanId;
37 import org.opendaylight.controller.sal.core.Edge;
38 import org.opendaylight.controller.sal.core.Node;
39 import org.opendaylight.controller.sal.core.NodeConnector;
40 import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
41 import org.opendaylight.controller.sal.core.Path;
42 import org.opendaylight.controller.sal.core.Property;
43 import org.opendaylight.controller.sal.core.State;
44 import org.opendaylight.controller.sal.core.UpdateType;
45 import org.opendaylight.controller.sal.flowprogrammer.Flow;
46 import org.opendaylight.controller.sal.match.Match;
47 import org.opendaylight.controller.sal.match.MatchType;
48 import org.opendaylight.controller.sal.routing.IListenRoutingUpdates;
49 import org.opendaylight.controller.sal.routing.IRouting;
50 import org.opendaylight.controller.sal.utils.EtherTypes;
51 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
52 import org.opendaylight.controller.sal.utils.Status;
53 import org.opendaylight.controller.switchmanager.IInventoryListener;
54 import org.opendaylight.controller.switchmanager.ISwitchManager;
55 import org.opendaylight.controller.topologymanager.ITopologyManager;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 public class SimpleForwardingImpl implements IfNewHostNotify,
60         IListenRoutingUpdates, IInventoryListener {
61     private static Logger log = LoggerFactory
62             .getLogger(SimpleForwardingImpl.class);
63     private static short DEFAULT_IPSWITCH_PRIORITY = 1;
64     private IfIptoHost hostTracker;
65     private IForwardingRulesManager frm;
66     private ITopologyManager topologyManager;
67     private IRouting routing;
68     private ConcurrentMap<HostNodePair, HashMap<NodeConnector, FlowEntry>> rulesDB;
69     private Map<Node, List<FlowEntry>> tobePrunedPos = new HashMap<Node, List<FlowEntry>>();
70     private IClusterContainerServices clusterContainerService = null;
71     private ISwitchManager switchManager;
72
73     /**
74      * Return codes from the programming of the perHost rules in HW
75      *
76      */
77     public enum RulesProgrammingReturnCode {
78         SUCCESS, FAILED_FEW_SWITCHES, FAILED_ALL_SWITCHES, FAILED_WRONG_PARAMS
79     }
80
81     public void setRouting(IRouting routing) {
82         this.routing = routing;
83     }
84
85     public void unsetRouting(IRouting routing) {
86         if (this.routing == routing) {
87             this.routing = null;
88         }
89     }
90
91     public ITopologyManager getTopologyManager() {
92         return topologyManager;
93     }
94
95     public void setTopologyManager(ITopologyManager topologyManager) {
96         log.debug("Setting topologyManager");
97         this.topologyManager = topologyManager;
98     }
99
100     public void unsetTopologyManager(ITopologyManager topologyManager) {
101         if (this.topologyManager == topologyManager) {
102             this.topologyManager = null;
103         }
104     }
105
106     public void setHostTracker(IfIptoHost hostTracker) {
107         log.debug("Setting HostTracker");
108         this.hostTracker = hostTracker;
109     }
110
111     public void setForwardingRulesManager(
112             IForwardingRulesManager forwardingRulesManager) {
113         log.debug("Setting ForwardingRulesManager");
114         this.frm = forwardingRulesManager;
115     }
116
117     public void unsetHostTracker(IfIptoHost hostTracker) {
118         if (this.hostTracker == hostTracker) {
119             this.hostTracker = null;
120         }
121     }
122
123     public void unsetForwardingRulesManager(
124             IForwardingRulesManager forwardingRulesManager) {
125         if (this.frm == forwardingRulesManager) {
126             this.frm = null;
127         }
128     }
129
130     /**
131      * Function called when the bundle gets activated
132      *
133      */
134     public void startUp() {
135         allocateCaches();
136         retrieveCaches();
137     }
138
139     /**
140      * Function called when the bundle gets stopped
141      *
142      */
143     public void shutDown() {
144         log.debug("Destroy all the host Rules given we are shutting down");
145         uninstallPerHostRules();
146         destroyCaches();
147     }
148
149     @SuppressWarnings("deprecation")
150         private void allocateCaches() {
151         if (this.clusterContainerService == null) {
152             log.info("un-initialized clusterContainerService, can't create cache");
153             return;
154         }
155
156         try {
157             clusterContainerService.createCache("forwarding.ipswitch.rules",
158                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
159         } catch (CacheExistException cee) {
160             log.error("\nCache already exists - destroy and recreate if needed");
161         } catch (CacheConfigException cce) {
162             log.error("\nCache configuration invalid - check cache mode");
163         }
164     }
165
166     @SuppressWarnings({ "unchecked", "deprecation" })
167     private void retrieveCaches() {
168         if (this.clusterContainerService == null) {
169             log.info("un-initialized clusterContainerService, can't retrieve cache");
170             return;
171         }
172
173         rulesDB = (ConcurrentMap<HostNodePair, HashMap<NodeConnector, FlowEntry>>) clusterContainerService
174                 .getCache("forwarding.ipswitch.rules");
175         if (rulesDB == null) {
176             log.error("\nFailed to get rulesDB handle");
177         }
178     }
179
180     @SuppressWarnings("deprecation")
181         private void destroyCaches() {
182         if (this.clusterContainerService == null) {
183             log.info("un-initialized clusterContainerService, can't destroy cache");
184             return;
185         }
186
187         clusterContainerService.destroyCache("forwarding.ipswitch.rules");
188     }
189
190     @SuppressWarnings("unused")
191     private void updatePerHostRuleInSW(HostNodeConnector host, Node currNode,
192             Node rootNode, Edge link, HostNodePair key,
193             Set<NodeConnector> passedPorts) {
194
195         // link parameter it's optional
196         if (host == null || key == null || currNode == null || rootNode == null) {
197             return;
198         }
199         Set<NodeConnector> ports = passedPorts;
200         // TODO: Replace this with SAL equivalent when available
201         //if (container == null) {
202         ports = new HashSet<NodeConnector>();
203         ports.add(NodeConnectorCreator.createNodeConnector(
204                 NodeConnectorIDType.ALL, NodeConnector.SPECIALNODECONNECTORID,
205                 currNode));
206         //}
207
208         HashMap<NodeConnector, FlowEntry> pos = this.rulesDB.get(key);
209         if (pos == null) {
210             pos = new HashMap<NodeConnector, FlowEntry>();
211         }
212         if (ports == null) {
213             log.debug("Empty port list, nothing to do");
214             return;
215         }
216         for (NodeConnector inPort : ports) {
217             /*
218              * skip the port connected to the target host
219              */
220             if (currNode.equals(rootNode)
221                     && (host.getnodeConnector().equals(inPort))) {
222                 continue;
223             }
224             FlowEntry removed_po = pos.remove(inPort);
225             Match match = new Match();
226             List<Action> actions = new ArrayList<Action>();
227             // IP destination based forwarding
228             //on /32 entries only!
229             match.setField(MatchType.DL_TYPE, EtherTypes.IPv4.shortValue());
230             match.setField(MatchType.NW_DST, host.getNetworkAddress());
231
232             //Action for the policy if to
233             //forward to a port except on the
234             //switch where the host sits,
235             //which is to rewrite also the MAC
236             //and to forward on the Host port
237             NodeConnector outPort = null;
238
239             if (currNode.equals(rootNode)) {
240                 outPort = host.getnodeConnector();
241                 if (inPort.equals(outPort)) {
242                     /*
243                      * skip the host port
244                      */
245                     continue;
246                 }
247                 actions.add(new SetDlDst(host.getDataLayerAddressBytes()));
248
249                 if (!inPort.getType().equals(
250                         NodeConnectorIDType.ALL)) {
251                     /*
252                      * Container mode: at the destination switch, we need to strip out the tag (VLAN)
253                      */
254                     actions.add(new PopVlan());
255                 }
256             } else {
257                 /*
258                  * currNode is NOT the rootNode
259                  */
260                 if (link != null) {
261                     outPort = link.getTailNodeConnector();
262                     if (inPort.equals(outPort)) {
263                         /*
264                          * skip the outgoing port
265                          */
266                         continue;
267                     }
268                     /*
269                      *  If outPort is network link, add VLAN tag
270                      */
271                     if (topologyManager.isInternal(outPort)) {
272                         log.debug("outPort {}/{} is internal uplink port",
273                                 currNode, outPort);
274                     } else {
275                         log.debug("outPort {}/{} is host facing port",
276                                 currNode, outPort);
277                     }
278
279                     if ((!inPort.getType().equals(
280                             NodeConnectorIDType.ALL))
281                             && (topologyManager.isInternal(outPort))) {
282                         Node nextNode = link.getHeadNodeConnector()
283                                 .getNode();
284                         // TODO: Replace this with SAL equivalent
285                         //short tag = container.getTag((Long)nextNode.getNodeID());
286                         short tag = 0;
287                         if (tag != 0) {
288                             log.debug("adding SET_VLAN {} for traffic " +
289                                     "leaving {}/{} toward switch {}",
290                                     new Object[] { tag, currNode, outPort,
291                                     nextNode});
292                             actions.add(new SetVlanId(tag));
293                         } else {
294                             log.debug("No tag assigned to switch {}", nextNode);
295                         }
296                     }
297                 }
298             }
299             if (outPort != null) {
300                 actions.add(new Output(outPort));
301             }
302             if (!inPort.getType().equals(NodeConnectorIDType.ALL)) {
303                 /*
304                  * include input port in the flow match field
305                  */
306                 match.setField(MatchType.IN_PORT, inPort);
307
308                 if (topologyManager.isInternal(inPort)) {
309                     log.debug("inPort {}/{} is internal uplink port", currNode,
310                             inPort);
311                 } else {
312                     log.debug("inPort {}/{} is host facing port", currNode,
313                             inPort);
314                 }
315                 /*
316                  * for incoming network link; if the VLAN tag is defined, include it for incoming flow matching
317                  */
318                 if (topologyManager.isInternal(inPort)) {
319                     // TODO: Replace this with SAL equivalent
320                     //short tag = container.getTag((Long)currNode.getNodeID());
321                     short tag = 0;
322                     if (tag != 0) {
323                         log.debug("adding MATCH VLAN {} for traffic entering" +
324                                 "  {}/{}",
325                                 new Object[] {tag, currNode, inPort});
326                         match.setField(MatchType.DL_VLAN, tag);
327                     } else {
328                         log.debug("No tag assigned to switch {}", currNode);
329                     }
330                 }
331             }
332             // Make sure the priority for IP switch entries is
333             // set to a level just above default drop entries
334             Flow flow = new Flow(match, actions);
335             flow.setIdleTimeout((short) 0);
336             flow.setHardTimeout((short) 0);
337             flow.setPriority(DEFAULT_IPSWITCH_PRIORITY);
338
339             String policyName = host.getNetworkAddress().getHostAddress()
340                     + "/32";
341             String flowName = "["
342                     + (!inPort.getType().equals(NodeConnectorIDType.ALL) ?
343                        (inPort.getID()).toString()
344                        + "," : "")
345                     + host.getNetworkAddress().getHostAddress() + "/32 on N "
346                     + currNode + "]";
347             FlowEntry po = new FlowEntry(policyName, flowName, flow, currNode);
348
349             // Now save the rule in the DB rule,
350             // so on updates from topology we can
351             // selectively
352             pos.put(inPort, po);
353             this.rulesDB.put(key, pos);
354             if (!inPort.getType().equals(NodeConnectorIDType.ALL)) {
355                 log.debug("Adding Match(inPort = {} , DIP = {})" +
356                         " Action(outPort= {}) to node {}",
357                         new Object[] { inPort,
358                         host.getNetworkAddress().getHostAddress(),
359                         outPort, currNode});
360                 if ((removed_po != null)
361                         && (!po.getFlow().getMatch().equals(
362                                 removed_po.getFlow().getMatch()))) {
363                     log.debug("Old Flow match: {}, New Flow match: {}",
364                             removed_po.getFlow().getMatch(), po.getFlow()
365                                     .getMatch());
366                     addTobePrunedPolicy(currNode, removed_po, po);
367                 }
368
369             } else {
370                 log.debug("Adding policyMatch(DIP = {}) Action(outPort= {}) " + 
371                         "to node {}", new Object[] {
372                         host.getNetworkAddress().getHostAddress(), outPort,
373                         currNode});
374             }
375         }
376     }
377
378     /**
379      * Calculate the per-Host rules to be installed in the rulesDB,
380      * and that will later on be installed in HW, this routine will
381      * implicitly calculate the shortest path tree among the switch
382      * to which the host is attached and all the other switches in the
383      * network and will automatically create all the rules that allow
384      * a /32 destination IP based forwarding, as in traditional IP
385      * networks.
386      *
387      * @param host Host for which we are going to prepare the rules in the rulesDB
388      *
389      * @return A set of switches touched by the calculation
390      */
391     private Set<Node> preparePerHostRules(HostNodeConnector host) {
392         if (host == null) {
393             return null;
394         }
395         if (this.routing == null) {
396             return null;
397         }
398         if (this.switchManager == null) {
399             return null;
400         }
401         if (this.rulesDB == null) {
402             return null;
403         }
404
405         Node rootNode = host.getnodeconnectorNode();
406         Set<Node> nodes = this.switchManager.getNodes();
407         Set<Node> switchesToProgram = new HashSet<Node>();
408         HostNodePair key;
409         HashMap<NodeConnector, FlowEntry> pos;
410         FlowEntry po;
411
412         for (Node node : nodes) {
413             if (node.equals(rootNode)) {
414                 // We skip it because for the node with host attached
415                 // we will process in every case even if there are no
416                 // routes
417                 continue;
418             }
419             List<Edge> links;
420             Path res = this.routing.getRoute(node, rootNode);
421             if ((res == null) || ((links = res.getEdges()) == null)) {
422                 // Still the path that connect node to rootNode
423                 // doesn't exists
424                 log.debug("NO Route/Path between SW[{}] --> SW[{}] cleaning " +
425                         "potentially existing entries", node, rootNode);
426                 key = new HostNodePair(host, node);
427                 pos = this.rulesDB.get(key);
428                 if (pos != null) {
429                     for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
430                         po = e.getValue();
431                         if (po != null) {
432                             //Uninstall the policy
433                             this.frm.uninstallFlowEntry(po);
434                         }
435                     }
436                     this.rulesDB.remove(key);
437                 }
438                 continue;
439             }
440
441             log.debug("Route between SW[{}] --> SW[{}]", node, rootNode);
442             Integer curr;
443             Node currNode = node;
444             key = new HostNodePair(host, currNode);
445             Edge link;
446             for (curr = 0; curr < links.size(); curr++) {
447                 link = links.get(curr);
448                 if (link == null) {
449                     log.error("Could not retrieve the Link");
450                     continue;
451                 }
452
453                 log.debug(link.toString());
454
455                 // Index all the switches to be programmed
456                 // switchesToProgram.add(currNode);
457                 Set<NodeConnector> ports = null;
458                 ports = switchManager.getUpNodeConnectors(currNode);
459                 updatePerHostRuleInSW(host, currNode, rootNode, link, key,
460                         ports);
461                 if ((this.rulesDB.get(key)) != null) {
462                     /*
463                      * Calling updatePerHostRuleInSW() doesn't guarantee that rules will be
464                      * added in currNode (e.g, there is only one link from currNode to rootNode
465                      * This check makes sure that there are some rules in the rulesDB for the
466                      * given key prior to adding switch to switchesToProgram
467                      */
468                     switchesToProgram.add(currNode);
469                 }
470                 currNode = link.getHeadNodeConnector().getNode();
471                 key = new HostNodePair(host, currNode);
472             }
473         }
474
475         // This rule will be added no matter if any topology is built
476         // or no, it serve as a way to handle the case of a node with
477         // multiple hosts attached to it but not yet connected to the
478         // rest of the world
479         switchesToProgram.add(rootNode);
480         Set<NodeConnector> ports = switchManager
481                 .getUpNodeConnectors(rootNode);
482         updatePerHostRuleInSW(host, rootNode, rootNode, null, new HostNodePair(
483                 host, rootNode), ports);
484
485         //              log.debug("Getting out at the end!");
486         return switchesToProgram;
487     }
488
489     /**
490      * Calculate the per-Host rules to be installed in the rulesDB
491      * from  a specific switch when a host facing port comes up.
492      * These rules will later on be installed in HW. This routine
493      * will implicitly calculate the shortest path from the switch
494      * where the port has come up to the switch where host is ,
495      * attached and will automatically create all the rules that allow
496      * a /32 destination IP based forwarding, as in traditional IP
497      * networks.
498      *
499      * @param host Host for which we are going to prepare the rules in the rulesDB
500      * @param swId Switch ID where the port has come up
501      *
502      * @return A set of switches touched by the calculation
503      */
504     private Set<Node> preparePerHostPerSwitchRules(HostNodeConnector host,
505             Node node, NodeConnector swport) {
506         if ((host == null) || (node == null)) {
507             return null;
508         }
509         if (this.routing == null) {
510             return null;
511         }
512         if (this.switchManager == null) {
513             return null;
514         }
515         if (this.rulesDB == null) {
516             return null;
517         }
518
519         Node rootNode = host.getnodeconnectorNode();
520         Set<Node> switchesToProgram = new HashSet<Node>();
521         HostNodePair key;
522         Map<NodeConnector, FlowEntry> pos;
523         FlowEntry po;
524         Set<NodeConnector> ports = new HashSet<NodeConnector>();
525         ports.add(swport);
526         List<Edge> links;
527
528         Path res = this.routing.getRoute(node, rootNode);
529         if ((res == null) || ((links = res.getEdges()) == null)) {
530             // Still the path that connect node to rootNode
531             // doesn't exists
532             log.debug("NO Route/Path between SW[{}] --> SW[{}] cleaning " +
533                     "potentially existing entries", node, rootNode);
534             key = new HostNodePair(host, node);
535             pos = this.rulesDB.get(key);
536             if (pos != null) {
537                 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
538                     po = e.getValue();
539                     if (po != null) {
540                         //Uninstall the policy
541                         this.frm.uninstallFlowEntry(po);
542                     }
543                 }
544                 this.rulesDB.remove(key);
545             }
546             return null;
547         }
548
549         log.debug("Route between SW[{}] --> SW[{}]", node, rootNode);
550         Integer curr;
551         Node currNode = node;
552         key = new HostNodePair(host, currNode);
553         Edge link;
554         for (curr = 0; curr < links.size(); curr++) {
555             link = links.get(curr);
556             if (link == null) {
557                 log.error("Could not retrieve the Link");
558                 continue;
559             }
560
561             log.debug("Link [{}/{}] --> [{}/{}]", new Object[] {
562                     currNode, link.getHeadNodeConnector(),
563                     link.getHeadNodeConnector().getNode(),
564                     link.getTailNodeConnector()});
565
566             // Index all the switches to be programmed
567             switchesToProgram.add(currNode);
568             updatePerHostRuleInSW(host, currNode, rootNode, link, key, ports);
569             break; // come out of the loop for port up case, interested only in programming one switch
570         }
571
572         // This rule will be added no matter if any topology is built
573         // or no, it serve as a way to handle the case of a node with
574         // multiple hosts attached to it but not yet connected to the
575         // rest of the world
576         // switchesToProgram.add(rootNode);
577         //updatePerHostRuleInSW(host, rootNode,
578         //                                        rootNode, null,
579         //                                        new HostNodePair(host, rootNode),ports);
580
581         //              log.debug("Getting out at the end!");
582         return switchesToProgram;
583     }
584
585     /**
586      * Routine that fetch the per-Host rules from the rulesDB and
587      * install in HW, the one having the same match rules will be
588      * overwritten silently.
589      *
590      * @param host host for which we want to install in HW the per-Host rules
591      * @param switchesToProgram list of switches to be programmed in
592      * HW, usually are them all, but better to be explicit, that list
593      * may change with time based on new switch addition/removal
594      *
595      * @return a return code that convey the programming status of the HW
596      */
597     private RulesProgrammingReturnCode installPerHostRules(
598             HostNodeConnector host, Set<Node> switchesToProgram) {
599         RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
600         if (host == null || switchesToProgram == null) {
601             return RulesProgrammingReturnCode.FAILED_WRONG_PARAMS;
602         }
603         Map<NodeConnector, FlowEntry> pos;
604         FlowEntry po;
605         // Now program every single switch
606         log.debug("Inside installPerHostRules");
607         for (Node swId : switchesToProgram) {
608             HostNodePair key = new HostNodePair(host, swId);
609             pos = this.rulesDB.get(key);
610             if (pos == null) {
611                 continue;
612             }
613             for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
614                 po = e.getValue();
615                 if (po != null) {
616                     // Populate the Policy field now
617                     Status poStatus = this.frm.installFlowEntry(po);
618                     if (!poStatus.isSuccess()) {
619                         log.error("Failed to install policy: "
620                                 + po.getGroupName() + " (" 
621                                 + poStatus.getDescription() + ")");
622
623                         retCode = RulesProgrammingReturnCode.FAILED_FEW_SWITCHES;
624                         // Remove the entry from the DB, it was not installed!
625                         this.rulesDB.remove(key);
626                     } else {
627                         log.debug("Successfully installed policy "
628                                 + po.toString() + " on switch " + swId);
629                     }
630                 } else {
631                     log.error("Cannot find a policy for SW:({}) Host: ({})",
632                               swId, host);
633                     /* // Now dump every single rule */
634                     /* for (HostNodePair dumpkey : this.rulesDB.keySet()) { */
635                     /*  po = this.rulesDB.get(dumpkey); */
636                     /*  log.debug("Dumping entry H{" + dumpkey.getHost() + "} S{" + dumpkey.getSwitchId() + "} = {" + (po == null ? "null policy" : po)); */
637                     /* } */
638                 }
639             }
640         }
641         log.debug("Leaving installPerHostRules");
642         return retCode;
643     }
644
645     /**
646      * Cleanup all the host rules for a given host
647      *
648      * @param host Host for which the host rules need to be cleaned
649      * up, the host could be null in that case it match all the hosts
650      *
651      * @return a return code that convey the programming status of the HW
652      */
653     private RulesProgrammingReturnCode uninstallPerHostRules(
654             HostNodeConnector host) {
655         RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
656         Map<NodeConnector, FlowEntry> pos;
657         FlowEntry po;
658         // Now program every single switch
659         for (HostNodePair key : this.rulesDB.keySet()) {
660             if (host == null || key.getHost().equals(host)) {
661                 pos = this.rulesDB.get(key);
662                 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
663                     po = e.getValue();
664                     if (po != null) {
665                         // Uninstall the policy
666                         this.frm.uninstallFlowEntry(po);
667                     }
668                 }
669                 this.rulesDB.remove(key);
670             }
671         }
672         return retCode;
673     }
674
675     /**
676      * Cleanup all the host rules for a given node, triggered when the
677      * switch disconnects, so there is no reason for Hw cleanup
678      * because it's disconnected anyhow
679      * TBD - Revisit above stmt in light of CSCus88743
680      * @param targetNode Node for which we want to do cleanup
681      *
682      */
683     private void uninstallPerNodeRules(Node targetNode) {
684         //RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
685         Map<NodeConnector, FlowEntry> pos;
686         FlowEntry po;
687
688         // Now program every single switch
689         for (HostNodePair key : this.rulesDB.keySet()) {
690             Node node = key.getNode();
691             if (targetNode == null || node.equals(targetNode)) {
692                 log.debug("Work on {} host {}", node, key.getHost());
693                 pos = this.rulesDB.get(key);
694                 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
695                     po = e.getValue();
696                     if (po != null) {
697                         // Uninstall the policy
698                         this.frm.uninstallFlowEntry(po);
699                     }
700                 }
701                 log.debug("Remove {}", key);
702                 this.rulesDB.remove(key);
703             }
704         }
705     }
706
707     /**
708      * Cleanup all the host rules currently present in the rulesDB
709      *
710      * @return a return code that convey the programming status of the HW
711      */
712     private RulesProgrammingReturnCode uninstallPerHostRules() {
713         return uninstallPerHostRules(null);
714     }
715
716     @Override
717     public void recalculateDone() {
718         if (this.hostTracker == null) {
719             //Not yet ready to process all the updates
720             return;
721         }
722         Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
723         for (HostNodeConnector host : allHosts) {
724             Set<Node> switches = preparePerHostRules(host);
725             if (switches != null) {
726                 // This will refresh existing rules, by overwriting
727                 // the previous ones
728                 installPerHostRules(host, switches);
729                 pruneExcessRules(switches);
730             }
731         }
732     }
733
734     void addTobePrunedPolicy(Node swId, FlowEntry po, FlowEntry new_po) {
735         List<FlowEntry> pl = tobePrunedPos.get(swId);
736         if (pl == null) {
737             pl = new LinkedList<FlowEntry>();
738             tobePrunedPos.put(swId, pl);
739         }
740         pl.add(po);
741         log.debug("Adding Pruned Policy for SwId: {}", swId);
742         log.debug("Old Policy: {}", po);
743         log.debug("New Policy: {}", new_po);
744     }
745
746     private void pruneExcessRules(Set<Node> switches) {
747         for (Node swId : switches) {
748             List<FlowEntry> pl = tobePrunedPos.get(swId);
749             if (pl != null) {
750                 log
751                         .debug(
752                                 "Policies for Switch: {} in the list to be deleted: {}",
753                                 swId, pl);
754                 Iterator<FlowEntry> plIter = pl.iterator();
755                 //for (Policy po: pl) {
756                 while (plIter.hasNext()) {
757                     FlowEntry po = plIter.next();
758                     log.error("Removing Policy, Switch: {} Policy: {}", swId,
759                             po);
760                     this.frm.uninstallFlowEntry(po);
761                     plIter.remove();
762                 }
763             }
764             // tobePrunedPos.remove(swId);
765         }
766     }
767
768     /*
769      * A Host facing port has come up in a container. Add rules on the switch where this
770      * port has come up for all the known hosts to the controller.
771      * @param swId switch id of the port where port came up
772      * @param swPort port which came up
773      */
774     private void updateRulesforHIFup(Node node, NodeConnector swPort) {
775         if (this.hostTracker == null) {
776             //Not yet ready to process all the updates
777             return;
778         }
779         log.debug("Host Facing Port in a container came up, install the rules for all hosts from this port !");
780         Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
781         for (HostNodeConnector host : allHosts) {
782             if (node.equals(host.getnodeconnectorNode())
783                     && swPort.equals(host.getnodeConnector())) {
784                 /*
785                  * This host resides behind the same switch and port for which a port up
786                  * message is received. Ideally this should not happen, but if it does,
787                  * don't program any rules for this host
788                  */
789                 continue;
790             }
791             Set<Node> switches = preparePerHostPerSwitchRules(host, node,
792                     swPort);
793             if (switches != null) {
794                 // This will refresh existing rules, by overwriting
795                 // the previous ones
796                 installPerHostRules(host, switches);
797             }
798         }
799
800     }
801
802     @Override
803     public void notifyHTClient(HostNodeConnector host) {
804         if (host == null) {
805             return;
806         }
807         Set<Node> switches = preparePerHostRules(host);
808         if (switches != null) {
809             installPerHostRules(host, switches);
810         }
811     }
812
813     @Override
814     public void notifyHTClientHostRemoved(HostNodeConnector host) {
815         if (host == null) {
816             return;
817         }
818         uninstallPerHostRules(host);
819     }
820
821     @Override
822     public void notifyNode(Node node, UpdateType type,
823             Map<String, Property> propMap) {
824         if (node == null)
825             return;
826
827         switch (type) {
828         case REMOVED:
829             log.debug("Node {} gone, doing a cleanup", node);
830             uninstallPerNodeRules(node);
831             break;
832         default:
833             break;
834         }
835     }
836
837     @Override
838     public void notifyNodeConnector(NodeConnector nodeConnector,
839             UpdateType type, Map<String, Property> propMap) {
840         if (nodeConnector == null)
841             return;
842
843         boolean up = false;
844         switch (type) {
845         case ADDED:
846             up = true;
847             break;
848         case REMOVED:
849             break;
850         case CHANGED:
851             State state = (State) propMap.get(State.StatePropName);
852             if ((state != null) && (state.getValue() == State.EDGE_UP)) {
853                 up = true;
854             }
855             break;
856         default:
857             return;
858         }
859
860         if (up) {
861             handleNodeConnectorStatusUp(nodeConnector);
862         } else {
863             handleNodeConnectorStatusDown(nodeConnector);
864         }
865     }
866
867     private void handleNodeConnectorStatusUp(NodeConnector nodeConnector) {
868         if (topologyManager == null) {
869             log.debug("topologyManager is not set yet");
870             return;
871         }
872
873         if (topologyManager.isInternal(nodeConnector)) {
874             log.debug("{} is not a host facing link", nodeConnector);
875             return;
876         }
877
878         log.debug("{} is up", nodeConnector);
879         updateRulesforHIFup(nodeConnector.getNode(), nodeConnector);
880     }
881
882     private void handleNodeConnectorStatusDown(NodeConnector nodeConnector) {
883         log.debug("{} is down", nodeConnector);
884     }
885
886     void setClusterContainerService(IClusterContainerServices s) {
887         log.debug("Cluster Service set");
888         this.clusterContainerService = s;
889     }
890
891     void unsetClusterContainerService(IClusterContainerServices s) {
892         if (this.clusterContainerService == s) {
893             log.debug("Cluster Service removed!");
894             this.clusterContainerService = null;
895         }
896     }
897
898     /**
899      * Function called by the dependency manager when all the required
900      * dependencies are satisfied
901      *
902      */
903     void init() {
904         startUp();
905     }
906
907     /**
908      * Function called by the dependency manager when at least one
909      * dependency become unsatisfied or when the component is shutting
910      * down because for example bundle is being stopped.
911      *
912      */
913     void destroy() {
914     }
915
916     /**
917      * Function called by dependency manager after "init ()" is called
918      * and after the services provided by the class are registered in
919      * the service registry
920      *
921      */
922     void start() {
923     }
924
925     /**
926      * Function called by the dependency manager before the services
927      * exported by the component are unregistered, this will be
928      * followed by a "destroy ()" calls
929      *
930      */
931     void stop() {
932     }
933
934     public void setSwitchManager(ISwitchManager switchManager) {
935         this.switchManager = switchManager;
936     }
937
938     public void unsetSwitchManager(ISwitchManager switchManager) {
939         if (this.switchManager == switchManager) {
940             this.switchManager = null;
941         }
942     }
943 }