08d34127a5a58107b731ad6c6cc666853d30c478
[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     @Override
1455     public Status toggleStaticFlowStatus(FlowConfig config) {
1456         // Validity check for api3 entry point
1457         if (config.isInternalFlow()) {
1458                 String msg = "Invalid operation: Controller generated flow " +
1459                                 "cannot be modified";
1460                 log.warn(msg);
1461                 return new Status(StatusCode.NOTACCEPTABLE, msg);
1462         }
1463
1464         for (Map.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1465             FlowConfig conf = entry.getValue();
1466             if (conf.isByNameAndNodeIdEqual(config)) {
1467                 // Program the network node
1468                 Status status = new Status(StatusCode.SUCCESS, null);
1469                 if (conf.installInHw()) {
1470                     status = this.removeEntry(conf.getFlowEntry());
1471                 } else {
1472                     status = this.addEntry(conf.getFlowEntry());
1473                 }
1474                 if (!status.isSuccess()) {
1475                     conf.setStatus(status.getDescription());
1476                     return status;
1477                 }
1478
1479                 // Update Configuration database
1480                 conf.setStatus(StatusCode.SUCCESS.toString());
1481                 conf.toggleStatus();
1482                 return status;
1483             }
1484         }
1485         return new Status(StatusCode.NOTFOUND,
1486                         "Unable to locate the entry. Failed to toggle status");
1487     }
1488
1489     /**
1490      * Uninstall all the Flow Entries present in the software view
1491      * A copy of each entry is stored in the inactive list so
1492      * that it can be re-applied when needed
1493      * This function is called on the default container instance of FRM only
1494      * when the first container is created
1495      */
1496     private void uninstallAllFlowEntries() {
1497         log.info("Uninstalling all flows");
1498
1499         // Store entries / create target list
1500         for (ConcurrentMap.Entry<Node, Set<FlowEntryInstall>> mapEntry : nodeFlows
1501                 .entrySet()) {
1502             for (FlowEntryInstall flowEntries : mapEntry.getValue()) {
1503                 inactiveFlows.add(flowEntries.getOriginal());
1504             }
1505         }
1506
1507         // Now remove the entries
1508         for (FlowEntry flowEntry : inactiveFlows) {
1509             Status status = this.removeEntry(flowEntry);
1510             if (!status.isSuccess()) {
1511                 log.warn("Failed to remove entry: {}: " + 
1512                                 status.getDescription(), flowEntry);
1513             }
1514         }
1515     }
1516
1517     /**
1518      * Re-install all the Flow Entries present in the inactive list
1519      * The inactive list will be empty at the end of this call
1520      * This function is called on the default container instance of FRM only
1521      * when the last container is deleted
1522      */
1523     private void reinstallAllFlowEntries() {
1524         log.info("Reinstalling all inactive flows");
1525
1526         for (FlowEntry flowEntry : this.inactiveFlows) {
1527                 Status status = this.addEntry(flowEntry);
1528             if (!status.isSuccess()) {
1529                 log.warn("Failed to install entry: {}: " + 
1530                                 status.getDescription(), flowEntry);
1531             }
1532         }
1533
1534         // Empty inactive list in any case
1535         inactiveFlows.clear();
1536     }
1537
1538     public List<FlowConfig> getStaticFlows() {
1539         return getStaticFlowsOrderedList(staticFlows, staticFlowsOrdinal.get(0)
1540                 .intValue());
1541     }
1542
1543     // TODO: need to come out with a better algorithm for mantaining the order
1544     // of the configuration entries
1545     // with actual one, index associated to deleted entries cannot be reused and
1546     // map grows...
1547     private List<FlowConfig> getStaticFlowsOrderedList(
1548             ConcurrentMap<Integer, FlowConfig> flowMap, int maxKey) {
1549         List<FlowConfig> orderedList = new ArrayList<FlowConfig>();
1550         for (int i = 0; i <= maxKey; i++) {
1551             FlowConfig entry = flowMap.get(i);
1552             if (entry != null) {
1553                 orderedList.add(entry);
1554             }
1555         }
1556         return orderedList;
1557     }
1558
1559     @Override
1560     public FlowConfig getStaticFlow(String name, Node node) {
1561         for (FlowConfig config : staticFlows.values()) {
1562             if (config.isByNameAndNodeIdEqual(name, node)) {
1563                 return config;
1564             }
1565         }
1566         return null;
1567     }
1568
1569     @Override
1570     public List<FlowConfig> getStaticFlows(Node node) {
1571         List<FlowConfig> list = new ArrayList<FlowConfig>();
1572         for (FlowConfig config : staticFlows.values()) {
1573             if (config.onNode(node)) {
1574                 list.add(config);
1575             }
1576         }
1577         return list;
1578     }
1579
1580     @Override
1581     public List<String> getStaticFlowNamesForNode(Node node) {
1582         List<String> list = new ArrayList<String>();
1583         for (FlowConfig config : staticFlows.values()) {
1584             if (config.onNode(node)) {
1585                 list.add(config.getName());
1586             }
1587         }
1588         return list;
1589     }
1590
1591     @Override
1592     public List<Node> getListNodeWithConfiguredFlows() {
1593         Set<Node> set = new HashSet<Node>();
1594         for (FlowConfig config : staticFlows.values()) {
1595             set.add(config.getNode());
1596         }
1597         return new ArrayList<Node>(set);
1598     }
1599
1600     @SuppressWarnings("unchecked")
1601     private void loadFlowConfiguration() {
1602         ObjectReader objReader = new ObjectReader();
1603         ConcurrentMap<Integer, FlowConfig> confList = (ConcurrentMap<Integer, FlowConfig>) objReader
1604                 .read(this, frmFileName);
1605
1606         ConcurrentMap<String, PortGroupConfig> pgConfig = (ConcurrentMap<String, PortGroupConfig>) objReader
1607                 .read(this, portGroupFileName);
1608
1609         if (pgConfig != null) {
1610             for (Map.Entry<String, PortGroupConfig> entry : pgConfig.entrySet()) {
1611                 addPortGroupConfig(entry.getKey(), entry.getValue()
1612                         .getMatchString(), true);
1613             }
1614         }
1615
1616         if (confList == null) {
1617             return;
1618         }
1619
1620         int maxKey = 0;
1621         for (Integer key : confList.keySet()) {
1622             if (key.intValue() > maxKey)
1623                 maxKey = key.intValue();
1624         }
1625
1626         for (FlowConfig conf : getStaticFlowsOrderedList(confList, maxKey)) {
1627             addStaticFlow(conf, true);
1628         }
1629     }
1630
1631     @Override
1632     public Object readObject(ObjectInputStream ois)
1633             throws FileNotFoundException, IOException, ClassNotFoundException {
1634         return ois.readObject();
1635     }
1636
1637     public Status saveConfig() {
1638         // Publish the save config event to the cluster nodes
1639         flowsSaveEvent.put(new Date().getTime(), SAVE);
1640         return saveConfigInternal();
1641     }
1642
1643     private Status saveConfigInternal() {
1644         ObjectWriter objWriter = new ObjectWriter();
1645         ConcurrentHashMap<Integer, FlowConfig> nonDynamicFlows = new ConcurrentHashMap<Integer, FlowConfig>();
1646         for (Integer ordinal : staticFlows.keySet()) {
1647             FlowConfig config = staticFlows.get(ordinal);
1648             if (config.isDynamic())
1649                 continue;
1650             nonDynamicFlows.put(ordinal, config);
1651         }
1652         objWriter.write(nonDynamicFlows, frmFileName);
1653         objWriter.write(new ConcurrentHashMap<String, PortGroupConfig>(
1654                 portGroupConfigs), portGroupFileName);
1655         return new Status(StatusCode.SUCCESS, null);
1656     }
1657
1658     @Override
1659     public void entryCreated(Long key, String cacheName, boolean local) {
1660     }
1661
1662     @Override
1663     public void entryUpdated(Long key, String new_value, String cacheName,
1664             boolean originLocal) {
1665         saveConfigInternal();
1666     }
1667
1668     @Override
1669     public void entryDeleted(Long key, String cacheName, boolean originLocal) {
1670     }
1671
1672     @Override
1673     public void subnetNotify(Subnet sub, boolean add) {
1674     }
1675
1676     private void installImplicitARPReplyPunt(Node node) {
1677
1678         if (node == null) {
1679             return;
1680         }
1681
1682         List<String> puntAction = new ArrayList<String>();
1683         puntAction.add(ActionType.CONTROLLER.toString());
1684
1685         FlowConfig allowARP = new FlowConfig();
1686         allowARP.setInstallInHw(true);
1687         allowARP.setName("**Punt ARP Reply");
1688         allowARP.setPriority("500");
1689         allowARP.setNode(node);
1690         allowARP.setEtherType("0x"
1691                 + Integer.toHexString(EtherTypes.ARP.intValue()).toUpperCase());
1692         allowARP.setDstMac(HexEncode.bytesToHexString(switchManager
1693                 .getControllerMAC()));
1694         allowARP.setActions(puntAction);
1695         addStaticFlow(allowARP, false);
1696     }
1697
1698     @Override
1699     public void modeChangeNotify(Node node, boolean proactive) {
1700         List<FlowConfig> defaultConfigs = new ArrayList<FlowConfig>();
1701
1702         List<String> puntAction = new ArrayList<String>();
1703         puntAction.add(ActionType.CONTROLLER.toString());
1704
1705         FlowConfig allowARP = new FlowConfig();
1706         allowARP.setInstallInHw(true);
1707         allowARP.setName("**Punt ARP");
1708         allowARP.setPriority("1");
1709         allowARP.setNode(node);
1710         allowARP.setEtherType("0x"
1711                 + Integer.toHexString(EtherTypes.ARP.intValue()).toUpperCase());
1712         allowARP.setActions(puntAction);
1713         defaultConfigs.add(allowARP);
1714
1715         FlowConfig allowLLDP = new FlowConfig();
1716         allowLLDP.setInstallInHw(true);
1717         allowLLDP.setName("**Punt LLDP");
1718         allowLLDP.setPriority("1");
1719         allowLLDP.setNode(node);
1720         allowLLDP.setEtherType("0x"
1721                                + Integer.toHexString(EtherTypes.LLDP.intValue())
1722                                 .toUpperCase());
1723         allowLLDP.setActions(puntAction);
1724         defaultConfigs.add(allowLLDP);
1725
1726         List<String> dropAction = new ArrayList<String>();
1727         dropAction.add(ActionType.DROP.toString());
1728
1729         FlowConfig dropAllConfig = new FlowConfig();
1730         dropAllConfig.setInstallInHw(true);
1731         dropAllConfig.setName("**Catch-All Drop");
1732         dropAllConfig.setPriority("0");
1733         dropAllConfig.setNode(node);
1734         dropAllConfig.setActions(dropAction);
1735         defaultConfigs.add(dropAllConfig);
1736
1737         for (FlowConfig fc : defaultConfigs) {
1738             if (proactive) {
1739                 addStaticFlow(fc, false);
1740             } else {
1741                 removeStaticFlow(fc);
1742             }
1743         }
1744
1745         log.info("Set Switch {} Mode to {}", node, proactive);
1746     }
1747
1748     /**
1749      * Remove from the databases all the flows installed on the node
1750      *
1751      * @param node
1752      */
1753     private synchronized void cleanDatabaseForNode(Node node) {
1754         log.info("Cleaning Flow database for Node " + node.toString());
1755
1756         // Find out which groups the node's flows are part of
1757         Set<String> affectedGroups = new HashSet<String>();
1758         Set<FlowEntryInstall> flowEntryList = nodeFlows.get(node);
1759         if (flowEntryList != null) {
1760             for (FlowEntryInstall entry : flowEntryList) {
1761                 String groupName = entry.getGroupName();
1762                 if (groupName != null) {
1763                     affectedGroups.add(groupName);
1764                 }
1765             }
1766         }
1767
1768         // Remove the node's flows from the group indexed flow database
1769         if (!affectedGroups.isEmpty()) {
1770             for (String group : affectedGroups) {
1771                 Set<FlowEntryInstall> flowList = groupFlows.get(group);
1772                 Set<FlowEntryInstall> toRemove = new HashSet<FlowEntryInstall>();
1773                 for (FlowEntryInstall entry : flowList) {
1774                     if (node.equals(entry.getNode())) {
1775                         toRemove.add(entry);
1776                     }
1777                 }
1778                 flowList.removeAll(toRemove);
1779                 if (flowList.isEmpty()) {
1780                     groupFlows.remove(group);
1781                 }
1782             }
1783         }
1784
1785         // Remove the node's flows from the node indexed flow database
1786         nodeFlows.remove(node);
1787     }
1788
1789     @Override
1790     public void notifyNode(Node node, UpdateType type,
1791             Map<String, Property> propMap) {
1792         switch (type) {
1793         case ADDED:
1794             addStaticFlowsToSwitch(node);
1795             break;
1796         case REMOVED:
1797             cleanDatabaseForNode(node);
1798             updateStaticFlowConfigsOnNodeDown(node);
1799             break;
1800         default:
1801             break;
1802         }
1803     }
1804
1805     @Override
1806     public void notifyNodeConnector(NodeConnector nodeConnector,
1807             UpdateType type, Map<String, Property> propMap) {
1808     }
1809
1810     private FlowConfig getDerivedFlowConfig(FlowConfig original,
1811             String configName, Short port) {
1812         FlowConfig derivedFlow = new FlowConfig(original);
1813         derivedFlow.setDynamic(true);
1814         derivedFlow.setPortGroup(null);
1815         derivedFlow.setName(original.getName() + "_" + configName + "_" + port);
1816         derivedFlow.setIngressPort(port + "");
1817         return derivedFlow;
1818     }
1819
1820     private void addPortGroupFlows(PortGroupConfig config, Node node,
1821             PortGroup data) {
1822         for (Iterator<FlowConfig> it = staticFlows.values().iterator(); it
1823                 .hasNext();) {
1824             FlowConfig staticFlow = it.next();
1825             if (staticFlow.getPortGroup() == null) {
1826                 continue;
1827             }
1828             if ((staticFlow.getNode().equals(node))
1829                     && (staticFlow.getPortGroup().equals(config.getName()))) {
1830                 for (Short port : data.getPorts()) {
1831                     FlowConfig derivedFlow = getDerivedFlowConfig(staticFlow,
1832                             config.getName(), port);
1833                     addStaticFlow(derivedFlow, false);
1834                 }
1835             }
1836         }
1837     }
1838
1839     private void removePortGroupFlows(PortGroupConfig config, Node node,
1840             PortGroup data) {
1841         for (Iterator<FlowConfig> it = staticFlows.values().iterator(); it
1842                 .hasNext();) {
1843             FlowConfig staticFlow = it.next();
1844             if (staticFlow.getPortGroup() == null) {
1845                 continue;
1846             }
1847             if ((staticFlow.getNode().equals(node))
1848                     && (staticFlow.getPortGroup().equals(config.getName()))) {
1849                 for (Short port : data.getPorts()) {
1850                     FlowConfig derivedFlow = getDerivedFlowConfig(staticFlow,
1851                             config.getName(), port);
1852                     removeStaticFlow(derivedFlow);
1853                 }
1854             }
1855         }
1856     }
1857
1858     @Override
1859     public void portGroupChanged(PortGroupConfig config,
1860             Map<Node, PortGroup> data, boolean add) {
1861         log.info("PortGroup Changed for :" + config + " Data: "
1862                  + portGroupData);
1863         Map<Node, PortGroup> existingData = portGroupData.get(config);
1864         if (existingData != null) {
1865             for (Map.Entry<Node, PortGroup> entry : data.entrySet()) {
1866                 PortGroup existingPortGroup = existingData.get(entry.getKey());
1867                 if (existingPortGroup == null) {
1868                     if (add) {
1869                         existingData.put(entry.getKey(), entry.getValue());
1870                         addPortGroupFlows(config, entry.getKey(), entry
1871                                 .getValue());
1872                     }
1873                 } else {
1874                     if (add) {
1875                         existingPortGroup.getPorts().addAll(
1876                                 entry.getValue().getPorts());
1877                         addPortGroupFlows(config, entry.getKey(), entry
1878                                 .getValue());
1879                     } else {
1880                         existingPortGroup.getPorts().removeAll(
1881                                 entry.getValue().getPorts());
1882                         removePortGroupFlows(config, entry.getKey(), entry
1883                                 .getValue());
1884                     }
1885                 }
1886             }
1887         } else {
1888             if (add) {
1889                 portGroupData.put(config, data);
1890                 for (Node swid : data.keySet()) {
1891                     addPortGroupFlows(config, swid, data.get(swid));
1892                 }
1893             }
1894         }
1895     }
1896
1897     public boolean addPortGroupConfig(String name, String regex, boolean restore) {
1898         PortGroupConfig config = portGroupConfigs.get(name);
1899         if (config != null)
1900             return false;
1901
1902         if ((portGroupProvider == null) && !restore) {
1903             return false;
1904         }
1905         if ((portGroupProvider != null)
1906                 && (!portGroupProvider.isMatchCriteriaSupported(regex))) {
1907             return false;
1908         }
1909
1910         config = new PortGroupConfig(name, regex);
1911         portGroupConfigs.put(name, config);
1912         if (portGroupProvider != null) {
1913             portGroupProvider.createPortGroupConfig(config);
1914         }
1915         return true;
1916     }
1917
1918     public boolean delPortGroupConfig(String name) {
1919         PortGroupConfig config = portGroupConfigs.get(name);
1920         if (config == null) {
1921             return false;
1922         }
1923
1924         if (portGroupProvider != null) {
1925             portGroupProvider.deletePortGroupConfig(config);
1926         }
1927         portGroupConfigs.remove(name);
1928         return true;
1929     }
1930
1931     private void usePortGroupConfig(String name) {
1932         PortGroupConfig config = portGroupConfigs.get(name);
1933         if (config == null) {
1934             return;
1935         }
1936         if (portGroupProvider != null) {
1937             Map<Node, PortGroup> data = portGroupProvider
1938                     .getPortGroupData(config);
1939             portGroupData.put(config, data);
1940         }
1941     }
1942
1943     @Override
1944     public Map<String, PortGroupConfig> getPortGroupConfigs() {
1945         return portGroupConfigs;
1946     }
1947
1948     public boolean isPortGroupSupported() {
1949         if (portGroupProvider == null) {
1950             return false;
1951         }
1952         return true;
1953     }
1954
1955     // Fir PortGroupProvider to use regular Dependency Manager
1956     /* @SuppressWarnings("rawtypes") */
1957     /* public void bind(Object arg0, Map arg1) throws Exception { */
1958     /* if (arg0 instanceof PortGroupProvider) { */
1959     /* setPortGroupProvider((PortGroupProvider)arg0); */
1960     /* } */
1961     /* } */
1962
1963     /* @SuppressWarnings("rawtypes") */
1964     /* @Override */
1965     /* public void unbind(Object arg0, Map arg1) throws Exception { */
1966     /* if (arg0 instanceof PortGroupProvider) { */
1967     /* portGroupProvider = null; */
1968     /* } */
1969     /* } */
1970
1971     public void setIContainer(IContainer s) {
1972         this.container = s;
1973     }
1974
1975     public void unsetIContainer(IContainer s) {
1976         if (this.container == s) {
1977             this.container = null;
1978         }
1979     }
1980
1981     public PortGroupProvider getPortGroupProvider() {
1982         return portGroupProvider;
1983     }
1984
1985     public void unsetPortGroupProvider(PortGroupProvider portGroupProvider) {
1986         this.portGroupProvider = null;
1987     }
1988
1989     public void setPortGroupProvider(PortGroupProvider portGroupProvider) {
1990         this.portGroupProvider = portGroupProvider;
1991         portGroupProvider.registerPortGroupChange(this);
1992         for (PortGroupConfig config : portGroupConfigs.values()) {
1993             portGroupProvider.createPortGroupConfig(config);
1994         }
1995     }
1996
1997     public void setHostFinder(IfIptoHost hostFinder) {
1998         this.hostFinder = hostFinder;
1999     }
2000
2001     public void unsetHostFinder(IfIptoHost hostFinder) {
2002         if (this.hostFinder == hostFinder) {
2003             this.hostFinder = null;
2004         }
2005     }
2006
2007     public void setFrmAware(IForwardingRulesManagerAware obj) {
2008         this.frmAware.add(obj);
2009     }
2010
2011     public void unsetFrmAware(IForwardingRulesManagerAware obj) {
2012         this.frmAware.remove(obj);
2013     }
2014
2015     void setClusterContainerService(IClusterContainerServices s) {
2016         log.debug("Cluster Service set");
2017         this.clusterContainerService = s;
2018     }
2019
2020     void unsetClusterContainerService(IClusterContainerServices s) {
2021         if (this.clusterContainerService == s) {
2022             log.debug("Cluster Service removed!");
2023             this.clusterContainerService = null;
2024         }
2025     }
2026
2027     private String getContainerName() {
2028         if (container == null) {
2029             return GlobalConstants.DEFAULT.toString();
2030         }
2031         return container.getName();
2032     }
2033
2034     /**
2035      * Function called by the dependency manager when all the required
2036      * dependencies are satisfied
2037      *
2038      */
2039     void init() {
2040         log.info("Init");
2041         frmAware = Collections
2042                 .synchronizedSet(new HashSet<IForwardingRulesManagerAware>());
2043         frmFileName = GlobalConstants.STARTUPHOME.toString() + "frm_staticflows_"
2044                 + this.getContainerName() + ".conf";
2045         portGroupFileName = GlobalConstants.STARTUPHOME.toString() + "portgroup_"
2046                 + this.getContainerName() + ".conf";
2047
2048         inContainerMode = false;
2049
2050         if (portGroupProvider != null) {
2051             portGroupProvider.registerPortGroupChange(this);
2052         }
2053
2054         nonClusterObjectCreate();
2055
2056         cacheStartup();
2057
2058         registerWithOSGIConsole();
2059
2060         /*
2061          * If we are not the first cluster node to come up, do not initialize
2062          * the static flow entries ordinal
2063          */
2064         if (staticFlowsOrdinal.size() == 0) {
2065             staticFlowsOrdinal.put(0, Integer.valueOf(0));
2066         }
2067     }
2068
2069     /**
2070      * Function called by the dependency manager when at least one dependency
2071      * become unsatisfied or when the component is shutting down because for
2072      * example bundle is being stopped.
2073      *
2074      */
2075     void destroy() {
2076         log.info("Destroy");
2077         destroyCaches();
2078     }
2079
2080     /**
2081      * Function called by dependency manager after "init ()" is called and after
2082      * the services provided by the class are registered in the service registry
2083      *
2084      */
2085     void start() {
2086         log.info("Start");
2087         /*
2088          * Read startup and build database if we have not already gotten the
2089          * configurations synced from another node
2090          */
2091         if (staticFlows.isEmpty()) {
2092             loadFlowConfiguration();
2093         }
2094     }
2095
2096     /**
2097      * Function called by the dependency manager before the services exported by
2098      * the component are unregistered, this will be followed by a "destroy ()"
2099      * calls
2100      *
2101      */
2102     void stop() {
2103         log.info("Stop");
2104     }
2105
2106     public void setFlowProgrammerService(IFlowProgrammerService service) {
2107         this.programmer = service;
2108     }
2109
2110     public void unsetFlowProgrammerService(IFlowProgrammerService service) {
2111         if (this.programmer == service) {
2112             this.programmer = null;
2113         }
2114     }
2115
2116     public void setSwitchManager(ISwitchManager switchManager) {
2117         this.switchManager = switchManager;
2118     }
2119
2120     public void unsetSwitchManager(ISwitchManager switchManager) {
2121         if (this.switchManager == switchManager) {
2122             this.switchManager = null;
2123         }
2124     }
2125
2126     @Override
2127     public void tagUpdated(String containerName, Node n, short oldTag,
2128             short newTag, UpdateType t) {
2129
2130     }
2131
2132     @Override
2133     public void containerFlowUpdated(String containerName,
2134             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
2135         /*
2136          * Whether it is an addition or removal, we have to recompute the
2137          * merged flows entries taking into account all the current container flows
2138          * because flow merging is not an injective function
2139          */
2140         updateFlowsContainerFlow();
2141     }
2142
2143     @Override
2144     public void nodeConnectorUpdated(String containerName, NodeConnector p,
2145             UpdateType t) {
2146         // No action
2147     }
2148
2149     @Override
2150     public void containerModeUpdated(UpdateType update) {
2151         switch (update) {
2152         case ADDED:
2153             this.inContainerMode = true;
2154             this.uninstallAllFlowEntries();
2155             break;
2156         case REMOVED:
2157             this.inContainerMode = false;
2158             this.reinstallAllFlowEntries();
2159             break;
2160         default:
2161         }
2162
2163         // Update our configuration DB
2164         updateStaticFlowConfigsOnContainerModeChange(update);
2165     }
2166
2167     /*
2168      * OSGI COMMANDS
2169      */
2170     @Override
2171     public String getHelp() {
2172         StringBuffer help = new StringBuffer();
2173         help.append("---FRM Matrix Application---\n");
2174         help.append("\t printMatrixData        - Prints the Matrix Configs\n");
2175         help.append("\t addMatrixConfig <name> <regex>\n");
2176         help.append("\t delMatrixConfig <name>\n");
2177         help.append("\t useMatrixConfig <name>\n");
2178         return help.toString();
2179     }
2180
2181     public void _printMatrixData(CommandInterpreter ci) {
2182         ci.println("Configs : ");
2183         ci.println("---------");
2184         ci.println(portGroupConfigs);
2185
2186         ci.println("Data : ");
2187         ci.println("------");
2188         ci.println(portGroupData);
2189     }
2190
2191     public void _addMatrixConfig(CommandInterpreter ci) {
2192         String name = ci.nextArgument();
2193         String regex = ci.nextArgument();
2194         addPortGroupConfig(name, regex, false);
2195     }
2196
2197     public void _delMatrixConfig(CommandInterpreter ci) {
2198         String name = ci.nextArgument();
2199         delPortGroupConfig(name);
2200     }
2201
2202     public void _useMatrixConfig(CommandInterpreter ci) {
2203         String name = ci.nextArgument();
2204         usePortGroupConfig(name);
2205     }
2206
2207     public void _arpPunt(CommandInterpreter ci) {
2208         String switchId = ci.nextArgument();
2209         long swid = HexEncode.stringToLong(switchId);
2210         Node node = NodeCreator.createOFNode(swid);
2211         installImplicitARPReplyPunt(node);
2212     }
2213
2214     public void _frmaddflow(CommandInterpreter ci) throws UnknownHostException {
2215         Node node = null;
2216         String nodeId = ci.nextArgument();
2217         if (nodeId == null) {
2218             ci.print("Node id not specified");
2219             return;
2220         }
2221         try {
2222             node = NodeCreator.createOFNode(Long.valueOf(nodeId));
2223         } catch (NumberFormatException e) {
2224             e.printStackTrace();
2225         }
2226         ci.println(this.programmer.addFlow(node, getSampleFlow(node)));
2227     }
2228
2229     public void _frmremoveflow(CommandInterpreter ci)
2230             throws UnknownHostException {
2231         Node node = null;
2232         String nodeId = ci.nextArgument();
2233         if (nodeId == null) {
2234             ci.print("Node id not specified");
2235             return;
2236         }
2237         try {
2238             node = NodeCreator.createOFNode(Long.valueOf(nodeId));
2239         } catch (NumberFormatException e) {
2240             e.printStackTrace();
2241         }
2242         ci.println(this.programmer.removeFlow(node, getSampleFlow(node)));
2243     }
2244
2245     private Flow getSampleFlow(Node node) throws UnknownHostException {
2246         NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
2247                 (short) 24, node);
2248         NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
2249                 (short) 30, node);
2250         byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
2251                 (byte) 0x9a, (byte) 0xbc };
2252         byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
2253                 (byte) 0x5e, (byte) 0x6f };
2254         InetAddress srcIP = InetAddress.getByName("172.28.30.50");
2255         InetAddress dstIP = InetAddress.getByName("171.71.9.52");
2256         InetAddress ipMask = InetAddress.getByName("255.255.255.0");
2257         InetAddress ipMask2 = InetAddress.getByName("255.0.0.0");
2258         short ethertype = EtherTypes.IPv4.shortValue();
2259         short vlan = (short) 27;
2260         byte vlanPr = 3;
2261         Byte tos = 4;
2262         byte proto = IPProtocols.TCP.byteValue();
2263         short src = (short) 55000;
2264         short dst = 80;
2265
2266         /*
2267          * Create a SAL Flow aFlow
2268          */
2269         Match match = new Match();
2270         match.setField(MatchType.IN_PORT, port);
2271         match.setField(MatchType.DL_SRC, srcMac);
2272         match.setField(MatchType.DL_DST, dstMac);
2273         match.setField(MatchType.DL_TYPE, ethertype);
2274         match.setField(MatchType.DL_VLAN, vlan);
2275         match.setField(MatchType.DL_VLAN_PR, vlanPr);
2276         match.setField(MatchType.NW_SRC, srcIP, ipMask);
2277         match.setField(MatchType.NW_DST, dstIP, ipMask2);
2278         match.setField(MatchType.NW_TOS, tos);
2279         match.setField(MatchType.NW_PROTO, proto);
2280         match.setField(MatchType.TP_SRC, src);
2281         match.setField(MatchType.TP_DST, dst);
2282
2283         List<Action> actions = new ArrayList<Action>();
2284         actions.add(new Output(oport));
2285         actions.add(new PopVlan());
2286         actions.add(new Flood());
2287         actions.add(new Controller());
2288         return new Flow(match, actions);
2289     }
2290
2291     @Override
2292     public Status saveConfiguration() {
2293         return  saveConfig();
2294     }
2295
2296     public void _frmNodeFlows(CommandInterpreter ci) {
2297         boolean verbose = false;
2298         String verboseCheck = ci.nextArgument();
2299         if (verboseCheck != null) {
2300             verbose = verboseCheck.equals("true");
2301         }
2302
2303         // Dump per node database
2304         for (Entry<Node, Set<FlowEntryInstall>> entry : this.nodeFlows
2305                 .entrySet()) {
2306             Node node = entry.getKey();
2307             for (FlowEntryInstall flow : entry.getValue()) {
2308                 if (!verbose) {
2309                     ci.println(node + " " + flow.getFlowName());
2310                 } else {
2311                     ci.println(node + " " + flow.toString());
2312                 }
2313             }
2314         }
2315     }
2316
2317     public void _frmGroupFlows(CommandInterpreter ci) {
2318         boolean verbose = false;
2319         String verboseCheck = ci.nextArgument();
2320         if (verboseCheck != null) {
2321             verbose = verboseCheck.equals("true");
2322         }
2323
2324         // Dump per node database
2325         for (Entry<String, Set<FlowEntryInstall>> entry : this.groupFlows
2326                 .entrySet()) {
2327             String group = entry.getKey();
2328             ci.println("Group " + group + ":");
2329             for (FlowEntryInstall flow : entry.getValue()) {
2330                 if (!verbose) {
2331                     ci.println(flow.getNode() + " " + flow.getFlowName());
2332                 } else {
2333                     ci.println(flow.getNode() + " " + flow.toString());
2334                 }
2335             }
2336         }
2337     }
2338
2339 }