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