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