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