Merge "On link flap new SimpleForwarding flows are rejected by FRM"
[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     private void allocateCaches() {
170         if (this.clusterContainerService == null) {
171             log.info("un-initialized clusterContainerService, can't create cache");
172             return;
173         }
174
175         try {
176             clusterContainerService.createCache(FORWARDING_RULES_CACHE_NAME,
177                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
178         } catch (CacheExistException cee) {
179             log.error("\nCache already exists - destroy and recreate if needed");
180         } catch (CacheConfigException cce) {
181             log.error("\nCache configuration invalid - check cache mode");
182         }
183     }
184
185     @SuppressWarnings({ "unchecked" })
186     private void retrieveCaches() {
187         if (this.clusterContainerService == null) {
188             log.info("un-initialized clusterContainerService, can't retrieve cache");
189             return;
190         }
191
192         rulesDB = (ConcurrentMap<HostNodePair, HashMap<NodeConnector, FlowEntry>>) clusterContainerService
193                 .getCache(FORWARDING_RULES_CACHE_NAME);
194         if (rulesDB == null) {
195             log.error("\nFailed to get rulesDB handle");
196         }
197     }
198
199     private void destroyCaches() {
200         if (this.clusterContainerService == null) {
201             log.info("un-initialized clusterContainerService, can't destroy cache");
202             return;
203         }
204
205         clusterContainerService.destroyCache(FORWARDING_RULES_CACHE_NAME);
206     }
207
208     /**
209      * Populates <tt>rulesDB</tt> with rules specifying how to reach
210      * <tt>host</tt> from <tt>currNode</tt> assuming that:
211      * <ul>
212      * <li><tt>host</tt> is attached to <tt>rootNode</tt>
213      * <li><tt>link</tt> is the next part of the path to reach <tt>rootNode</tt>
214      * from <tt>currNode</tt>
215      * <li><tt>rulesDB.get(key)</tt> represents the list of rules stored about
216      * <tt>host</tt> at <tt>currNode</tt>
217      * </ul>
218      *
219      * @param host
220      *            The host to be reached.
221      * @param currNode
222      *            The current node being processed.
223      * @param rootNode
224      *            The node to be reached. Really, the switch which host is
225      *            attached to.
226      * @param link
227      *            The link to follow from curNode to get to rootNode
228      * @param key
229      *            The key to store computed rules at in the rulesDB. For now,
230      *            this is a {@link HostNodePair} of host and currNode.
231      */
232     private void updatePerHostRuleInSW(HostNodeConnector host, Node currNode,
233             Node rootNode, Edge link, HostNodePair key) {
234
235         // only the link parameter is optional
236         if (host == null || key == null || currNode == null || rootNode == null) {
237             return;
238         }
239
240         Set<NodeConnector> ports = new HashSet<NodeConnector>();
241         // add a special port of type ALL and port 0 to represent the node
242         // without specifying a port on that node
243         ports.add(NodeConnectorCreator.createNodeConnector(
244                 NodeConnectorIDType.ALL, NodeConnector.SPECIALNODECONNECTORID,
245                 currNode));
246
247         HashMap<NodeConnector, FlowEntry> pos = this.rulesDB.get(key);
248         if (pos == null) {
249             pos = new HashMap<NodeConnector, FlowEntry>();
250         }
251
252         for (NodeConnector inPort : ports) {
253             // skip the port connected to the target host
254             if (currNode.equals(rootNode)
255                     && (host.getnodeConnector().equals(inPort))) {
256                 continue;
257             }
258
259             // remove the current rule, if any
260             FlowEntry removed_po = pos.remove(inPort);
261             Match match = new Match();
262             List<Action> actions = new ArrayList<Action>();
263
264             // IP destination based forwarding on /32 entries only!
265             match.setField(MatchType.DL_TYPE, EtherTypes.IPv4.shortValue());
266             match.setField(MatchType.NW_DST, host.getNetworkAddress());
267
268             /* Action for the policy is to forward to a port except on the
269              * switch where the host sits, which is to rewrite also the MAC
270              * and to forward on the Host port */
271             NodeConnector outPort = null;
272
273             if (currNode.equals(rootNode)) {
274                 /* If we're at the root node, then rewrite the DL addr and
275                  * possibly pop the VLAN tag. This allows for MAC rewriting
276                  * in the core of the network assuming we can uniquely ID
277                  * packets based on IP address. */
278
279                 outPort = host.getnodeConnector();
280                 if (inPort.equals(outPort)) {
281                     // TODO: isn't this code skipped already by the above continue?
282                     // skip the host port
283                     continue;
284                 }
285                 actions.add(new SetDlDst(host.getDataLayerAddressBytes()));
286
287                 if (!inPort.getType().equals(NodeConnectorIDType.ALL)) {
288                     // Container mode: at the destination switch, we need to strip out the tag (VLAN)
289                     actions.add(new PopVlan());
290                 }
291             } else {
292                 // currNode is NOT the rootNode, find the next hop and create a rule
293                 if (link != null) {
294                     outPort = link.getTailNodeConnector();
295                     if (inPort.equals(outPort)) {
296                         // skip the outgoing port
297                         continue;
298                     }
299
300                     // If outPort is network link, add VLAN tag
301                     if (topologyManager.isInternal(outPort)) {
302                         log.debug("outPort {}/{} is internal uplink port",
303                                 currNode, outPort);
304                     } else {
305                         log.debug("outPort {}/{} is host facing port",
306                                 currNode, outPort);
307                     }
308
309                     if ((!inPort.getType().equals(NodeConnectorIDType.ALL))
310                         && (topologyManager.isInternal(outPort))) {
311                         Node nextNode = link.getHeadNodeConnector()
312                                             .getNode();
313                         // TODO: Replace this with SAL equivalent
314                         //short tag = container.getTag((Long)nextNode.getNodeID());
315                         short tag = 0;
316                         if (tag != 0) {
317                             log.debug("adding SET_VLAN {} for traffic " +
318                                     "leaving {}/{} toward switch {}",
319                                     new Object[] { tag, currNode, outPort,
320                                     nextNode});
321                             actions.add(new SetVlanId(tag));
322                         } else {
323                             log.debug("No tag assigned to switch {}", nextNode);
324                         }
325                     }
326                 }
327             }
328             if (outPort != null) {
329                 actions.add(new Output(outPort));
330             }
331             if (!inPort.getType().equals(NodeConnectorIDType.ALL)) {
332                 // include input port in the flow match field
333                 match.setField(MatchType.IN_PORT, inPort);
334
335                 if (topologyManager.isInternal(inPort)) {
336                     log.debug("inPort {}/{} is internal uplink port", currNode,
337                             inPort);
338                 } else {
339                     log.debug("inPort {}/{} is host facing port", currNode,
340                             inPort);
341                 }
342
343                 // for incoming network link; if the VLAN tag is defined, include it for incoming flow matching
344                 if (topologyManager.isInternal(inPort)) {
345                     // TODO: Replace this with SAL equivalent
346                     //short tag = container.getTag((Long)currNode.getNodeID());
347                     short tag = 0;
348                     if (tag != 0) {
349                         log.debug("adding MATCH VLAN {} for traffic entering" +
350                                 "  {}/{}",
351                                 new Object[] {tag, currNode, inPort});
352                         match.setField(MatchType.DL_VLAN, tag);
353                     } else {
354                         log.debug("No tag assigned to switch {}", currNode);
355                     }
356                 }
357             }
358             // Make sure the priority for IP switch entries is
359             // set to a level just above default drop entries
360             Flow flow = new Flow(match, actions);
361             flow.setIdleTimeout((short) 0);
362             flow.setHardTimeout((short) 0);
363             flow.setPriority(DEFAULT_IPSWITCH_PRIORITY);
364
365             String policyName = host.getNetworkAddress().getHostAddress()
366                     + "/32";
367             String flowName = "["
368                     + (!inPort.getType().equals(NodeConnectorIDType.ALL) ?
369                        (inPort.getID()).toString()
370                        + "," : "")
371                     + host.getNetworkAddress().getHostAddress() + "/32 on N "
372                     + currNode + "]";
373             FlowEntry po = new FlowEntry(policyName, flowName, flow, currNode);
374
375             /* Now save the rule in the DB rule, so on updates from topology we
376              * can selectively */
377             pos.put(inPort, po);
378             this.rulesDB.put(key, pos);
379             if (!inPort.getType().equals(NodeConnectorIDType.ALL)) {
380                 log.debug("Adding Match(inPort = {} , DIP = {})" +
381                         " Action(outPort= {}) to node {}",
382                         new Object[] { inPort,
383                         host.getNetworkAddress().getHostAddress(),
384                         outPort, currNode});
385                 if ((removed_po != null)
386                         && (!po.getFlow().getMatch().equals(
387                                 removed_po.getFlow().getMatch()))) {
388                     log.debug("Old Flow match: {}, New Flow match: {}",
389                             removed_po.getFlow().getMatch(), po.getFlow()
390                                     .getMatch());
391                     addTobePrunedPolicy(currNode, removed_po, po);
392                 }
393
394             } else {
395                 log.debug("Adding policyMatch(DIP = {}) Action(outPort= {}) " +
396                         "to node {}", new Object[] {
397                         host.getNetworkAddress().getHostAddress(), outPort,
398                         currNode});
399             }
400         }
401     }
402
403     /**
404      * Calculate the per-Host rules to be installed in the rulesDB,
405      * and that will later on be installed in HW, this routine will
406      * implicitly calculate the shortest path tree among the switch
407      * to which the host is attached and all the other switches in the
408      * network and will automatically create all the rules that allow
409      * a /32 destination IP based forwarding, as in traditional IP
410      * networks.
411      *
412      * @param host Host for which we are going to prepare the rules in the rulesDB
413      *
414      * @return A set of switches touched by the calculation
415      */
416     private Set<Node> preparePerHostRules(HostNodeConnector host) {
417         if (host == null) {
418             return null;
419         }
420
421         //TODO: race condition! unset* functions can make these null.
422         if (this.routing == null) {
423             return null;
424         }
425         if (this.switchManager == null) {
426             return null;
427         }
428         if (this.rulesDB == null) {
429             return null;
430         }
431
432         Node rootNode = host.getnodeconnectorNode();
433         Set<Node> nodes = this.switchManager.getNodes();
434         Set<Node> switchesToProgram = new HashSet<Node>();
435         HostNodePair key;
436         HashMap<NodeConnector, FlowEntry> pos;
437         FlowEntry po;
438
439         // for all nodes in the system
440         for (Node node : nodes) {
441             if (node.equals(rootNode)) {
442                 // We skip it because for the node with host attached
443                 // we will process in every case even if there are no
444                 // routes
445                 continue;
446             }
447             List<Edge> links;
448             Path res = this.routing.getRoute(node, rootNode);
449             if ((res == null) || ((links = res.getEdges()) == null)) {
450                 // No route from node to rootNode can be found, back out any
451                 // existing forwarding rules if they exist.
452                 log.debug("NO Route/Path between SW[{}] --> SW[{}] cleaning " +
453                         "potentially existing entries", node, rootNode);
454                 key = new HostNodePair(host, node);
455                 pos = this.rulesDB.get(key);
456                 if (pos != null) {
457                     for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
458                         po = e.getValue();
459                         if (po != null) {
460                             // uninstall any existing rules we put in the
461                             // ForwardingRulesManager
462                             this.frm.uninstallFlowEntry(po);
463                         }
464                     }
465                     this.rulesDB.remove(key);
466                 }
467                 continue;
468             }
469
470             log.debug("Route between SW[{}] --> SW[{}]", node, rootNode);
471             Node currNode = node;
472             key = new HostNodePair(host, currNode);
473
474             // for each link in the route from here to there
475             for (Edge link : links) {
476                 if (link == null) {
477                     log.error("Could not retrieve the Link");
478                     // TODO: should we keep going?
479                     continue;
480                 }
481
482                 log.debug(link.toString());
483
484                 // Index all the switches to be programmed
485                 updatePerHostRuleInSW(host, currNode, rootNode, link, key);
486                 if ((this.rulesDB.get(key)) != null) {
487                     /* Calling updatePerHostRuleInSW() doesn't guarantee that
488                      * rules will be added in currNode (e.g, there is only one
489                      * link from currNode to rootNode This check makes sure that
490                      * there are some rules in the rulesDB for the given key
491                      * prior to adding switch to switchesToProgram
492                      */
493                     switchesToProgram.add(currNode);
494                 }
495                 currNode = link.getHeadNodeConnector().getNode();
496                 key = new HostNodePair(host, currNode);
497             }
498         }
499
500         // This rule will be added no matter if any topology is built
501         // or no, it serve as a way to handle the case of a node with
502         // multiple hosts attached to it but not yet connected to the
503         // rest of the world
504         switchesToProgram.add(rootNode);
505         updatePerHostRuleInSW(host, rootNode, rootNode, null,
506                               new HostNodePair(host, rootNode));
507
508         //      log.debug("Getting out at the end!");
509         return switchesToProgram;
510     }
511
512     /**
513      * Calculate the per-Host rules to be installed in the rulesDB
514      * from a specific switch when a host facing port comes up.
515      * These rules will later on be installed in HW. This routine
516      * will implicitly calculate the shortest path from the switch
517      * where the port has come up to the switch where host is ,
518      * attached and will automatically create all the rules that allow
519      * a /32 destination IP based forwarding, as in traditional IP
520      * networks.
521      *
522      * @param host Host for which we are going to prepare the rules in the rulesDB
523      * @param swId Switch ID where the port has come up
524      *
525      * @return A set of switches touched by the calculation
526      */
527     private Set<Node> preparePerHostPerSwitchRules(HostNodeConnector host,
528             Node node, NodeConnector swport) {
529         if ((host == null) || (node == null)) {
530             return null;
531         }
532         if (this.routing == null) {
533             return null;
534         }
535         if (this.switchManager == null) {
536             return null;
537         }
538         if (this.rulesDB == null) {
539             return null;
540         }
541
542         Node rootNode = host.getnodeconnectorNode();
543         Set<Node> switchesToProgram = new HashSet<Node>();
544         HostNodePair key;
545         Map<NodeConnector, FlowEntry> pos;
546         FlowEntry po;
547         List<Edge> links;
548
549         Path res = this.routing.getRoute(node, rootNode);
550         if ((res == null) || ((links = res.getEdges()) == null)) {
551             // the routing service doesn't know how to get there from here
552             log.debug("NO Route/Path between SW[{}] --> SW[{}] cleaning " +
553                     "potentially existing entries", node, rootNode);
554             key = new HostNodePair(host, node);
555             pos = this.rulesDB.get(key);
556             if (pos != null) {
557                 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
558                     po = e.getValue();
559                     if (po != null) {
560                         //Uninstall the policy
561                         this.frm.uninstallFlowEntry(po);
562                     }
563                 }
564                 this.rulesDB.remove(key);
565             }
566             return null;
567         }
568
569         log.debug("Route between SW[{}] --> SW[{}]", node, rootNode);
570         Integer curr;
571         Node currNode = node;
572         key = new HostNodePair(host, currNode);
573         Edge link;
574         for (curr = 0; curr < links.size(); curr++) {
575             link = links.get(curr);
576             if (link == null) {
577                 log.error("Could not retrieve the Link");
578                 continue;
579             }
580
581             log.debug("Link [{}/{}] --> [{}/{}]", new Object[] {
582                     currNode, link.getHeadNodeConnector(),
583                     link.getHeadNodeConnector().getNode(),
584                     link.getTailNodeConnector()});
585
586             // Index all the switches to be programmed
587             switchesToProgram.add(currNode);
588             updatePerHostRuleInSW(host, currNode, rootNode, link, key);
589             break; // come out of the loop for port up case, interested only in programming one switch
590         }
591
592         // This rule will be added no matter if any topology is built
593         // or no, it serve as a way to handle the case of a node with
594         // multiple hosts attached to it but not yet connected to the
595         // rest of the world
596         // switchesToProgram.add(rootNode);
597         //updatePerHostRuleInSW(host, rootNode,
598         //                                        rootNode, null,
599         //                                        new HostNodePair(host, rootNode),ports);
600
601         //      log.debug("Getting out at the end!");
602         return switchesToProgram;
603     }
604
605     /**
606      * Routine that fetch the per-Host rules from the rulesDB and
607      * install in HW, the one having the same match rules will be
608      * overwritten silently.
609      *
610      * @param host host for which we want to install in HW the per-Host rules
611      * @param switchesToProgram list of switches to be programmed in
612      * HW, usually are them all, but better to be explicit, that list
613      * may change with time based on new switch addition/removal
614      *
615      * @return a return code that convey the programming status of the HW
616      */
617     private RulesProgrammingReturnCode installPerHostRules(
618             HostNodeConnector host, Set<Node> switchesToProgram) {
619         RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
620         if (host == null || switchesToProgram == null) {
621             return RulesProgrammingReturnCode.FAILED_WRONG_PARAMS;
622         }
623         Map<NodeConnector, FlowEntry> pos;
624         FlowEntry po;
625         // Now program every single switch
626         log.debug("Inside installPerHostRules");
627         for (Node swId : switchesToProgram) {
628             HostNodePair key = new HostNodePair(host, swId);
629             pos = this.rulesDB.get(key);
630             if (pos == null) {
631                 continue;
632             }
633             for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
634                 po = e.getValue();
635                 if (po != null) {
636                     // Populate the Policy field now
637                     Status poStatus = this.frm.modifyOrAddFlowEntry(po);
638                     if (!poStatus.isSuccess()) {
639                         log.error("Failed to install policy: "
640                                 + po.getGroupName() + " ("
641                                 + poStatus.getDescription() + ")");
642
643                         retCode = RulesProgrammingReturnCode.FAILED_FEW_SWITCHES;
644                         // Remove the entry from the DB, it was not installed!
645                         this.rulesDB.remove(key);
646                     } else {
647                         log.debug("Successfully installed policy "
648                                 + po.toString() + " on switch " + swId);
649                     }
650                 } else {
651                     log.error("Cannot find a policy for SW:({}) Host: ({})",
652                               swId, host);
653                     /* // Now dump every single rule */
654                     /* for (HostNodePair dumpkey : this.rulesDB.keySet()) { */
655                     /*  po = this.rulesDB.get(dumpkey); */
656                     /*  log.debug("Dumping entry H{" + dumpkey.getHost() + "} S{" + dumpkey.getSwitchId() + "} = {" + (po == null ? "null policy" : po)); */
657                     /* } */
658                 }
659             }
660         }
661         log.debug("Leaving installPerHostRules");
662         return retCode;
663     }
664
665     /**
666      * Cleanup all the host rules for a given host
667      *
668      * @param host Host for which the host rules need to be cleaned
669      * up, the host could be null in that case it match all the hosts
670      *
671      * @return a return code that convey the programming status of the HW
672      */
673     private RulesProgrammingReturnCode uninstallPerHostRules(
674             HostNodeConnector host) {
675         RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
676         Map<NodeConnector, FlowEntry> pos;
677         FlowEntry po;
678         // Now program every single switch
679         for (HostNodePair key : this.rulesDB.keySet()) {
680             if (host == null || key.getHost().equals(host)) {
681                 pos = this.rulesDB.get(key);
682                 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
683                     po = e.getValue();
684                     if (po != null) {
685                         // Uninstall the policy
686                         this.frm.uninstallFlowEntry(po);
687                     }
688                 }
689                 this.rulesDB.remove(key);
690             }
691         }
692         return retCode;
693     }
694
695     /**
696      * Cleanup all the host rules for a given node, triggered when the
697      * switch disconnects, so there is no reason for Hw cleanup
698      * because it's disconnected anyhow
699      * TBD - Revisit above stmt in light of CSCus88743
700      * @param targetNode Node for which we want to do cleanup
701      *
702      */
703     private void uninstallPerNodeRules(Node targetNode) {
704         //RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
705         Map<NodeConnector, FlowEntry> pos;
706         FlowEntry po;
707
708         // Now program every single switch
709         for (HostNodePair key : this.rulesDB.keySet()) {
710             Node node = key.getNode();
711             if (targetNode == null || node.equals(targetNode)) {
712                 log.debug("Work on {} host {}", node, key.getHost());
713                 pos = this.rulesDB.get(key);
714                 for (Map.Entry<NodeConnector, FlowEntry> e : pos.entrySet()) {
715                     po = e.getValue();
716                     if (po != null) {
717                         // Uninstall the policy
718                         this.frm.uninstallFlowEntry(po);
719                     }
720                 }
721                 log.debug("Remove {}", key);
722                 this.rulesDB.remove(key);
723             }
724         }
725     }
726
727     /**
728      * Cleanup all the host rules currently present in the rulesDB
729      *
730      * @return a return code that convey the programming status of the HW
731      */
732     private RulesProgrammingReturnCode uninstallPerHostRules() {
733         return uninstallPerHostRules(null);
734     }
735
736     @Override
737     public void recalculateDone() {
738         if (this.hostTracker == null) {
739             //Not yet ready to process all the updates
740             //TODO: we should make sure that this call is executed eventually
741             return;
742         }
743         Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
744         for (HostNodeConnector host : allHosts) {
745             Set<Node> switches = preparePerHostRules(host);
746             if (switches != null) {
747                 // This will refresh existing rules, by overwriting
748                 // the previous ones
749                 installPerHostRules(host, switches);
750                 pruneExcessRules(switches);
751             }
752         }
753     }
754
755     void addTobePrunedPolicy(Node swId, FlowEntry po, FlowEntry new_po) {
756         List<FlowEntry> pl = tobePrunedPos.get(swId);
757         if (pl == null) {
758             pl = new LinkedList<FlowEntry>();
759             tobePrunedPos.put(swId, pl);
760         }
761         pl.add(po);
762         log.debug("Adding Pruned Policy for SwId: {}", swId);
763         log.debug("Old Policy: {}", po);
764         log.debug("New Policy: {}", new_po);
765     }
766
767     private void pruneExcessRules(Set<Node> switches) {
768         for (Node swId : switches) {
769             List<FlowEntry> pl = tobePrunedPos.get(swId);
770             if (pl != null) {
771                 log
772                         .debug(
773                                 "Policies for Switch: {} in the list to be deleted: {}",
774                                 swId, pl);
775                 Iterator<FlowEntry> plIter = pl.iterator();
776                 //for (Policy po: pl) {
777                 while (plIter.hasNext()) {
778                     FlowEntry po = plIter.next();
779                     log.error("Removing Policy, Switch: {} Policy: {}", swId,
780                             po);
781                     this.frm.uninstallFlowEntry(po);
782                     plIter.remove();
783                 }
784             }
785             // tobePrunedPos.remove(swId);
786         }
787     }
788
789     /**
790      * A Host facing port has come up in a container. Add rules on the switch where this
791      * port has come up for all the known hosts to the controller.
792      * @param swId switch id of the port where port came up
793      * @param swPort port which came up
794      */
795     private void updateRulesforHIFup(Node node, NodeConnector swPort) {
796         if (this.hostTracker == null) {
797             //Not yet ready to process all the updates
798             return;
799         }
800         log.debug("Host Facing Port in a container came up, install the rules for all hosts from this port !");
801         Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
802         for (HostNodeConnector host : allHosts) {
803             if (node.equals(host.getnodeconnectorNode())
804                     && swPort.equals(host.getnodeConnector())) {
805                 /*
806                  * This host resides behind the same switch and port for which a port up
807                  * message is received. Ideally this should not happen, but if it does,
808                  * don't program any rules for this host
809                  */
810                 continue;
811             }
812             Set<Node> switches = preparePerHostPerSwitchRules(host, node,
813                     swPort);
814             if (switches != null) {
815                 // This will refresh existing rules, by overwriting
816                 // the previous ones
817                 installPerHostRules(host, switches);
818             }
819         }
820
821     }
822
823     @Override
824     public void notifyHTClient(HostNodeConnector host) {
825         if (host == null) {
826             return;
827         }
828         Set<Node> switches = preparePerHostRules(host);
829         if (switches != null) {
830             installPerHostRules(host, switches);
831         }
832     }
833
834     @Override
835     public void notifyHTClientHostRemoved(HostNodeConnector host) {
836         if (host == null) {
837             return;
838         }
839         uninstallPerHostRules(host);
840     }
841
842     @Override
843     public void notifyNode(Node node, UpdateType type,
844             Map<String, Property> propMap) {
845         if (node == null) {
846             return;
847         }
848
849         switch (type) {
850         case REMOVED:
851             log.debug("Node {} gone, doing a cleanup", node);
852             uninstallPerNodeRules(node);
853             break;
854         default:
855             break;
856         }
857     }
858
859     @Override
860     public void notifyNodeConnector(NodeConnector nodeConnector,
861             UpdateType type, Map<String, Property> propMap) {
862         if (nodeConnector == null) {
863             return;
864         }
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 }