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

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.