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