Issue in flow programming:
[controller.git] / opendaylight / forwardingrulesmanager / src / main / java / org / opendaylight / controller / forwardingrulesmanager / internal / ForwardingRulesManagerImpl.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.forwardingrulesmanager.internal;
11
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.io.ObjectInputStream;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Date;
20 import java.util.EnumSet;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.Set;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap;
30
31 import org.eclipse.osgi.framework.console.CommandInterpreter;
32 import org.eclipse.osgi.framework.console.CommandProvider;
33 import org.opendaylight.controller.clustering.services.CacheConfigException;
34 import org.opendaylight.controller.clustering.services.CacheExistException;
35 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
36 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
37 import org.opendaylight.controller.clustering.services.IClusterServices;
38 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
39 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
40 import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
41 import org.opendaylight.controller.forwardingrulesmanager.FlowEntryInstall;
42 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
43 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManagerAware;
44 import org.opendaylight.controller.forwardingrulesmanager.PortGroup;
45 import org.opendaylight.controller.forwardingrulesmanager.PortGroupChangeListener;
46 import org.opendaylight.controller.forwardingrulesmanager.PortGroupConfig;
47 import org.opendaylight.controller.forwardingrulesmanager.PortGroupProvider;
48 import org.opendaylight.controller.hosttracker.IfIptoHost;
49 import org.opendaylight.controller.sal.action.Action;
50 import org.opendaylight.controller.sal.action.ActionType;
51 import org.opendaylight.controller.sal.action.Controller;
52 import org.opendaylight.controller.sal.action.Flood;
53 import org.opendaylight.controller.sal.action.Output;
54 import org.opendaylight.controller.sal.action.PopVlan;
55 import org.opendaylight.controller.sal.core.ContainerFlow;
56 import org.opendaylight.controller.sal.core.IContainer;
57 import org.opendaylight.controller.sal.core.IContainerListener;
58 import org.opendaylight.controller.sal.core.Node;
59 import org.opendaylight.controller.sal.core.NodeConnector;
60 import org.opendaylight.controller.sal.core.Property;
61 import org.opendaylight.controller.sal.core.UpdateType;
62 import org.opendaylight.controller.sal.flowprogrammer.Flow;
63 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
64 import org.opendaylight.controller.sal.match.Match;
65 import org.opendaylight.controller.sal.match.MatchType;
66 import org.opendaylight.controller.sal.utils.StatusCode;
67 import org.opendaylight.controller.sal.utils.EtherTypes;
68 import org.opendaylight.controller.sal.utils.GlobalConstants;
69 import org.opendaylight.controller.sal.utils.HexEncode;
70 import org.opendaylight.controller.sal.utils.IObjectReader;
71 import org.opendaylight.controller.sal.utils.IPProtocols;
72 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
73 import org.opendaylight.controller.sal.utils.NodeCreator;
74 import org.opendaylight.controller.sal.utils.ObjectReader;
75 import org.opendaylight.controller.sal.utils.ObjectWriter;
76 import org.opendaylight.controller.sal.utils.Status;
77 import org.opendaylight.controller.switchmanager.IInventoryListener;
78 import org.opendaylight.controller.switchmanager.ISwitchManager;
79 import org.opendaylight.controller.switchmanager.ISwitchManagerAware;
80 import org.opendaylight.controller.switchmanager.Subnet;
81 import org.osgi.framework.BundleContext;
82 import org.osgi.framework.FrameworkUtil;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
85
86 /**
87  * Class that manages forwarding rule installation and removal per container of
88  * the network. It also maintains the central repository of all the forwarding
89  * rules installed on the network nodes.
90  */
91 public class ForwardingRulesManagerImpl implements IForwardingRulesManager,
92         PortGroupChangeListener, IContainerListener, ISwitchManagerAware,
93         IConfigurationContainerAware, IInventoryListener, IObjectReader,
94         ICacheUpdateAware<Long, String>, CommandProvider {
95     private static final String SAVE = "Save";
96     private static final Logger log = LoggerFactory
97             .getLogger(ForwardingRulesManagerImpl.class);
98     private Map<Long, String> flowsSaveEvent;
99     private String frmFileName;
100     private String portGroupFileName;
101     private ConcurrentMap<Integer, FlowConfig> staticFlows;
102     private ConcurrentMap<Integer, Integer> staticFlowsOrdinal;
103     private ConcurrentMap<String, PortGroupConfig> portGroupConfigs;
104     private ConcurrentMap<PortGroupConfig, Map<Node, PortGroup>> portGroupData;
105     private ConcurrentMap<String, Object> TSPolicies;
106     private boolean inContainerMode; // being used by default instance only
107     /*
108      * Flow database. It's the software view of what was installed on the
109      * switch. It is indexed by node. For convenience a version indexed
110      * by group name is also maintained. The core element is a class which
111      * contains the flow entry pushed by the functional modules and the
112      * respective container flow merged version. In absence of container
113      * flows, the two flow entries are the same.
114      */
115     private ConcurrentMap<Node, Set<FlowEntryInstall>> nodeFlows;
116     private ConcurrentMap<String, Set<FlowEntryInstall>> groupFlows;
117     /*
118      * Inactive flow list. This is for the global instance of FRM
119      * It will contain all the flow entries which were installed on the
120      * global container when the first container is created.
121      */
122     private List<FlowEntry> inactiveFlows;
123
124     private IfIptoHost hostFinder;
125     private IContainer container;
126     private Set<IForwardingRulesManagerAware> frmAware;
127     private PortGroupProvider portGroupProvider;
128     private IFlowProgrammerService programmer;
129     private IClusterContainerServices clusterContainerService = null;
130     private ISwitchManager switchManager;
131
132     /**
133      * Adds a flow entry onto the network node
134      * It runs various validity checks and derive the final container flows
135      * merged entries that will be attempted to be installed
136      *
137      * @param flowEntry the original flow entry application requested to add
138      * @return
139      */
140     private Status addEntry(FlowEntry flowEntry) {
141         // Sanity Check
142         if (flowEntry == null || flowEntry.getNode() == null) {
143                 String msg = "Invalid FlowEntry";
144             log.warn(msg + ": " + flowEntry);
145             return new Status(StatusCode.NOTACCEPTABLE, msg);
146         }
147
148         /*
149          * Derive the container flow merged entries to install
150          * In presence of N container flows, we may end up with
151          * N different entries to install...
152          */
153         List<FlowEntryInstall> toInstallList = deriveInstallEntries(flowEntry
154                 .clone(), container.getContainerFlows());
155
156         // Container Flow conflict Check
157         if (toInstallList.isEmpty()) {
158                 String msg = "Flow Entry conflicts with all Container Flows";
159                 log.warn(msg);
160             return new Status(StatusCode.CONFLICT, msg);
161         }
162
163         // Derive the list of entries good to be installed
164         List<FlowEntryInstall> toInstallSafe = new ArrayList<FlowEntryInstall>();
165         for (FlowEntryInstall entry : toInstallList) {
166             // Conflict Check: Verify new entry would not overwrite existing ones
167             if (findMatch(entry.getInstall(), false) != null) {
168                 log.warn("Operation Rejected: A flow with same match " + 
169                                 "and priority exists on the target node");
170                 log.trace("Aborting to install " + entry);
171                 continue;
172             }
173             toInstallSafe.add(entry);
174         }
175
176         // Declare failure if all the container flow merged entries clash with existing entries
177         if (toInstallSafe.size() == 0) {
178                 String msg = "A flow with same match and priority exists " + 
179                                 "on the target node";
180                 log.warn(msg);
181             return new Status(StatusCode.CONFLICT, msg);
182         }
183
184         // Try to install an entry at the time
185         Status error = new Status(null, null);
186         boolean oneSucceded = false;
187         for (FlowEntryInstall installEntry : toInstallList) {
188
189             // Install and update database
190                 Status ret = addEntriesInternal(installEntry);
191
192             if (ret.isSuccess()) {
193                 oneSucceded = true;
194             } else {
195                 error = ret;
196                 log.warn("Failed to install the entry: " + ret.getDescription());
197             }
198         }
199
200         return (oneSucceded) ? new Status(StatusCode.SUCCESS, null) : error;
201     }
202
203     /**
204      * Given a flow entry and the list of container flows, it returns the list
205      * of container flow merged flow entries good to be installed on this
206      * container. If the list of container flows is null or empty, the install
207      * entry list will contain only one entry, the original flow entry. If the
208      * flow entry is  congruent with all the N container flows, then the output
209      * install entry list will contain N entries. If the output list is empty,
210      * it means the passed flow entry conflicts with all the container flows.
211      *
212      * @param cFlowList The list of container flows
213      * @return the list of container flow merged entries good to be installed on this container
214      */
215     private List<FlowEntryInstall> deriveInstallEntries(FlowEntry request,
216             List<ContainerFlow> cFlowList) {
217         List<FlowEntryInstall> toInstallList = new ArrayList<FlowEntryInstall>(
218                 1);
219
220         if (container.getContainerFlows() == null
221                 || container.getContainerFlows().isEmpty()) {
222             // No container flows => entry good to be installed unchanged
223             toInstallList.add(new FlowEntryInstall(request.clone(), null));
224         } else {
225             // Create the list of entries to be installed. If the flow entry is
226             // not congruent with any container flow, no install entries will be created
227             for (ContainerFlow cFlow : container.getContainerFlows()) {
228                 if (cFlow.allowsFlow(request.getFlow())) {
229                     toInstallList.add(new FlowEntryInstall(request.clone(),
230                             cFlow));
231                 }
232             }
233         }
234         return toInstallList;
235     }
236
237     /**
238      * Modify a flow entry with a new one
239      * It runs various validity check and derive the final container flows
240      * merged flow entries to work with
241      *
242      * @param currentFlowEntry
243      * @param newFlowEntry
244      * @return Success or error string
245      */
246     private Status modifyEntry(FlowEntry currentFlowEntry,
247             FlowEntry newFlowEntry) {
248         Status retExt;
249
250         // Sanity checks
251         if (currentFlowEntry == null || currentFlowEntry.getNode() == null
252                 || newFlowEntry == null || newFlowEntry.getNode() == null) {
253                 String msg ="Modify: Invalid FlowEntry";
254             log.warn(msg + ": {} or {} ", currentFlowEntry, newFlowEntry);
255             return new Status(StatusCode.NOTACCEPTABLE, msg);
256         }
257         if (!currentFlowEntry.getNode().equals(newFlowEntry.getNode())
258                 || !currentFlowEntry.getFlowName().equals(
259                         newFlowEntry.getFlowName())) {
260                 String msg = "Modify: Incompatible Flow Entries";
261             log.warn(msg +": {} and {}", currentFlowEntry, newFlowEntry);
262             return new Status(StatusCode.NOTACCEPTABLE, msg);
263         }
264
265         // Equality Check
266         if (currentFlowEntry.equals(newFlowEntry)) {
267                 String msg = "Modify skipped as flows are the same";
268             log.debug(msg + ": " + currentFlowEntry + " and " + newFlowEntry);
269             return new Status(StatusCode.SUCCESS, msg);
270         }
271
272         // Conflict Check: Verify the new entry would not conflict with another existing one
273         // This is a loose check on the previous original flow entry requests. No check
274         // on the container flow merged flow entries (if any) yet
275         FlowEntryInstall sameMatchOriginalEntry = findMatch(newFlowEntry, true);
276         if (sameMatchOriginalEntry != null
277                 && !sameMatchOriginalEntry.getOriginal().equals(
278                         currentFlowEntry)) {
279                 String msg = "Operation Rejected: Another flow with same match " +
280                                 "and priority exists on the target node";
281             log.warn(msg);
282             return new Status(StatusCode.CONFLICT, msg);
283         }
284
285         // Derive the installed and toInstall entries
286         List<FlowEntryInstall> installedList = deriveInstallEntries(
287                 currentFlowEntry.clone(), container.getContainerFlows());
288         List<FlowEntryInstall> toInstallList = deriveInstallEntries(
289                 newFlowEntry.clone(), container.getContainerFlows());
290
291         if (toInstallList.isEmpty()) {
292                 String msg = "Modify Operation Rejected: The new entry " + 
293                                 "conflicts with all the container flows";
294             log.warn(msg);
295             return new Status(StatusCode.CONFLICT, msg);
296         }
297
298         /*
299          * If the two list sizes differ, it means the new flow entry does not
300          * satisfy the same number of container flows the current entry does.
301          * This is only possible when the new entry and current entry have
302          * different match. In this scenario the modification would ultimately
303          * be handled as a remove and add operations in the protocol plugin.
304          *
305          * Also, if any of the new flow entries would clash with an existing
306          * one, we cannot proceed with the modify operation, because it would
307          * fail for some entries and leave stale entries on the network node.
308          * Modify path can be taken only if it can be performed completely,
309          * for all entries.
310          *
311          * So, for the above two cases, to simplify, let's decouple the modify in:
312          * 1) remove current entries
313          * 2) install new entries
314          */
315         boolean decouple = false;
316         if (installedList.size() != toInstallList.size()) {
317             log.info("Modify: New flow entry does not satisfy the same " +
318                     "number of container flows as the original entry does");
319             decouple = true;
320         }
321         List<FlowEntryInstall> toInstallSafe = new ArrayList<FlowEntryInstall>();
322         for (FlowEntryInstall installEntry : toInstallList) {
323             // Conflict Check: Verify the new entry would not overwrite another existing one
324             FlowEntryInstall sameMatchEntry = findMatch(installEntry
325                     .getInstall(), false);
326             if (sameMatchEntry != null
327                     && !sameMatchEntry.getOriginal().equals(currentFlowEntry)) {
328                 log.info("Modify: new container flow merged flow entry " + 
329                     "clashes with existing flow");
330                 decouple = true;
331             } else {
332                 toInstallSafe.add(installEntry);
333             }
334         }
335
336         if (decouple) {
337             // Remove current entries
338             for (FlowEntryInstall currEntry : installedList) {
339                 this.removeEntryInternal(currEntry);
340             }
341             // Install new entries
342             for (FlowEntryInstall newEntry : toInstallSafe) {
343                 this.addEntriesInternal(newEntry);
344             }
345         } else {
346             /*
347              * The two list have the same size and the entries to install do not
348              * clash with any existing flow on the network node. We assume here
349              * (and might be wrong) that the same container flows that were satisfied
350              * by the current entries are the same that are satisfied by the new
351              * entries. Let's take the risk for now.
352              *
353              * Note: modification has to be complete. If any entry modification
354              * fails, we need to stop, restore the already modified entries,
355              * and declare failure.
356              */
357             Status retModify;
358             int i = 0;
359             int size = toInstallList.size();
360             while (i < size) {
361                 // Modify and update database
362                 retModify = modifyEntryInternal(installedList.get(i),
363                         toInstallList.get(i));
364                 if (retModify.isSuccess()) {
365                     i++;
366                 } else {
367                     break;
368                 }
369             }
370             // Check if uncompleted modify
371             if (i < size) {
372                 log.warn("Unable to perform a complete modify for all " + 
373                                 "the container flows merged entries");
374                 // Restore original entries
375                 int j = 0;
376                 while (j < i) {
377                     log.info("Attempting to restore initial entries");
378                     retExt = modifyEntryInternal(toInstallList.get(i),
379                             installedList.get(i));
380                     if (retExt.isSuccess()) {
381                         j++;
382                     } else {
383                         break;
384                     }
385                 }
386                 // Fatal error, recovery failed
387                 if (j < i) {
388                         String msg = "Flow recovery failed ! Unrecoverable Error";
389                     log.error(msg);
390                     return new Status(StatusCode.INTERNALERROR, msg);
391                 }
392             }
393         }
394         return new Status(StatusCode.SUCCESS, null);
395     }
396
397     /**
398      * This is the function that modifies the final container flows merged
399      * entries on the network node and update the database. It expects that
400      * all the  validity checks are passed
401      *
402      * @param currentEntries
403      * @param newEntries
404      * @return
405      */
406     private Status modifyEntryInternal(FlowEntryInstall currentEntries,
407             FlowEntryInstall newEntries) {
408         // Modify the flow on the network node
409         Status status = programmer.modifyFlow(currentEntries.getNode(),
410                 currentEntries.getInstall().getFlow(), newEntries.getInstall()
411                         .getFlow());
412
413         if (!status.isSuccess()) {
414             log.warn("SDN Plugin failed to program the flow: " + status.getDescription());
415             return status;
416         }
417
418         log.trace("Modified {} => {}", currentEntries.getInstall(), newEntries
419                 .getInstall());
420
421         // Update DB
422         updateLocalDatabase(currentEntries, false);
423         updateLocalDatabase(newEntries, true);
424
425         return status;
426     }
427
428     /**
429      * Remove a flow entry. If the entry is not present in the software view
430      * (entry or node not present), it return successfully
431      *
432      * @param flowEntry
433      * @return
434      */
435     private Status removeEntry(FlowEntry flowEntry) {
436         Status error = new Status(null, null);
437         
438         // Sanity Check
439         if (flowEntry == null || flowEntry.getNode() == null) {
440                 String msg = "Invalid FlowEntry";
441             log.warn(msg + ": " + flowEntry);
442             return new Status(StatusCode.NOTACCEPTABLE, msg);
443         }
444
445         // Derive the container flows merged installed entries
446         List<FlowEntryInstall> installedList = deriveInstallEntries(flowEntry
447                 .clone(), container.getContainerFlows());
448
449         Set<FlowEntryInstall> flowsOnNode = nodeFlows.get(flowEntry.getNode());
450         boolean atLeastOneRemoved = false;
451         for (FlowEntryInstall entry : installedList) {
452             if (flowsOnNode == null) {
453                 String msg = "Removal skipped (Node down)";
454                 log.debug(msg + " for flow entry " + flowEntry);
455                 return new Status(StatusCode.SUCCESS, msg);
456             }
457             if (!flowsOnNode.contains(entry)) {
458                 log.debug("Removal skipped (not present in software view) "
459                         + "for flow entry " + flowEntry);
460
461                 if (installedList.size() == 1) {
462                     // If we had only one entry to remove, we are done
463                     return new Status(StatusCode.SUCCESS, null);
464                 } else {
465                     continue;
466                 }
467             }
468
469             // Remove and update DB
470             Status ret = removeEntryInternal(entry);
471
472             if (!ret.isSuccess()) {
473                 error = ret;
474                 log.warn("Failed to remove the entry: " + ret.getDescription());
475                 if (installedList.size() == 1) {
476                     // If we had only one entry to remove, this is fatal failure
477                     return error;
478                 }
479             } else {
480                 atLeastOneRemoved = true;
481             }
482         }
483
484         /*
485          * No worries if full removal failed. Consistency checker will
486          * take care of removing the stale entries later, or adjusting
487          * the software database if not in sync with hardware
488          */
489         return (atLeastOneRemoved) ? 
490                         new Status(StatusCode.SUCCESS, null) : error;
491     }
492
493     /**
494      * This is the function that removes the final container flows merged entry
495      * from the network node and update the database. It expects that all the
496      * validity checks are passed
497      *
498      * @param entry the FlowEntryInstall
499      * @return "Success" or error string
500      */
501     private Status removeEntryInternal(FlowEntryInstall entry) {
502         // Mark the entry to be deleted (for CC just in case we fail)
503         entry.toBeDeleted();
504
505         // Remove from node
506         Status status = 
507                         programmer.removeFlow(entry.getNode(), 
508                                         entry.getInstall().getFlow());
509
510         if (!status.isSuccess()) {
511             log.warn("SDN Plugin failed to remove the flow: " + 
512                         status.getDescription());
513             return status;
514         }
515         log.trace("Removed  {}", entry.getInstall());
516
517         // Update DB
518         updateLocalDatabase(entry, false);
519
520         return status;
521     }
522
523     /**
524      * This is the function that installs the final container flow merged entry
525      * on the network node and updates the database. It expects that all the
526      * validity and conflict checks are passed. That means it does not check
527      * whether this flow would conflict or overwrite an existing one.
528      *
529      * @param entry the FlowEntryInstall
530      * @return "Success" or error string
531      */
532     private Status addEntriesInternal(FlowEntryInstall entry) {
533         // Install the flow on the network node
534         Status status = programmer.addFlow(entry.getNode(), 
535                         entry.getInstall().getFlow());
536
537         if (!status.isSuccess()) {
538             log.warn("SDN Plugin failed to program the flow: " + 
539                         status.getDescription());
540             return status;
541         }
542
543         log.trace("Added    {}", entry.getInstall());
544
545         // Update DB
546         updateLocalDatabase(entry, true);
547
548         return status;
549     }
550
551     /**
552      * Returns true if the flow conflicts with all the container's flows.
553      * This means that if the function returns true, the passed flow entry
554      * is congruent with at least one container flow, hence it is good to
555      * be installed on this container.
556      *
557      * @param flowEntry
558      * @return true if flow conflicts with all the container flows, false otherwise
559      */
560     private boolean entryConflictsWithContainerFlows(FlowEntry flowEntry) {
561         List<ContainerFlow> cFlowList = container.getContainerFlows();
562
563         // Validity check and avoid unnecessary computation
564         // Also takes care of default container where no container flows are present
565         if (cFlowList == null || cFlowList.isEmpty()) {
566             return false;
567         }
568
569         for (ContainerFlow cFlow : cFlowList) {
570             if (cFlow.allowsFlow(flowEntry.getFlow())) {
571                 // Entry is allowed by at least one container flow: good to go
572                 return false;
573             }
574         }
575         return true;
576     }
577
578     private synchronized void updateLocalDatabase(FlowEntryInstall entry,
579             boolean add) {
580         // Update node indexed flow database
581         updateNodeFlowsDB(entry, add);
582
583         // Update group indexed flow database
584         updateGroupFlowsDB(entry, add);
585     }
586
587     /*
588      * Update the node mapped flows database
589      */
590     private void updateNodeFlowsDB(FlowEntryInstall flowEntries, boolean add) {
591         Node node = flowEntries.getNode();
592
593         Set<FlowEntryInstall> flowEntrylist = this.nodeFlows.get(node);
594         if (flowEntrylist == null) {
595             if (add == false) {
596                 return;
597             } else {
598                 flowEntrylist = new HashSet<FlowEntryInstall>();
599             }
600         }
601
602         if (add == true) {
603             flowEntrylist.add(flowEntries);
604         } else {
605             flowEntrylist.remove(flowEntries);
606         }
607
608         if (flowEntrylist.isEmpty()) {
609             this.nodeFlows.remove(node);
610         } else {
611             this.nodeFlows.put(node, flowEntrylist);
612         }
613     }
614
615     /*
616      * Update the group name mapped flows database
617      */
618     private void updateGroupFlowsDB(FlowEntryInstall flowEntries, boolean add) {
619         Set<FlowEntryInstall> flowList;
620         FlowEntryInstall exists = null;
621         String flowName = flowEntries.getFlowName();
622         String groupName = flowEntries.getGroupName();
623
624         if (this.groupFlows == null) {
625             return;
626         }
627
628         // Flow may not be part of a group
629         if (groupName == null) {
630             return;
631         }
632
633         if (this.groupFlows.containsKey(groupName)) {
634             flowList = this.groupFlows.get(groupName);
635         } else {
636             if (add == false) {
637                 return;
638             } else {
639                 flowList = new HashSet<FlowEntryInstall>();
640             }
641         }
642
643         for (FlowEntryInstall flow : flowList) {
644             if (flow.equalsByNodeAndName(flowEntries.getNode(), flowName)) {
645                 exists = flow;
646                 break;
647             }
648         }
649
650         if (exists == null && add == false) {
651             return;
652         }
653
654         if (exists != null) {
655             flowList.remove(exists);
656         }
657
658         if (add == true) {
659             flowList.add(flowEntries);
660         }
661
662         if (flowList.isEmpty()) {
663             this.groupFlows.remove(groupName);
664         } else {
665             this.groupFlows.put(groupName, flowList);
666         }
667     }
668
669     /**
670      * Remove a flow entry that has been added previously First checks if the
671      * entry is effectively present in the local database
672      */
673     @SuppressWarnings("unused")
674     private Status removeEntry(Node node, String flowName) {
675         FlowEntryInstall target = null;
676
677         // Find in database
678         for (FlowEntryInstall entry : this.nodeFlows.get(node)) {
679             if (entry.equalsByNodeAndName(node, flowName)) {
680                 target = entry;
681                 break;
682             }
683         }
684
685         // If it is not there, stop any further processing
686         if (target == null) {
687             return new Status(StatusCode.SUCCESS, "Entry is not present");
688         }
689
690         // Remove from node
691         Status status = programmer.removeFlow(target.getNode(), target
692                 .getInstall().getFlow());
693
694         // Update DB
695         if (status.isSuccess()) {
696             updateLocalDatabase(target, false);
697         }
698
699         return status;
700     }
701
702     @Override
703     public Status installFlowEntry(FlowEntry flowEntry) {
704         Status status;
705         if (inContainerMode) {
706                 String msg = "Controller in container mode: Install Refused";
707             status = new Status(StatusCode.NOTACCEPTABLE, msg);
708             log.warn(msg);
709         } else {
710             status = addEntry(flowEntry);
711         }
712         return status;
713     }
714
715     @Override
716     public Status uninstallFlowEntry(FlowEntry entry) {
717         Status status;
718         if (inContainerMode) {
719                 String msg = "Controller in container mode: Uninstall Refused";
720             status = new Status(StatusCode.NOTACCEPTABLE, msg);
721             log.warn(msg);
722         } else {
723                 status = removeEntry(entry);
724         }
725         return status;
726     }
727
728     @Override
729     public Status modifyFlowEntry(FlowEntry currentFlowEntry,
730             FlowEntry newFlowEntry) {
731         Status status = null;
732         if (inContainerMode) {
733             String msg = "Controller in container mode: Modify Refused";
734             status = new Status(StatusCode.NOTACCEPTABLE, msg);
735             log.warn(msg);
736         } else {
737             status = modifyEntry(currentFlowEntry, newFlowEntry);
738         }
739         return status;
740     }
741
742     @Override
743     public Status modifyOrAddFlowEntry(FlowEntry newFlowEntry) {
744         /*
745          * Run a loose check on the installed entries to decide whether to go
746          * with a add or modify method. A loose check means only check against
747          * the original flow entry requests and not against the installed
748          * flow entries which are the result of the original entry merged with
749          * the container flow(s) (if any). The modifyFlowEntry method in
750          * presence of conflicts with the Container flows (if any) would revert
751          * back to a delete + add pattern
752          */
753         FlowEntryInstall currentFlowEntries = findMatch(newFlowEntry, true);
754
755         if (currentFlowEntries != null) {
756             return modifyFlowEntry(currentFlowEntries.getOriginal(),
757                     newFlowEntry);
758         } else {
759             return installFlowEntry(newFlowEntry);
760         }
761     }
762
763     /**
764      * Try to find in the database if a Flow with the same Match and priority
765      * of the passed one already exists for the specified network node.
766      * Flow, priority and network node are all specified in the FlowEntry
767      * If found, the respective FlowEntryInstall Object is returned
768      *
769      * @param flowEntry the FlowEntry to be tested against the ones installed
770      * @param looseCheck if true, the function will run the check against the
771      *          original flow entry portion of the installed entries
772      * @return null if not found, otherwise the FlowEntryInstall which contains
773      *          the existing flow entry
774      */
775     private FlowEntryInstall findMatch(FlowEntry flowEntry, boolean looseCheck) {
776         Flow flow = flowEntry.getFlow();
777         Match match = flow.getMatch();
778         short priority = flow.getPriority();
779         Set<FlowEntryInstall> thisNodeList = nodeFlows.get(flowEntry.getNode());
780
781         if (thisNodeList != null) {
782             for (FlowEntryInstall flowEntries : thisNodeList) {
783                 flow = (looseCheck == false) ? flowEntries.getInstall()
784                         .getFlow() : flowEntries.getOriginal().getFlow();
785                 if (flow.getMatch().equals(match)
786                         && flow.getPriority() == priority) {
787                     return flowEntries;
788                 }
789             }
790         }
791         return null;
792     }
793
794     public boolean checkFlowEntryConflict(FlowEntry flowEntry) {
795         return entryConflictsWithContainerFlows(flowEntry);
796     }
797
798     /**
799      * Updates all installed flows because the container flow got updated
800      * This is obtained in two phases on per node basis:
801      * 1) Uninstall of all flows
802      * 2) Reinstall of all flows
803      * This is needed because a new container flows merged flow may conflict with an existing
804      * old container flows merged flow on the network node
805      */
806     private void updateFlowsContainerFlow() {
807         List<FlowEntryInstall> oldCouples = new ArrayList<FlowEntryInstall>();
808         List<FlowEntry> toReinstall = new ArrayList<FlowEntry>();
809         for (Entry<Node, Set<FlowEntryInstall>> entry : this.nodeFlows
810                 .entrySet()) {
811             oldCouples.clear();
812             toReinstall.clear();
813             if (entry.getValue() == null) {
814                 continue;
815             }
816             // Create a set of old entries and one of original entries to be reinstalled
817             for (FlowEntryInstall oldCouple : entry.getValue()) {
818                 oldCouples.add(oldCouple);
819                 toReinstall.add(oldCouple.getOriginal());
820             }
821             // Remove the old couples. No validity checks to be run, use the internal remove
822             for (FlowEntryInstall oldCouple : oldCouples) {
823                 this.removeEntryInternal(oldCouple);
824             }
825             // Reinstall the original flow entries, via the regular path: new cFlow merge + validations
826             for (FlowEntry flowEntry : toReinstall) {
827                 this.installFlowEntry(flowEntry);
828             }
829         }
830     }
831
832     public void nonClusterObjectCreate() {
833         nodeFlows = new ConcurrentHashMap<Node, Set<FlowEntryInstall>>();
834         TSPolicies = new ConcurrentHashMap<String, Object>();
835         groupFlows = new ConcurrentHashMap<String, Set<FlowEntryInstall>>();
836         staticFlowsOrdinal = new ConcurrentHashMap<Integer, Integer>();
837         portGroupConfigs = new ConcurrentHashMap<String, PortGroupConfig>();
838         portGroupData = new ConcurrentHashMap<PortGroupConfig, Map<Node, PortGroup>>();
839         staticFlows = new ConcurrentHashMap<Integer, FlowConfig>();
840         flowsSaveEvent = new HashMap<Long, String>();
841         inactiveFlows = new ArrayList<FlowEntry>(1);
842     }
843
844     private void registerWithOSGIConsole() {
845         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
846                 .getBundleContext();
847         bundleContext.registerService(CommandProvider.class.getName(), this,
848                 null);
849     }
850
851     @Override
852     public void setTSPolicyData(String policyname, Object o, boolean add) {
853
854         if (add) {
855             /* Check if this policy already exists */
856             if (!(TSPolicies.containsKey(policyname))) {
857                 TSPolicies.put(policyname, o);
858             }
859         } else {
860             TSPolicies.remove(policyname);
861         }
862         if (frmAware != null) {
863             synchronized (frmAware) {
864                 for (IForwardingRulesManagerAware frma : frmAware) {
865                     try {
866                         frma.policyUpdate(policyname, add);
867                     } catch (Exception e) {
868                         log.error("Exception on callback", e);
869                     }
870                 }
871             }
872         }
873     }
874
875     @Override
876     public Map<String, Object> getTSPolicyData() {
877         return TSPolicies;
878     }
879
880     @Override
881     public Object getTSPolicyData(String policyName) {
882         if (TSPolicies.containsKey(policyName)) {
883             return TSPolicies.get(policyName);
884         } else {
885             return null;
886         }
887     }
888
889     @Override
890     public List<FlowEntry> getFlowEntriesForGroup(String policyName) {
891         List<FlowEntry> list = null;
892         if (this.groupFlows != null && this.groupFlows.containsKey(policyName)) {
893             list = new ArrayList<FlowEntry>();
894             for (FlowEntryInstall entries : groupFlows.get(policyName)) {
895                 list.add(entries.getOriginal());
896             }
897             return new ArrayList<FlowEntry>();
898         }
899         return list;
900     }
901
902     @Override
903     public void addOutputPort(Node node, String flowName,
904             List<NodeConnector> portList) {
905
906         Set<FlowEntryInstall> flowEntryList = this.nodeFlows.get(node);
907
908         for (FlowEntryInstall flow : flowEntryList) {
909             if (flow.getFlowName().equals(flowName)) {
910                 FlowEntry currentFlowEntry = flow.getOriginal();
911                 FlowEntry newFlowEntry = currentFlowEntry.clone();
912                 for (NodeConnector dstPort : portList) {
913                     newFlowEntry.getFlow().addAction(new Output(dstPort));
914                 }
915                 Status error = modifyEntry(currentFlowEntry, newFlowEntry);
916                 if (error.isSuccess()) {
917                     log.info("Ports {} added to FlowEntry {}", portList,
918                             flowName);
919                 } else {
920                     log.warn("Failed to add ports {} to Flow entry {}: "
921                             + error.getDescription(), portList, 
922                             currentFlowEntry.toString());
923                 }
924                 return;
925             }
926         }
927         log.warn("Failed to add ports to Flow {} on Node {}: Entry Not Found",
928                 flowName, node);
929     }
930
931     @Override
932     public void removeOutputPort(Node node, String flowName,
933             List<NodeConnector> portList) {
934
935         Set<FlowEntryInstall> flowEntryList = this.nodeFlows.get(node);
936
937         for (FlowEntryInstall flow : flowEntryList) {
938             if (flow.getFlowName().equals(flowName)) {
939                 FlowEntry currentFlowEntry = flow.getOriginal();
940                 FlowEntry newFlowEntry = currentFlowEntry.clone();
941                 for (NodeConnector dstPort : portList) {
942                     Action action = new Output(dstPort);
943                     newFlowEntry.getFlow().removeAction(action);
944                 }
945                 Status status = modifyEntry(currentFlowEntry, newFlowEntry);
946                 if (status.isSuccess()) {
947                     log.info("Ports {} removed from FlowEntry {}", portList,
948                             flowName);
949                 } else {
950                     log.warn("Failed to remove ports {} from Flow entry {}: "
951                             + status.getDescription(), portList, 
952                             currentFlowEntry.toString());
953                 }
954                 return;
955             }
956         }
957         log
958                 .warn(
959                         "Failed to remove ports from Flow {} on Node {}: Entry Not Found",
960                         flowName, node);
961     }
962
963     /*
964      * This function assumes the target flow has only one output port
965      */
966     @Override
967     public void replaceOutputPort(Node node, String flowName,
968             NodeConnector outPort) {
969         FlowEntry currentFlowEntry = null;
970         FlowEntry newFlowEntry = null;
971         Set<FlowEntryInstall> flowEntryList = this.nodeFlows.get(node);
972
973         // Find the flow
974         for (FlowEntryInstall flow : flowEntryList) {
975             if (flow.getFlowName().equals(flowName)) {
976                 currentFlowEntry = flow.getOriginal();
977                 break;
978             }
979         }
980         if (currentFlowEntry == null) {
981             log
982                     .warn(
983                             "Failed to replace output port for flow {} on node {}: Entry Not Found",
984                             flowName, node);
985             return;
986         }
987
988         // Create a flow copy with the new output port
989         newFlowEntry = currentFlowEntry.clone();
990         Action target = null;
991         for (Action action : newFlowEntry.getFlow().getActions()) {
992             if (action.getType() == ActionType.OUTPUT) {
993                 target = action;
994                 break;
995             }
996         }
997         newFlowEntry.getFlow().removeAction(target);
998         newFlowEntry.getFlow().addAction(new Output(outPort));
999
1000         // Modify on network node
1001         Status status = modifyEntry(currentFlowEntry, newFlowEntry);
1002
1003         if (status.isSuccess()) {
1004             log.info("Output port replaced with " + outPort
1005                     + " for flow {} on node {}", flowName, node);
1006         } else {
1007             log.warn("Failed to replace output port for flow {} on node {}: ",
1008                     status.getDescription(), flowName, node);
1009         }
1010         return;
1011     }
1012
1013     @Override
1014     public NodeConnector getOutputPort(Node node, String flowName) {
1015         Set<FlowEntryInstall> flowEntryList = this.nodeFlows.get(node);
1016
1017         for (FlowEntryInstall flow : flowEntryList) {
1018             if (flow.getFlowName().equals(flowName)) {
1019                 for (Action action : flow.getOriginal().getFlow().getActions()) {
1020                     if (action.getType() == ActionType.OUTPUT) {
1021                         return ((Output) action).getPort();
1022                     }
1023                 }
1024             }
1025         }
1026
1027         return null;
1028     }
1029
1030     private void cacheStartup() {
1031         allocateCaches();
1032         retrieveCaches();
1033     }
1034
1035     @SuppressWarnings("deprecation")
1036     private void allocateCaches() {
1037         if (this.clusterContainerService == null) {
1038             log
1039                     .warn("Un-initialized clusterContainerService, can't create cache");
1040             return;
1041         }
1042
1043         log.debug("FRM allocateCaches for Container {}", container);
1044
1045         try {
1046             clusterContainerService.createCache("frm.nodeFlows", EnumSet
1047                     .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1048
1049             clusterContainerService.createCache("frm.groupFlows", EnumSet
1050                     .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1051
1052             clusterContainerService.createCache("frm.staticFlows", EnumSet
1053                     .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1054
1055             clusterContainerService.createCache("frm.flowsSaveEvent", EnumSet
1056                     .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1057
1058             clusterContainerService.createCache("frm.staticFlowsOrdinal",
1059                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1060
1061             clusterContainerService.createCache("frm.portGroupConfigs", EnumSet
1062                     .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1063
1064             clusterContainerService.createCache("frm.portGroupData", EnumSet
1065                     .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1066
1067             clusterContainerService.createCache("frm.TSPolicies", EnumSet
1068                     .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1069
1070         } catch (CacheConfigException cce) {
1071             log.error("FRM CacheConfigException");
1072         } catch (CacheExistException cce) {
1073             log.error("FRM CacheExistException");
1074         }
1075     }
1076
1077     @SuppressWarnings( { "unchecked", "deprecation" })
1078     private void retrieveCaches() {
1079         ConcurrentMap<?, ?> map;
1080
1081         if (this.clusterContainerService == null) {
1082             log
1083                     .warn("un-initialized clusterContainerService, can't retrieve cache");
1084             return;
1085         }
1086
1087         log.debug("FRM retrieveCaches for Container {}", container);
1088
1089         map = clusterContainerService.getCache("frm.nodeFlows");
1090         if (map != null) {
1091             nodeFlows = (ConcurrentMap<Node, Set<FlowEntryInstall>>) map;
1092         } else {
1093             log
1094                     .error(
1095                             "FRM Cache frm.nodeFlows allocation failed for Container {}",
1096                             container);
1097         }
1098
1099         map = clusterContainerService.getCache("frm.groupFlows");
1100         if (map != null) {
1101             groupFlows = (ConcurrentMap<String, Set<FlowEntryInstall>>) map;
1102         } else {
1103             log
1104                     .error(
1105                             "FRM Cache frm.groupFlows allocation failed for Container {}",
1106                             container);
1107         }
1108
1109         map = clusterContainerService.getCache("frm.staticFlows");
1110         if (map != null) {
1111             staticFlows = (ConcurrentMap<Integer, FlowConfig>) map;
1112         } else {
1113             log
1114                     .error(
1115                             "FRM Cache frm.staticFlows allocation failed for Container {}",
1116                             container);
1117         }
1118
1119         map = clusterContainerService.getCache("frm.flowsSaveEvent");
1120         if (map != null) {
1121             flowsSaveEvent = (ConcurrentMap<Long, String>) map;
1122         } else {
1123             log
1124                     .error(
1125                             "FRM Cache frm.flowsSaveEvent allocation failed for Container {}",
1126                             container);
1127         }
1128
1129         map = clusterContainerService.getCache("frm.staticFlowsOrdinal");
1130         if (map != null) {
1131             staticFlowsOrdinal = (ConcurrentMap<Integer, Integer>) map;
1132         } else {
1133             log
1134                     .error(
1135                             "FRM Cache frm.staticFlowsOrdinal allocation failed for Container {}",
1136                             container);
1137         }
1138
1139         map = clusterContainerService.getCache("frm.portGroupConfigs");
1140         if (map != null) {
1141             portGroupConfigs = (ConcurrentMap<String, PortGroupConfig>) map;
1142         } else {
1143             log
1144                     .error(
1145                             "FRM Cache frm.portGroupConfigs allocation failed for Container {}",
1146                             container);
1147         }
1148
1149         map = clusterContainerService.getCache("frm.portGroupData");
1150         if (map != null) {
1151             portGroupData = (ConcurrentMap<PortGroupConfig, Map<Node, PortGroup>>) map;
1152         } else {
1153             log
1154                     .error(
1155                             "FRM Cache frm.portGroupData allocation failed for Container {}",
1156                             container);
1157         }
1158
1159         map = clusterContainerService.getCache("frm.TSPolicies");
1160         if (map != null) {
1161             TSPolicies = (ConcurrentMap<String, Object>) map;
1162         } else {
1163             log
1164                     .error(
1165                             "FRM Cache frm.TSPolicies allocation failed for Container {}",
1166                             container);
1167         }
1168
1169     }
1170
1171     @SuppressWarnings("deprecation")
1172         private void destroyCaches() {
1173         if (this.clusterContainerService == null) {
1174             log
1175                     .warn("Un-initialized clusterContainerService, can't destroy cache");
1176             return;
1177         }
1178
1179         log.debug("FRM destroyCaches for Container {}", container);
1180         clusterContainerService.destroyCache("frm.nodeFlows");
1181         clusterContainerService.destroyCache("frm.TSPolicies");
1182         clusterContainerService.destroyCache("frm.groupFlows");
1183         clusterContainerService.destroyCache("frm.staticFlows");
1184         clusterContainerService.destroyCache("frm.flowsSaveEvent");
1185         clusterContainerService.destroyCache("frm.staticFlowsOrdinal");
1186         clusterContainerService.destroyCache("frm.portGroupData");
1187         clusterContainerService.destroyCache("frm.portGroupConfigs");
1188         nonClusterObjectCreate();
1189     }
1190
1191     private boolean flowConfigExists(FlowConfig config) {
1192         // As per customer requirement, flow name has to be unique on per node
1193         // id basis
1194         for (FlowConfig fc : staticFlows.values()) {
1195             if (fc.isByNameAndNodeIdEqual(config)) {
1196                 return true;
1197             }
1198         }
1199         return false;
1200     }
1201
1202     @Override
1203     public Status addStaticFlow(FlowConfig config, boolean restore) {
1204         StringBuffer resultStr = new StringBuffer();
1205         boolean multipleFlowPush = false;
1206         String error;
1207         Status status;
1208         config.setStatus(StatusCode.SUCCESS.toString());
1209
1210         // Presence check
1211         if (flowConfigExists(config)) {
1212             error = "Entry with this name on specified switch already exists";
1213             config.setStatus(error);
1214             return new Status(StatusCode.CONFLICT, error);
1215         }
1216
1217         // Skip validation check if we are trying to restore a saved config
1218         if (!restore && !config.isValid(container, resultStr)) {
1219             log.debug(resultStr.toString());
1220             error = "Invalid Configuration (" + resultStr.toString() + ")";
1221             config.setStatus(error);
1222             return new Status(StatusCode.BADREQUEST, error);
1223         }
1224
1225         if ((config.getIngressPort() == null) && config.getPortGroup() != null) {
1226             for (String portGroupName : portGroupConfigs.keySet()) {
1227                 if (portGroupName.equalsIgnoreCase(config.getPortGroup())) {
1228                     multipleFlowPush = true;
1229                     break;
1230                 }
1231             }
1232             if (!multipleFlowPush) {
1233                 log.debug(resultStr.toString());
1234                 error = "Invalid Configuration (Invalid PortGroup Name)";
1235                 config.setStatus(error);
1236                 return new Status(StatusCode.BADREQUEST, error);
1237             }
1238         }
1239
1240         /*
1241          * If requested program the entry in hardware first before updating the
1242          * StaticFlow DB
1243          */
1244         if (!multipleFlowPush) {
1245             // Program hw
1246             if (config.installInHw()) {
1247                 FlowEntry entry = config.getFlowEntry();
1248                 status = this.addEntry(entry);
1249                 if (!status.isSuccess()) {
1250                     config.setStatus(status.getDescription());
1251                     if (!restore) {
1252                         return status;
1253                     }
1254                 }
1255             }
1256         }
1257
1258         /*
1259          * When the control reaches this point, either of the following
1260          * conditions is true 1. This is a single entry configuration (non
1261          * PortGroup) and the hardware installation is successful 2. This is a
1262          * multiple entry configuration (PortGroup) and hardware installation is
1263          * NOT done directly on this event. 3. The User prefers to retain the
1264          * configuration in Controller and skip hardware installation.
1265          *
1266          * Hence it is safe to update the StaticFlow DB at this point.
1267          *
1268          * Note : For the case of PortGrouping, it is essential to have this DB
1269          * populated before the PortGroupListeners can query for the DB
1270          * triggered using portGroupChanged event...
1271          */
1272         Integer ordinal = staticFlowsOrdinal.get(0);
1273         staticFlowsOrdinal.put(0, ++ordinal);
1274         staticFlows.put(ordinal, config);
1275
1276         if (multipleFlowPush) {
1277             PortGroupConfig pgconfig = portGroupConfigs.get(config
1278                     .getPortGroup());
1279             Map<Node, PortGroup> existingData = portGroupData.get(pgconfig);
1280             if (existingData != null) {
1281                 portGroupChanged(pgconfig, existingData, true);
1282             }
1283         }
1284         return new Status(StatusCode.SUCCESS, null);
1285     }
1286
1287     private void addStaticFlowsToSwitch(Node node) {
1288         for (FlowConfig config : staticFlows.values()) {
1289             if (config.isPortGroupEnabled()) {
1290                 continue;
1291             }
1292             if (config.getNode().equals(node)) {
1293                 if (config.installInHw()
1294                         && !config.getStatus().equals(
1295                                         StatusCode.SUCCESS.toString())) {
1296                     Status status = this.addEntry(config.getFlowEntry());
1297                     config.setStatus(status.getDescription());
1298                 }
1299             }
1300         }
1301     }
1302
1303     private void updateStaticFlowConfigsOnNodeDown(Node node) {
1304         log.trace("Updating Static Flow configs on node down: " + node);
1305
1306         for (FlowConfig config : staticFlows.values()) {
1307             if (config.isPortGroupEnabled()) {
1308                 continue;
1309             }
1310             if (config.installInHw() && config.getNode().equals(node)) {
1311                 config.setStatus("Node is down");
1312             }
1313         }
1314     }
1315
1316     private void updateStaticFlowConfigsOnContainerModeChange(UpdateType update) {
1317         log.trace("Updating Static Flow configs on container mode change: "
1318                 + update);
1319
1320         for (FlowConfig config : staticFlows.values()) {
1321             if (config.isPortGroupEnabled()) {
1322                 continue;
1323             }
1324             if (config.installInHw()) {
1325                 switch (update) {
1326                 case ADDED:
1327                     config
1328                             .setStatus("Removed from node because in container mode");
1329                     break;
1330                 case REMOVED:
1331                     config.setStatus(StatusCode.SUCCESS.toString());
1332                     break;
1333                 default:
1334                 }
1335             }
1336         }
1337     }
1338
1339     public Status removeStaticFlow(FlowConfig config) {
1340         /*
1341          * No config.isInternal() check as NB does not take this path and GUI
1342          * cannot issue a delete on an internal generated flow. We need this path
1343          * to be accessible when switch mode is changed from proactive to
1344          * reactive, so that we can remove the internal generated LLDP and ARP
1345          * punt flows
1346          */
1347         for (Map.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1348             if (entry.getValue().isByNameAndNodeIdEqual(config)) {
1349                 // Program the network node
1350                 Status status = this.removeEntry(config.getFlowEntry());
1351                 // Update configuration database if programming was successful
1352                 if (status.isSuccess()) {
1353                     staticFlows.remove(entry.getKey());
1354                     return status;
1355                 } else {
1356                     entry.getValue().setStatus(status.getDescription());
1357                     return status;
1358                 }
1359             }
1360         }
1361         return new Status(StatusCode.NOTFOUND, "Entry Not Present");
1362     }
1363
1364     @Override
1365     public Status removeStaticFlow(String name, Node node) {
1366         for (Map.Entry<Integer, FlowConfig> mapEntry : staticFlows.entrySet()) {
1367             FlowConfig entry = mapEntry.getValue();
1368             Status status = new Status(null,null);
1369             if (entry.isByNameAndNodeIdEqual(name, node)) {
1370                 // Validity check for api3 entry point
1371                 if (entry.isInternalFlow()) {
1372                         String msg = "Invalid operation: Controller generated " +
1373                                         "flow cannot be deleted";
1374                         log.warn(msg);
1375                         return new Status(StatusCode.NOTACCEPTABLE, msg);
1376                 }
1377                 if (!entry.isPortGroupEnabled()) {
1378                     // Program the network node
1379                     status = this.removeEntry(entry.getFlowEntry());
1380                 }
1381                 // Update configuration database if programming was successful
1382                 if (status.isSuccess()) {
1383                     staticFlows.remove(mapEntry.getKey());
1384                     return status;
1385                 } else {
1386                     entry.setStatus(status.getDescription());
1387                     return status;
1388                 }
1389             }
1390         }
1391         return new Status(StatusCode.NOTFOUND, "Entry Not Present");
1392     }
1393
1394     public Status modifyStaticFlow(FlowConfig newFlowConfig) {
1395         // Validity check for api3 entry point
1396         if (newFlowConfig.isInternalFlow()) {
1397                 String msg = "Invalid operation: Controller generated flow " + 
1398                                         "cannot be modified";
1399                 log.warn(msg);
1400             return new Status(StatusCode.NOTACCEPTABLE, msg);
1401         }
1402
1403         // Validity Check
1404         StringBuffer resultStr = new StringBuffer();
1405         if (!newFlowConfig.isValid(container, resultStr)) {
1406             String msg = "Invalid Configuration (" + resultStr.toString()
1407                     + ")";
1408             newFlowConfig.setStatus(msg);
1409             log.warn(msg);
1410             return new Status(StatusCode.BADREQUEST, msg);
1411         }
1412
1413         FlowConfig oldFlowConfig = null;
1414         Integer index = null;
1415         for (Map.Entry<Integer, FlowConfig> mapEntry : staticFlows.entrySet()) {
1416             FlowConfig entry = mapEntry.getValue();
1417             if (entry.isByNameAndNodeIdEqual(newFlowConfig.getName(),
1418                     newFlowConfig.getNode())) {
1419                 oldFlowConfig = entry;
1420                 index = mapEntry.getKey();
1421                 break;
1422             }
1423         }
1424
1425         if (oldFlowConfig == null) {
1426                 String msg = "Attempt to modify a non existing static flow";
1427                 log.warn(msg);
1428                 return new Status(StatusCode.NOTFOUND, msg);
1429         }
1430
1431         // Do not attempt to reinstall the flow, warn user
1432         if (newFlowConfig.equals(oldFlowConfig)) {
1433                 String msg = "No modification detected";
1434                 log.info("Static flow modification skipped: " + msg);
1435             return new Status(StatusCode.SUCCESS, msg);
1436         }
1437
1438         // If flow is installed, program the network node
1439         Status status = new Status(StatusCode.SUCCESS, "Saved in config");
1440         if (oldFlowConfig.installInHw()) {
1441                 status = this.modifyEntry(oldFlowConfig.getFlowEntry(),
1442                     newFlowConfig.getFlowEntry());
1443         }
1444
1445         // Update configuration database if programming was successful
1446         if (status.isSuccess()) {
1447             newFlowConfig.setStatus(status.getDescription());
1448             staticFlows.put(index, newFlowConfig);
1449         }
1450
1451         return status;
1452     }
1453
1454
1455
1456         @Override
1457         public Status toggleStaticFlowStatus(String name, Node node) {
1458                 return toggleStaticFlowStatus(getStaticFlow(name, node));
1459         }
1460         
1461     @Override
1462     public Status toggleStaticFlowStatus(FlowConfig config) {
1463         if (config == null) {
1464                 String msg = "Invalid request: null flow config";
1465                 log.warn(msg);
1466                 return new Status(StatusCode.BADREQUEST, msg);
1467         }
1468         // Validity check for api3 entry point
1469         if (config.isInternalFlow()) {
1470                 String msg = "Invalid operation: Controller generated flow " +
1471                                 "cannot be modified";
1472                 log.warn(msg);
1473                 return new Status(StatusCode.NOTACCEPTABLE, msg);
1474         }
1475
1476         for (Map.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1477             FlowConfig conf = entry.getValue();
1478             if (conf.isByNameAndNodeIdEqual(config)) {
1479                 // Program the network node
1480                 Status status = new Status(StatusCode.SUCCESS, null);
1481                 if (conf.installInHw()) {
1482                     status = this.removeEntry(conf.getFlowEntry());
1483                 } else {
1484                     status = this.addEntry(conf.getFlowEntry());
1485                 }
1486                 if (!status.isSuccess()) {
1487                     conf.setStatus(status.getDescription());
1488                     return status;
1489                 }
1490
1491                 // Update Configuration database
1492                 conf.setStatus(StatusCode.SUCCESS.toString());
1493                 conf.toggleStatus();
1494                 return status;
1495             }
1496         }
1497         return new Status(StatusCode.NOTFOUND,
1498                         "Unable to locate the entry. Failed to toggle status");
1499     }
1500
1501     /**
1502      * Uninstall all the Flow Entries present in the software view
1503      * A copy of each entry is stored in the inactive list so
1504      * that it can be re-applied when needed
1505      * This function is called on the default container instance of FRM only
1506      * when the first container is created
1507      */
1508     private void uninstallAllFlowEntries() {
1509         log.info("Uninstalling all flows");
1510
1511         // Store entries / create target list
1512         for (ConcurrentMap.Entry<Node, Set<FlowEntryInstall>> mapEntry : nodeFlows
1513                 .entrySet()) {
1514             for (FlowEntryInstall flowEntries : mapEntry.getValue()) {
1515                 inactiveFlows.add(flowEntries.getOriginal());
1516             }
1517         }
1518
1519         // Now remove the entries
1520         for (FlowEntry flowEntry : inactiveFlows) {
1521             Status status = this.removeEntry(flowEntry);
1522             if (!status.isSuccess()) {
1523                 log.warn("Failed to remove entry: {}: " + 
1524                                 status.getDescription(), flowEntry);
1525             }
1526         }
1527     }
1528
1529     /**
1530      * Re-install all the Flow Entries present in the inactive list
1531      * The inactive list will be empty at the end of this call
1532      * This function is called on the default container instance of FRM only
1533      * when the last container is deleted
1534      */
1535     private void reinstallAllFlowEntries() {
1536         log.info("Reinstalling all inactive flows");
1537
1538         for (FlowEntry flowEntry : this.inactiveFlows) {
1539                 Status status = this.addEntry(flowEntry);
1540             if (!status.isSuccess()) {
1541                 log.warn("Failed to install entry: {}: " + 
1542                                 status.getDescription(), flowEntry);
1543             }
1544         }
1545
1546         // Empty inactive list in any case
1547         inactiveFlows.clear();
1548     }
1549
1550     public List<FlowConfig> getStaticFlows() {
1551         return getStaticFlowsOrderedList(staticFlows, staticFlowsOrdinal.get(0)
1552                 .intValue());
1553     }
1554
1555     // TODO: need to come out with a better algorithm for mantaining the order
1556     // of the configuration entries
1557     // with actual one, index associated to deleted entries cannot be reused and
1558     // map grows...
1559     private List<FlowConfig> getStaticFlowsOrderedList(
1560             ConcurrentMap<Integer, FlowConfig> flowMap, int maxKey) {
1561         List<FlowConfig> orderedList = new ArrayList<FlowConfig>();
1562         for (int i = 0; i <= maxKey; i++) {
1563             FlowConfig entry = flowMap.get(i);
1564             if (entry != null) {
1565                 orderedList.add(entry);
1566             }
1567         }
1568         return orderedList;
1569     }
1570
1571     @Override
1572     public FlowConfig getStaticFlow(String name, Node node) {
1573         for (FlowConfig config : staticFlows.values()) {
1574             if (config.isByNameAndNodeIdEqual(name, node)) {
1575                 return config;
1576             }
1577         }
1578         return null;
1579     }
1580
1581     @Override
1582     public List<FlowConfig> getStaticFlows(Node node) {
1583         List<FlowConfig> list = new ArrayList<FlowConfig>();
1584         for (FlowConfig config : staticFlows.values()) {
1585             if (config.onNode(node)) {
1586                 list.add(config);
1587             }
1588         }
1589         return list;
1590     }
1591
1592     @Override
1593     public List<String> getStaticFlowNamesForNode(Node node) {
1594         List<String> list = new ArrayList<String>();
1595         for (FlowConfig config : staticFlows.values()) {
1596             if (config.onNode(node)) {
1597                 list.add(config.getName());
1598             }
1599         }
1600         return list;
1601     }
1602
1603     @Override
1604     public List<Node> getListNodeWithConfiguredFlows() {
1605         Set<Node> set = new HashSet<Node>();
1606         for (FlowConfig config : staticFlows.values()) {
1607             set.add(config.getNode());
1608         }
1609         return new ArrayList<Node>(set);
1610     }
1611
1612     @SuppressWarnings("unchecked")
1613     private void loadFlowConfiguration() {
1614         ObjectReader objReader = new ObjectReader();
1615         ConcurrentMap<Integer, FlowConfig> confList = (ConcurrentMap<Integer, FlowConfig>) objReader
1616                 .read(this, frmFileName);
1617
1618         ConcurrentMap<String, PortGroupConfig> pgConfig = (ConcurrentMap<String, PortGroupConfig>) objReader
1619                 .read(this, portGroupFileName);
1620
1621         if (pgConfig != null) {
1622             for (Map.Entry<String, PortGroupConfig> entry : pgConfig.entrySet()) {
1623                 addPortGroupConfig(entry.getKey(), entry.getValue()
1624                         .getMatchString(), true);
1625             }
1626         }
1627
1628         if (confList == null) {
1629             return;
1630         }
1631
1632         int maxKey = 0;
1633         for (Integer key : confList.keySet()) {
1634             if (key.intValue() > maxKey)
1635                 maxKey = key.intValue();
1636         }
1637
1638         for (FlowConfig conf : getStaticFlowsOrderedList(confList, maxKey)) {
1639             addStaticFlow(conf, true);
1640         }
1641     }
1642
1643     @Override
1644     public Object readObject(ObjectInputStream ois)
1645             throws FileNotFoundException, IOException, ClassNotFoundException {
1646         return ois.readObject();
1647     }
1648
1649     public Status saveConfig() {
1650         // Publish the save config event to the cluster nodes
1651         flowsSaveEvent.put(new Date().getTime(), SAVE);
1652         return saveConfigInternal();
1653     }
1654
1655     private Status saveConfigInternal() {
1656         ObjectWriter objWriter = new ObjectWriter();
1657         ConcurrentHashMap<Integer, FlowConfig> nonDynamicFlows = new ConcurrentHashMap<Integer, FlowConfig>();
1658         for (Integer ordinal : staticFlows.keySet()) {
1659             FlowConfig config = staticFlows.get(ordinal);
1660             if (config.isDynamic())
1661                 continue;
1662             nonDynamicFlows.put(ordinal, config);
1663         }
1664         objWriter.write(nonDynamicFlows, frmFileName);
1665         objWriter.write(new ConcurrentHashMap<String, PortGroupConfig>(
1666                 portGroupConfigs), portGroupFileName);
1667         return new Status(StatusCode.SUCCESS, null);
1668     }
1669
1670     @Override
1671     public void entryCreated(Long key, String cacheName, boolean local) {
1672     }
1673
1674     @Override
1675     public void entryUpdated(Long key, String new_value, String cacheName,
1676             boolean originLocal) {
1677         saveConfigInternal();
1678     }
1679
1680     @Override
1681     public void entryDeleted(Long key, String cacheName, boolean originLocal) {
1682     }
1683
1684     @Override
1685     public void subnetNotify(Subnet sub, boolean add) {
1686     }
1687
1688     private void installImplicitARPReplyPunt(Node node) {
1689
1690         if (node == null) {
1691             return;
1692         }
1693
1694         List<String> puntAction = new ArrayList<String>();
1695         puntAction.add(ActionType.CONTROLLER.toString());
1696
1697         FlowConfig allowARP = new FlowConfig();
1698         allowARP.setInstallInHw(true);
1699         allowARP.setName("**Punt ARP Reply");
1700         allowARP.setPriority("500");
1701         allowARP.setNode(node);
1702         allowARP.setEtherType("0x"
1703                 + Integer.toHexString(EtherTypes.ARP.intValue()).toUpperCase());
1704         allowARP.setDstMac(HexEncode.bytesToHexString(switchManager
1705                 .getControllerMAC()));
1706         allowARP.setActions(puntAction);
1707         addStaticFlow(allowARP, false);
1708     }
1709
1710     @Override
1711     public void modeChangeNotify(Node node, boolean proactive) {
1712         List<FlowConfig> defaultConfigs = new ArrayList<FlowConfig>();
1713
1714         List<String> puntAction = new ArrayList<String>();
1715         puntAction.add(ActionType.CONTROLLER.toString());
1716
1717         FlowConfig allowARP = new FlowConfig();
1718         allowARP.setInstallInHw(true);
1719         allowARP.setName("**Punt ARP");
1720         allowARP.setPriority("1");
1721         allowARP.setNode(node);
1722         allowARP.setEtherType("0x"
1723                 + Integer.toHexString(EtherTypes.ARP.intValue()).toUpperCase());
1724         allowARP.setActions(puntAction);
1725         defaultConfigs.add(allowARP);
1726
1727         FlowConfig allowLLDP = new FlowConfig();
1728         allowLLDP.setInstallInHw(true);
1729         allowLLDP.setName("**Punt LLDP");
1730         allowLLDP.setPriority("1");
1731         allowLLDP.setNode(node);
1732         allowLLDP.setEtherType("0x"
1733                                + Integer.toHexString(EtherTypes.LLDP.intValue())
1734                                 .toUpperCase());
1735         allowLLDP.setActions(puntAction);
1736         defaultConfigs.add(allowLLDP);
1737
1738         List<String> dropAction = new ArrayList<String>();
1739         dropAction.add(ActionType.DROP.toString());
1740
1741         FlowConfig dropAllConfig = new FlowConfig();
1742         dropAllConfig.setInstallInHw(true);
1743         dropAllConfig.setName("**Catch-All Drop");
1744         dropAllConfig.setPriority("0");
1745         dropAllConfig.setNode(node);
1746         dropAllConfig.setActions(dropAction);
1747         defaultConfigs.add(dropAllConfig);
1748
1749         for (FlowConfig fc : defaultConfigs) {
1750             if (proactive) {
1751                 addStaticFlow(fc, false);
1752             } else {
1753                 removeStaticFlow(fc);
1754             }
1755         }
1756
1757         log.info("Set Switch {} Mode to {}", node, proactive);
1758     }
1759
1760     /**
1761      * Remove from the databases all the flows installed on the node
1762      *
1763      * @param node
1764      */
1765     private synchronized void cleanDatabaseForNode(Node node) {
1766         log.info("Cleaning Flow database for Node " + node.toString());
1767
1768         // Find out which groups the node's flows are part of
1769         Set<String> affectedGroups = new HashSet<String>();
1770         Set<FlowEntryInstall> flowEntryList = nodeFlows.get(node);
1771         if (flowEntryList != null) {
1772             for (FlowEntryInstall entry : flowEntryList) {
1773                 String groupName = entry.getGroupName();
1774                 if (groupName != null) {
1775                     affectedGroups.add(groupName);
1776                 }
1777             }
1778         }
1779
1780         // Remove the node's flows from the group indexed flow database
1781         if (!affectedGroups.isEmpty()) {
1782             for (String group : affectedGroups) {
1783                 Set<FlowEntryInstall> flowList = groupFlows.get(group);
1784                 Set<FlowEntryInstall> toRemove = new HashSet<FlowEntryInstall>();
1785                 for (FlowEntryInstall entry : flowList) {
1786                     if (node.equals(entry.getNode())) {
1787                         toRemove.add(entry);
1788                     }
1789                 }
1790                 flowList.removeAll(toRemove);
1791                 if (flowList.isEmpty()) {
1792                     groupFlows.remove(group);
1793                 }
1794             }
1795         }
1796
1797         // Remove the node's flows from the node indexed flow database
1798         nodeFlows.remove(node);
1799     }
1800
1801     @Override
1802     public void notifyNode(Node node, UpdateType type,
1803             Map<String, Property> propMap) {
1804         switch (type) {
1805         case ADDED:
1806             addStaticFlowsToSwitch(node);
1807             break;
1808         case REMOVED:
1809             cleanDatabaseForNode(node);
1810             updateStaticFlowConfigsOnNodeDown(node);
1811             break;
1812         default:
1813             break;
1814         }
1815     }
1816
1817     @Override
1818     public void notifyNodeConnector(NodeConnector nodeConnector,
1819             UpdateType type, Map<String, Property> propMap) {
1820     }
1821
1822     private FlowConfig getDerivedFlowConfig(FlowConfig original,
1823             String configName, Short port) {
1824         FlowConfig derivedFlow = new FlowConfig(original);
1825         derivedFlow.setDynamic(true);
1826         derivedFlow.setPortGroup(null);
1827         derivedFlow.setName(original.getName() + "_" + configName + "_" + port);
1828         derivedFlow.setIngressPort(port + "");
1829         return derivedFlow;
1830     }
1831
1832     private void addPortGroupFlows(PortGroupConfig config, Node node,
1833             PortGroup data) {
1834         for (Iterator<FlowConfig> it = staticFlows.values().iterator(); it
1835                 .hasNext();) {
1836             FlowConfig staticFlow = it.next();
1837             if (staticFlow.getPortGroup() == null) {
1838                 continue;
1839             }
1840             if ((staticFlow.getNode().equals(node))
1841                     && (staticFlow.getPortGroup().equals(config.getName()))) {
1842                 for (Short port : data.getPorts()) {
1843                     FlowConfig derivedFlow = getDerivedFlowConfig(staticFlow,
1844                             config.getName(), port);
1845                     addStaticFlow(derivedFlow, false);
1846                 }
1847             }
1848         }
1849     }
1850
1851     private void removePortGroupFlows(PortGroupConfig config, Node node,
1852             PortGroup data) {
1853         for (Iterator<FlowConfig> it = staticFlows.values().iterator(); it
1854                 .hasNext();) {
1855             FlowConfig staticFlow = it.next();
1856             if (staticFlow.getPortGroup() == null) {
1857                 continue;
1858             }
1859             if ((staticFlow.getNode().equals(node))
1860                     && (staticFlow.getPortGroup().equals(config.getName()))) {
1861                 for (Short port : data.getPorts()) {
1862                     FlowConfig derivedFlow = getDerivedFlowConfig(staticFlow,
1863                             config.getName(), port);
1864                     removeStaticFlow(derivedFlow);
1865                 }
1866             }
1867         }
1868     }
1869
1870     @Override
1871     public void portGroupChanged(PortGroupConfig config,
1872             Map<Node, PortGroup> data, boolean add) {
1873         log.info("PortGroup Changed for :" + config + " Data: "
1874                  + portGroupData);
1875         Map<Node, PortGroup> existingData = portGroupData.get(config);
1876         if (existingData != null) {
1877             for (Map.Entry<Node, PortGroup> entry : data.entrySet()) {
1878                 PortGroup existingPortGroup = existingData.get(entry.getKey());
1879                 if (existingPortGroup == null) {
1880                     if (add) {
1881                         existingData.put(entry.getKey(), entry.getValue());
1882                         addPortGroupFlows(config, entry.getKey(), entry
1883                                 .getValue());
1884                     }
1885                 } else {
1886                     if (add) {
1887                         existingPortGroup.getPorts().addAll(
1888                                 entry.getValue().getPorts());
1889                         addPortGroupFlows(config, entry.getKey(), entry
1890                                 .getValue());
1891                     } else {
1892                         existingPortGroup.getPorts().removeAll(
1893                                 entry.getValue().getPorts());
1894                         removePortGroupFlows(config, entry.getKey(), entry
1895                                 .getValue());
1896                     }
1897                 }
1898             }
1899         } else {
1900             if (add) {
1901                 portGroupData.put(config, data);
1902                 for (Node swid : data.keySet()) {
1903                     addPortGroupFlows(config, swid, data.get(swid));
1904                 }
1905             }
1906         }
1907     }
1908
1909     public boolean addPortGroupConfig(String name, String regex, boolean restore) {
1910         PortGroupConfig config = portGroupConfigs.get(name);
1911         if (config != null)
1912             return false;
1913
1914         if ((portGroupProvider == null) && !restore) {
1915             return false;
1916         }
1917         if ((portGroupProvider != null)
1918                 && (!portGroupProvider.isMatchCriteriaSupported(regex))) {
1919             return false;
1920         }
1921
1922         config = new PortGroupConfig(name, regex);
1923         portGroupConfigs.put(name, config);
1924         if (portGroupProvider != null) {
1925             portGroupProvider.createPortGroupConfig(config);
1926         }
1927         return true;
1928     }
1929
1930     public boolean delPortGroupConfig(String name) {
1931         PortGroupConfig config = portGroupConfigs.get(name);
1932         if (config == null) {
1933             return false;
1934         }
1935
1936         if (portGroupProvider != null) {
1937             portGroupProvider.deletePortGroupConfig(config);
1938         }
1939         portGroupConfigs.remove(name);
1940         return true;
1941     }
1942
1943     private void usePortGroupConfig(String name) {
1944         PortGroupConfig config = portGroupConfigs.get(name);
1945         if (config == null) {
1946             return;
1947         }
1948         if (portGroupProvider != null) {
1949             Map<Node, PortGroup> data = portGroupProvider
1950                     .getPortGroupData(config);
1951             portGroupData.put(config, data);
1952         }
1953     }
1954
1955     @Override
1956     public Map<String, PortGroupConfig> getPortGroupConfigs() {
1957         return portGroupConfigs;
1958     }
1959
1960     public boolean isPortGroupSupported() {
1961         if (portGroupProvider == null) {
1962             return false;
1963         }
1964         return true;
1965     }
1966
1967     // Fir PortGroupProvider to use regular Dependency Manager
1968     /* @SuppressWarnings("rawtypes") */
1969     /* public void bind(Object arg0, Map arg1) throws Exception { */
1970     /* if (arg0 instanceof PortGroupProvider) { */
1971     /* setPortGroupProvider((PortGroupProvider)arg0); */
1972     /* } */
1973     /* } */
1974
1975     /* @SuppressWarnings("rawtypes") */
1976     /* @Override */
1977     /* public void unbind(Object arg0, Map arg1) throws Exception { */
1978     /* if (arg0 instanceof PortGroupProvider) { */
1979     /* portGroupProvider = null; */
1980     /* } */
1981     /* } */
1982
1983     public void setIContainer(IContainer s) {
1984         this.container = s;
1985     }
1986
1987     public void unsetIContainer(IContainer s) {
1988         if (this.container == s) {
1989             this.container = null;
1990         }
1991     }
1992
1993     public PortGroupProvider getPortGroupProvider() {
1994         return portGroupProvider;
1995     }
1996
1997     public void unsetPortGroupProvider(PortGroupProvider portGroupProvider) {
1998         this.portGroupProvider = null;
1999     }
2000
2001     public void setPortGroupProvider(PortGroupProvider portGroupProvider) {
2002         this.portGroupProvider = portGroupProvider;
2003         portGroupProvider.registerPortGroupChange(this);
2004         for (PortGroupConfig config : portGroupConfigs.values()) {
2005             portGroupProvider.createPortGroupConfig(config);
2006         }
2007     }
2008
2009     public void setHostFinder(IfIptoHost hostFinder) {
2010         this.hostFinder = hostFinder;
2011     }
2012
2013     public void unsetHostFinder(IfIptoHost hostFinder) {
2014         if (this.hostFinder == hostFinder) {
2015             this.hostFinder = null;
2016         }
2017     }
2018
2019     public void setFrmAware(IForwardingRulesManagerAware obj) {
2020         this.frmAware.add(obj);
2021     }
2022
2023     public void unsetFrmAware(IForwardingRulesManagerAware obj) {
2024         this.frmAware.remove(obj);
2025     }
2026
2027     void setClusterContainerService(IClusterContainerServices s) {
2028         log.debug("Cluster Service set");
2029         this.clusterContainerService = s;
2030     }
2031
2032     void unsetClusterContainerService(IClusterContainerServices s) {
2033         if (this.clusterContainerService == s) {
2034             log.debug("Cluster Service removed!");
2035             this.clusterContainerService = null;
2036         }
2037     }
2038
2039     private String getContainerName() {
2040         if (container == null) {
2041             return GlobalConstants.DEFAULT.toString();
2042         }
2043         return container.getName();
2044     }
2045
2046     /**
2047      * Function called by the dependency manager when all the required
2048      * dependencies are satisfied
2049      *
2050      */
2051     void init() {
2052         log.info("Init");
2053         frmAware = Collections
2054                 .synchronizedSet(new HashSet<IForwardingRulesManagerAware>());
2055         frmFileName = GlobalConstants.STARTUPHOME.toString() + "frm_staticflows_"
2056                 + this.getContainerName() + ".conf";
2057         portGroupFileName = GlobalConstants.STARTUPHOME.toString() + "portgroup_"
2058                 + this.getContainerName() + ".conf";
2059
2060         inContainerMode = false;
2061
2062         if (portGroupProvider != null) {
2063             portGroupProvider.registerPortGroupChange(this);
2064         }
2065
2066         nonClusterObjectCreate();
2067
2068         cacheStartup();
2069
2070         registerWithOSGIConsole();
2071
2072         /*
2073          * If we are not the first cluster node to come up, do not initialize
2074          * the static flow entries ordinal
2075          */
2076         if (staticFlowsOrdinal.size() == 0) {
2077             staticFlowsOrdinal.put(0, Integer.valueOf(0));
2078         }
2079     }
2080
2081     /**
2082      * Function called by the dependency manager when at least one dependency
2083      * become unsatisfied or when the component is shutting down because for
2084      * example bundle is being stopped.
2085      *
2086      */
2087     void destroy() {
2088         log.info("Destroy");
2089         destroyCaches();
2090     }
2091
2092     /**
2093      * Function called by dependency manager after "init ()" is called and after
2094      * the services provided by the class are registered in the service registry
2095      *
2096      */
2097     void start() {
2098         log.info("Start");
2099         /*
2100          * Read startup and build database if we have not already gotten the
2101          * configurations synced from another node
2102          */
2103         if (staticFlows.isEmpty()) {
2104             loadFlowConfiguration();
2105         }
2106     }
2107
2108     /**
2109      * Function called by the dependency manager before the services exported by
2110      * the component are unregistered, this will be followed by a "destroy ()"
2111      * calls
2112      *
2113      */
2114     void stop() {
2115         log.info("Stop");
2116     }
2117
2118     public void setFlowProgrammerService(IFlowProgrammerService service) {
2119         this.programmer = service;
2120     }
2121
2122     public void unsetFlowProgrammerService(IFlowProgrammerService service) {
2123         if (this.programmer == service) {
2124             this.programmer = null;
2125         }
2126     }
2127
2128     public void setSwitchManager(ISwitchManager switchManager) {
2129         this.switchManager = switchManager;
2130     }
2131
2132     public void unsetSwitchManager(ISwitchManager switchManager) {
2133         if (this.switchManager == switchManager) {
2134             this.switchManager = null;
2135         }
2136     }
2137
2138     @Override
2139     public void tagUpdated(String containerName, Node n, short oldTag,
2140             short newTag, UpdateType t) {
2141
2142     }
2143
2144     @Override
2145     public void containerFlowUpdated(String containerName,
2146             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
2147         /*
2148          * Whether it is an addition or removal, we have to recompute the
2149          * merged flows entries taking into account all the current container flows
2150          * because flow merging is not an injective function
2151          */
2152         updateFlowsContainerFlow();
2153     }
2154
2155     @Override
2156     public void nodeConnectorUpdated(String containerName, NodeConnector p,
2157             UpdateType t) {
2158         // No action
2159     }
2160
2161     @Override
2162     public void containerModeUpdated(UpdateType update) {
2163         switch (update) {
2164         case ADDED:
2165             this.inContainerMode = true;
2166             this.uninstallAllFlowEntries();
2167             break;
2168         case REMOVED:
2169             this.inContainerMode = false;
2170             this.reinstallAllFlowEntries();
2171             break;
2172         default:
2173         }
2174
2175         // Update our configuration DB
2176         updateStaticFlowConfigsOnContainerModeChange(update);
2177     }
2178
2179     /*
2180      * OSGI COMMANDS
2181      */
2182     @Override
2183     public String getHelp() {
2184         StringBuffer help = new StringBuffer();
2185         help.append("---FRM Matrix Application---\n");
2186         help.append("\t printMatrixData        - Prints the Matrix Configs\n");
2187         help.append("\t addMatrixConfig <name> <regex>\n");
2188         help.append("\t delMatrixConfig <name>\n");
2189         help.append("\t useMatrixConfig <name>\n");
2190         return help.toString();
2191     }
2192
2193     public void _printMatrixData(CommandInterpreter ci) {
2194         ci.println("Configs : ");
2195         ci.println("---------");
2196         ci.println(portGroupConfigs);
2197
2198         ci.println("Data : ");
2199         ci.println("------");
2200         ci.println(portGroupData);
2201     }
2202
2203     public void _addMatrixConfig(CommandInterpreter ci) {
2204         String name = ci.nextArgument();
2205         String regex = ci.nextArgument();
2206         addPortGroupConfig(name, regex, false);
2207     }
2208
2209     public void _delMatrixConfig(CommandInterpreter ci) {
2210         String name = ci.nextArgument();
2211         delPortGroupConfig(name);
2212     }
2213
2214     public void _useMatrixConfig(CommandInterpreter ci) {
2215         String name = ci.nextArgument();
2216         usePortGroupConfig(name);
2217     }
2218
2219     public void _arpPunt(CommandInterpreter ci) {
2220         String switchId = ci.nextArgument();
2221         long swid = HexEncode.stringToLong(switchId);
2222         Node node = NodeCreator.createOFNode(swid);
2223         installImplicitARPReplyPunt(node);
2224     }
2225
2226     public void _frmaddflow(CommandInterpreter ci) throws UnknownHostException {
2227         Node node = null;
2228         String nodeId = ci.nextArgument();
2229         if (nodeId == null) {
2230             ci.print("Node id not specified");
2231             return;
2232         }
2233         try {
2234             node = NodeCreator.createOFNode(Long.valueOf(nodeId));
2235         } catch (NumberFormatException e) {
2236             e.printStackTrace();
2237         }
2238         ci.println(this.programmer.addFlow(node, getSampleFlow(node)));
2239     }
2240
2241     public void _frmremoveflow(CommandInterpreter ci)
2242             throws UnknownHostException {
2243         Node node = null;
2244         String nodeId = ci.nextArgument();
2245         if (nodeId == null) {
2246             ci.print("Node id not specified");
2247             return;
2248         }
2249         try {
2250             node = NodeCreator.createOFNode(Long.valueOf(nodeId));
2251         } catch (NumberFormatException e) {
2252             e.printStackTrace();
2253         }
2254         ci.println(this.programmer.removeFlow(node, getSampleFlow(node)));
2255     }
2256
2257     private Flow getSampleFlow(Node node) throws UnknownHostException {
2258         NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
2259                 (short) 24, node);
2260         NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
2261                 (short) 30, node);
2262         byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
2263                 (byte) 0x9a, (byte) 0xbc };
2264         byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
2265                 (byte) 0x5e, (byte) 0x6f };
2266         InetAddress srcIP = InetAddress.getByName("172.28.30.50");
2267         InetAddress dstIP = InetAddress.getByName("171.71.9.52");
2268         InetAddress ipMask = InetAddress.getByName("255.255.255.0");
2269         InetAddress ipMask2 = InetAddress.getByName("255.0.0.0");
2270         short ethertype = EtherTypes.IPv4.shortValue();
2271         short vlan = (short) 27;
2272         byte vlanPr = 3;
2273         Byte tos = 4;
2274         byte proto = IPProtocols.TCP.byteValue();
2275         short src = (short) 55000;
2276         short dst = 80;
2277
2278         /*
2279          * Create a SAL Flow aFlow
2280          */
2281         Match match = new Match();
2282         match.setField(MatchType.IN_PORT, port);
2283         match.setField(MatchType.DL_SRC, srcMac);
2284         match.setField(MatchType.DL_DST, dstMac);
2285         match.setField(MatchType.DL_TYPE, ethertype);
2286         match.setField(MatchType.DL_VLAN, vlan);
2287         match.setField(MatchType.DL_VLAN_PR, vlanPr);
2288         match.setField(MatchType.NW_SRC, srcIP, ipMask);
2289         match.setField(MatchType.NW_DST, dstIP, ipMask2);
2290         match.setField(MatchType.NW_TOS, tos);
2291         match.setField(MatchType.NW_PROTO, proto);
2292         match.setField(MatchType.TP_SRC, src);
2293         match.setField(MatchType.TP_DST, dst);
2294
2295         List<Action> actions = new ArrayList<Action>();
2296         actions.add(new Output(oport));
2297         actions.add(new PopVlan());
2298         actions.add(new Flood());
2299         actions.add(new Controller());
2300         return new Flow(match, actions);
2301     }
2302
2303     @Override
2304     public Status saveConfiguration() {
2305         return  saveConfig();
2306     }
2307
2308     public void _frmNodeFlows(CommandInterpreter ci) {
2309         boolean verbose = false;
2310         String verboseCheck = ci.nextArgument();
2311         if (verboseCheck != null) {
2312             verbose = verboseCheck.equals("true");
2313         }
2314
2315         // Dump per node database
2316         for (Entry<Node, Set<FlowEntryInstall>> entry : this.nodeFlows
2317                 .entrySet()) {
2318             Node node = entry.getKey();
2319             for (FlowEntryInstall flow : entry.getValue()) {
2320                 if (!verbose) {
2321                     ci.println(node + " " + flow.getFlowName());
2322                 } else {
2323                     ci.println(node + " " + flow.toString());
2324                 }
2325             }
2326         }
2327     }
2328
2329     public void _frmGroupFlows(CommandInterpreter ci) {
2330         boolean verbose = false;
2331         String verboseCheck = ci.nextArgument();
2332         if (verboseCheck != null) {
2333             verbose = verboseCheck.equals("true");
2334         }
2335
2336         // Dump per node database
2337         for (Entry<String, Set<FlowEntryInstall>> entry : this.groupFlows
2338                 .entrySet()) {
2339             String group = entry.getKey();
2340             ci.println("Group " + group + ":");
2341             for (FlowEntryInstall flow : entry.getValue()) {
2342                 if (!verbose) {
2343                     ci.println(flow.getNode() + " " + flow.getFlowName());
2344                 } else {
2345                     ci.println(flow.getNode() + " " + flow.toString());
2346                 }
2347             }
2348         }
2349     }
2350
2351 }