c8112737d79ccd72bf3a31e07ee63129fe6ccfb6
[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.Callable;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.ConcurrentMap;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.LinkedBlockingQueue;
32
33 import org.eclipse.osgi.framework.console.CommandInterpreter;
34 import org.eclipse.osgi.framework.console.CommandProvider;
35 import org.opendaylight.controller.clustering.services.CacheConfigException;
36 import org.opendaylight.controller.clustering.services.CacheExistException;
37 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
38 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
39 import org.opendaylight.controller.clustering.services.IClusterServices;
40 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
41 import org.opendaylight.controller.connectionmanager.IConnectionManager;
42 import org.opendaylight.controller.containermanager.IContainerManager;
43 import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
44 import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
45 import org.opendaylight.controller.forwardingrulesmanager.FlowEntryInstall;
46 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
47 import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManagerAware;
48 import org.opendaylight.controller.forwardingrulesmanager.PortGroup;
49 import org.opendaylight.controller.forwardingrulesmanager.PortGroupChangeListener;
50 import org.opendaylight.controller.forwardingrulesmanager.PortGroupConfig;
51 import org.opendaylight.controller.forwardingrulesmanager.PortGroupProvider;
52 import org.opendaylight.controller.forwardingrulesmanager.implementation.data.FlowEntryDistributionOrder;
53 import org.opendaylight.controller.sal.action.Action;
54 import org.opendaylight.controller.sal.action.ActionType;
55 import org.opendaylight.controller.sal.action.Controller;
56 import org.opendaylight.controller.sal.action.Flood;
57 import org.opendaylight.controller.sal.action.Output;
58 import org.opendaylight.controller.sal.action.PopVlan;
59 import org.opendaylight.controller.sal.connection.ConnectionLocality;
60 import org.opendaylight.controller.sal.core.Config;
61 import org.opendaylight.controller.sal.core.ContainerFlow;
62 import org.opendaylight.controller.sal.core.IContainer;
63 import org.opendaylight.controller.sal.core.IContainerLocalListener;
64 import org.opendaylight.controller.sal.core.Node;
65 import org.opendaylight.controller.sal.core.NodeConnector;
66 import org.opendaylight.controller.sal.core.Property;
67 import org.opendaylight.controller.sal.core.UpdateType;
68 import org.opendaylight.controller.sal.flowprogrammer.Flow;
69 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener;
70 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
71 import org.opendaylight.controller.sal.match.Match;
72 import org.opendaylight.controller.sal.match.MatchType;
73 import org.opendaylight.controller.sal.utils.EtherTypes;
74 import org.opendaylight.controller.sal.utils.GlobalConstants;
75 import org.opendaylight.controller.sal.utils.HexEncode;
76 import org.opendaylight.controller.sal.utils.IObjectReader;
77 import org.opendaylight.controller.sal.utils.IPProtocols;
78 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
79 import org.opendaylight.controller.sal.utils.NodeCreator;
80 import org.opendaylight.controller.sal.utils.ObjectReader;
81 import org.opendaylight.controller.sal.utils.ObjectWriter;
82 import org.opendaylight.controller.sal.utils.Status;
83 import org.opendaylight.controller.sal.utils.StatusCode;
84 import org.opendaylight.controller.switchmanager.IInventoryListener;
85 import org.opendaylight.controller.switchmanager.ISwitchManager;
86 import org.opendaylight.controller.switchmanager.ISwitchManagerAware;
87 import org.opendaylight.controller.switchmanager.Subnet;
88 import org.osgi.framework.BundleContext;
89 import org.osgi.framework.FrameworkUtil;
90 import org.slf4j.Logger;
91 import org.slf4j.LoggerFactory;
92
93 /**
94  * Class that manages forwarding rule installation and removal per container of
95  * the network. It also maintains the central repository of all the forwarding
96  * rules installed on the network nodes.
97  */
98 public class ForwardingRulesManager implements
99         IForwardingRulesManager,
100         PortGroupChangeListener,
101         IContainerLocalListener,
102         ISwitchManagerAware,
103         IConfigurationContainerAware,
104         IInventoryListener,
105         IObjectReader,
106         ICacheUpdateAware<Object,Object>,
107         CommandProvider,
108         IFlowProgrammerListener {
109
110     private static final Logger log = LoggerFactory.getLogger(ForwardingRulesManager.class);
111     private static final Logger logsync = LoggerFactory.getLogger("FRMsync");
112     private static final String PORTREMOVED = "Port removed";
113     private static final String NODEDOWN = "Node is Down";
114     private static final String INVALID_FLOW_ENTRY = "Invalid FlowEntry";
115     private String frmFileName;
116     private String portGroupFileName;
117     private ConcurrentMap<Integer, FlowConfig> staticFlows;
118     private ConcurrentMap<Integer, Integer> staticFlowsOrdinal;
119     private ConcurrentMap<String, PortGroupConfig> portGroupConfigs;
120     private ConcurrentMap<PortGroupConfig, Map<Node, PortGroup>> portGroupData;
121     private ConcurrentMap<String, Object> TSPolicies;
122     private IContainerManager containerManager;
123     private boolean inContainerMode; // being used by global instance only
124     protected boolean stopping;
125
126     /*
127      * Flow database. It's the software view of what was requested to install
128      * and what is installed on the switch. It is indexed by the entry itself.
129      * The entry's hashcode resumes the network node index, the flow's priority
130      * and the flow's match. The value element is a class which contains the
131      * flow entry pushed by the applications modules and the respective
132      * container flow merged version. In absence of container flows, the two
133      * flow entries are the same.
134      */
135     private ConcurrentMap<FlowEntry, FlowEntry> originalSwView;
136     private ConcurrentMap<FlowEntryInstall, FlowEntryInstall> installedSwView;
137     /*
138      * Per node and per group indexing
139      */
140     private ConcurrentMap<Node, List<FlowEntryInstall>> nodeFlows;
141     private ConcurrentMap<String, List<FlowEntryInstall>> groupFlows;
142
143     /*
144      * Inactive flow list. This is for the global instance of FRM It will
145      * contain all the flow entries which were installed on the global container
146      * when the first container is created.
147      */
148     private ConcurrentMap<FlowEntry, FlowEntry> inactiveFlows;
149
150     private IContainer container;
151     private Set<IForwardingRulesManagerAware> frmAware =
152         Collections.synchronizedSet(new HashSet<IForwardingRulesManagerAware>());
153     private PortGroupProvider portGroupProvider;
154     private IFlowProgrammerService programmer;
155     private IClusterContainerServices clusterContainerService = null;
156     private ISwitchManager switchManager;
157     private Thread frmEventHandler;
158     protected BlockingQueue<FRMEvent> pendingEvents;
159
160     // Distributes FRM programming in the cluster
161     private IConnectionManager connectionManager;
162
163     /*
164      * Name clustered caches used to support FRM entry distribution these are by
165      * necessity non-transactional as long as need to be able to synchronize
166      * states also while a transaction is in progress
167      */
168     static final String WORK_ORDER_CACHE = "frm.workOrder";
169     static final String WORK_STATUS_CACHE = "frm.workStatus";
170
171     /*
172      * Data structure responsible for distributing the FlowEntryInstall requests
173      * in the cluster. The key value is entry that is being either Installed or
174      * Updated or Delete. The value field is the same of the key value in case
175      * of Installation or Deletion, it's the new entry in case of Modification,
176      * this because the clustering caches don't allow null values.
177      *
178      * The logic behind this data structure is that the controller that initiate
179      * the request will place the order here, someone will pick it and then will
180      * remove from this data structure because is being served.
181      *
182      * TODO: We need to have a way to cleanup this data structure if entries are
183      * not picked by anyone, which is always a case can happen especially on
184      * Node disconnect cases.
185      */
186     protected ConcurrentMap<FlowEntryDistributionOrder, FlowEntryInstall> workOrder;
187
188     /*
189      * Data structure responsible for retrieving the results of the workOrder
190      * submitted to the cluster.
191      *
192      * The logic behind this data structure is that the controller that has
193      * executed the order will then place the result in workStatus signaling
194      * that there was a success or a failure.
195      *
196      * TODO: The workStatus entries need to have a lifetime associated in case
197      * of requestor controller leaving the cluster.
198      */
199     protected ConcurrentMap<FlowEntryDistributionOrder, Status> workStatus;
200
201     /*
202      * Local Map used to hold the Future which a caller can use to monitor for
203      * completion
204      */
205     private ConcurrentMap<FlowEntryDistributionOrder, FlowEntryDistributionOrderFutureTask> workMonitor =
206             new ConcurrentHashMap<FlowEntryDistributionOrder, FlowEntryDistributionOrderFutureTask>();
207
208     /*
209      * Max pool size for the executor
210      */
211     private static final int maxPoolSize = 10;
212
213     /**
214      * @param e
215      *            Entry being installed/updated/removed
216      * @param u
217      *            New entry will be placed after the update operation. Valid
218      *            only for UpdateType.CHANGED, null for all the other cases
219      * @param t
220      *            Type of update
221      * @return a Future object for monitoring the progress of the result, or
222      *         null in case the processing should take place locally
223      */
224     private FlowEntryDistributionOrderFutureTask distributeWorkOrder(FlowEntryInstall e, FlowEntryInstall u,
225             UpdateType t) {
226         // A null entry it's an unexpected condition, anyway it's safe to keep
227         // the handling local
228         if (e == null) {
229             return null;
230         }
231
232         Node n = e.getNode();
233         if (connectionManager.getLocalityStatus(n) == ConnectionLocality.NOT_LOCAL) {
234             // Create the work order and distribute it
235             FlowEntryDistributionOrder fe =
236                     new FlowEntryDistributionOrder(e, t, clusterContainerService.getMyAddress());
237             // First create the monitor job
238             FlowEntryDistributionOrderFutureTask ret = new FlowEntryDistributionOrderFutureTask(fe);
239             logsync.trace("Node {} not local so sending fe {}", n, fe);
240             workMonitor.put(fe, ret);
241             if (t.equals(UpdateType.CHANGED)) {
242                 // Then distribute the work
243                 workOrder.put(fe, u);
244             } else {
245                 // Then distribute the work
246                 workOrder.put(fe, e);
247             }
248             logsync.trace("WorkOrder requested");
249             // Now create an Handle to monitor the execution of the operation
250             return ret;
251         }
252
253         logsync.trace("Node {} could be local. so processing Entry:{} UpdateType:{}", n, e, t);
254         return null;
255     }
256
257     /**
258      * Adds a flow entry onto the network node It runs various validity checks
259      * and derive the final container flows merged entries that will be
260      * attempted to be installed
261      *
262      * @param flowEntry
263      *            the original flow entry application requested to add
264      * @param async
265      *            the flag indicating if this is a asynchronous request
266      * @return the status of this request. In case of asynchronous call, it will
267      *         contain the unique id assigned to this request
268      */
269     private Status addEntry(FlowEntry flowEntry, boolean async) {
270
271         // Sanity Check
272         if (flowEntry == null || flowEntry.getNode() == null || flowEntry.getFlow() == null) {
273             String logMsg = INVALID_FLOW_ENTRY + ": {}";
274             log.warn(logMsg, flowEntry);
275             return new Status(StatusCode.NOTACCEPTABLE, INVALID_FLOW_ENTRY);
276         }
277
278         /*
279          * Redundant Check: Check if the request is a redundant one from the
280          * same application the flowEntry is equal to an existing one. Given we
281          * do not have an application signature in the requested FlowEntry yet,
282          * we are here detecting the above condition by comparing the flow
283          * names, if set. If they are equal to the installed flow, most likely
284          * this is a redundant installation request from the same application
285          * and we can silently return success
286          *
287          * TODO: in future a sort of application reference list mechanism will
288          * be added to the FlowEntry so that exact flow can be used by different
289          * applications.
290          */
291         FlowEntry present = this.originalSwView.get(flowEntry);
292         if (present != null) {
293             boolean sameFlow = present.getFlow().equals(flowEntry.getFlow());
294             boolean sameApp = present.getFlowName() != null && present.getFlowName().equals(flowEntry.getFlowName());
295             if (sameFlow && sameApp) {
296                 log.trace("Skipping redundant request for flow {} on node {}", flowEntry.getFlowName(),
297                         flowEntry.getNode());
298                 return new Status(StatusCode.SUCCESS, "Entry is already present");
299             }
300         }
301
302         /*
303          * Derive the container flow merged entries to install In presence of N
304          * container flows, we may end up with N different entries to install...
305          */
306         List<FlowEntryInstall> toInstallList = deriveInstallEntries(flowEntry.clone(), container.getContainerFlows());
307
308         // Container Flow conflict Check
309         if (toInstallList.isEmpty()) {
310             String msg = "Flow Entry conflicts with all Container Flows";
311             String logMsg = msg + ": {}";
312             log.warn(logMsg, flowEntry);
313             return new Status(StatusCode.CONFLICT, msg);
314         }
315
316         // Derive the list of entries good to be installed
317         List<FlowEntryInstall> toInstallSafe = new ArrayList<FlowEntryInstall>();
318         for (FlowEntryInstall entry : toInstallList) {
319             // Conflict Check: Verify new entry would not overwrite existing
320             // ones
321             if (this.installedSwView.containsKey(entry)) {
322                 log.warn("Operation Rejected: A flow with same match and priority exists on the target node");
323                 log.trace("Aborting to install {}", entry);
324                 continue;
325             }
326             toInstallSafe.add(entry);
327         }
328
329         // Declare failure if all the container flow merged entries clash with
330         // existing entries
331         if (toInstallSafe.size() == 0) {
332             String msg = "A flow with same match and priority exists on the target node";
333             String logMsg = msg + ": {}";
334             log.warn(logMsg, flowEntry);
335             return new Status(StatusCode.CONFLICT, msg);
336         }
337
338         // Try to install an entry at the time
339         Status error = new Status(null, null);
340         Status succeded = null;
341         boolean oneSucceded = false;
342         for (FlowEntryInstall installEntry : toInstallSafe) {
343
344             // Install and update database
345             Status ret = addEntriesInternal(installEntry, async);
346
347             if (ret.isSuccess()) {
348                 oneSucceded = true;
349                 /*
350                  * The first successful status response will be returned For the
351                  * asynchronous call, we can discard the container flow
352                  * complication for now and assume we will always deal with one
353                  * flow only per request
354                  */
355                 succeded = ret;
356             } else {
357                 error = ret;
358                 log.trace("Failed to install the entry: {}. The failure is: {}", installEntry, ret.getDescription());
359             }
360         }
361
362         return (oneSucceded) ? succeded : error;
363     }
364
365     /**
366      * Given a flow entry and the list of container flows, it returns the list
367      * of container flow merged flow entries good to be installed on this
368      * container. If the list of container flows is null or empty, the install
369      * entry list will contain only one entry, the original flow entry. If the
370      * flow entry is congruent with all the N container flows, then the output
371      * install entry list will contain N entries. If the output list is empty,
372      * it means the passed flow entry conflicts with all the container flows.
373      *
374      * @param cFlowList
375      *            The list of container flows
376      * @return the list of container flow merged entries good to be installed on
377      *         this container
378      */
379     private List<FlowEntryInstall> deriveInstallEntries(FlowEntry request, List<ContainerFlow> cFlowList) {
380         List<FlowEntryInstall> toInstallList = new ArrayList<FlowEntryInstall>(1);
381
382         if (container.getContainerFlows() == null || container.getContainerFlows().isEmpty()) {
383             // No container flows => entry good to be installed unchanged
384             toInstallList.add(new FlowEntryInstall(request.clone(), null));
385         } else {
386             // Create the list of entries to be installed. If the flow entry is
387             // not congruent with any container flow, no install entries will be
388             // created
389             for (ContainerFlow cFlow : container.getContainerFlows()) {
390                 if (cFlow.allowsFlow(request.getFlow())) {
391                     toInstallList.add(new FlowEntryInstall(request.clone(), cFlow));
392                 }
393             }
394         }
395         return toInstallList;
396     }
397
398     /**
399      * Modify a flow entry with a new one It runs various validity check and
400      * derive the final container flows merged flow entries to work with
401      *
402      * @param currentFlowEntry
403      * @param newFlowEntry
404      * @param async
405      *            the flag indicating if this is a asynchronous request
406      * @return the status of this request. In case of asynchronous call, it will
407      *         contain the unique id assigned to this request
408      */
409     private Status modifyEntry(FlowEntry currentFlowEntry, FlowEntry newFlowEntry, boolean async) {
410         Status retExt;
411
412         // Sanity checks
413         if (currentFlowEntry == null || currentFlowEntry.getNode() == null || newFlowEntry == null
414                 || newFlowEntry.getNode() == null || newFlowEntry.getFlow() == null) {
415             String msg = "Modify: " + INVALID_FLOW_ENTRY;
416             String logMsg = msg + ": {} or {}";
417             log.warn(logMsg, currentFlowEntry, newFlowEntry);
418             return new Status(StatusCode.NOTACCEPTABLE, msg);
419         }
420         if (!currentFlowEntry.getNode().equals(newFlowEntry.getNode())
421                 || !currentFlowEntry.getFlowName().equals(newFlowEntry.getFlowName())) {
422             String msg = "Modify: Incompatible Flow Entries";
423             String logMsg = msg + ": {} and {}";
424             log.warn(logMsg, currentFlowEntry, newFlowEntry);
425             return new Status(StatusCode.NOTACCEPTABLE, msg);
426         }
427
428         // Equality Check
429         if (currentFlowEntry.getFlow().equals(newFlowEntry.getFlow())) {
430             String msg = "Modify skipped as flows are the same";
431             String logMsg = msg + ": {} and {}";
432             log.debug(logMsg, currentFlowEntry, newFlowEntry);
433             return new Status(StatusCode.SUCCESS, msg);
434         }
435
436         /*
437          * Conflict Check: Verify the new entry would not conflict with an
438          * existing one. This is a loose check on the previous original flow
439          * entry requests. No check on the container flow merged flow entries
440          * (if any) yet
441          */
442         FlowEntry sameMatchOriginalEntry = originalSwView.get(newFlowEntry);
443         if (sameMatchOriginalEntry != null && !sameMatchOriginalEntry.equals(currentFlowEntry)) {
444             String msg = "Operation Rejected: Another flow with same match and priority exists on the target node";
445             String logMsg = msg + ": {}";
446             log.warn(logMsg, currentFlowEntry);
447             return new Status(StatusCode.CONFLICT, msg);
448         }
449
450         // Derive the installed and toInstall entries
451         List<FlowEntryInstall> installedList = deriveInstallEntries(currentFlowEntry.clone(),
452                 container.getContainerFlows());
453         List<FlowEntryInstall> toInstallList = deriveInstallEntries(newFlowEntry.clone(), container.getContainerFlows());
454
455         if (toInstallList.isEmpty()) {
456             String msg = "Modify Operation Rejected: The new entry conflicts with all the container flows";
457             String logMsg = msg + ": {}";
458             log.warn(logMsg, newFlowEntry);
459             log.warn(msg);
460             return new Status(StatusCode.CONFLICT, msg);
461         }
462
463         /*
464          * If the two list sizes differ, it means the new flow entry does not
465          * satisfy the same number of container flows the current entry does.
466          * This is only possible when the new entry and current entry have
467          * different match. In this scenario the modification would ultimately
468          * be handled as a remove and add operations in the protocol plugin.
469          *
470          * Also, if any of the new flow entries would clash with an existing
471          * one, we cannot proceed with the modify operation, because it would
472          * fail for some entries and leave stale entries on the network node.
473          * Modify path can be taken only if it can be performed completely, for
474          * all entries.
475          *
476          * So, for the above two cases, to simplify, let's decouple the modify
477          * in: 1) remove current entries 2) install new entries
478          */
479         Status succeeded = null;
480         boolean decouple = false;
481         if (installedList.size() != toInstallList.size()) {
482             log.info("Modify: New flow entry does not satisfy the same "
483                     + "number of container flows as the original entry does");
484             decouple = true;
485         }
486         List<FlowEntryInstall> toInstallSafe = new ArrayList<FlowEntryInstall>();
487         for (FlowEntryInstall installEntry : toInstallList) {
488             /*
489              * Conflict Check: Verify the new entry would not overwrite another
490              * existing one
491              */
492             FlowEntryInstall sameMatchEntry = installedSwView.get(installEntry);
493             if (sameMatchEntry != null && !sameMatchEntry.getOriginal().equals(currentFlowEntry)) {
494                 log.info("Modify: new container flow merged flow entry clashes with existing flow");
495                 decouple = true;
496             } else {
497                 toInstallSafe.add(installEntry);
498             }
499         }
500
501         if (decouple) {
502             // Remove current entries
503             for (FlowEntryInstall currEntry : installedList) {
504                 this.removeEntryInternal(currEntry, async);
505             }
506             // Install new entries
507             for (FlowEntryInstall newEntry : toInstallSafe) {
508                 succeeded = this.addEntriesInternal(newEntry, async);
509             }
510         } else {
511             /*
512              * The two list have the same size and the entries to install do not
513              * clash with any existing flow on the network node. We assume here
514              * (and might be wrong) that the same container flows that were
515              * satisfied by the current entries are the same that are satisfied
516              * by the new entries. Let's take the risk for now.
517              *
518              * Note: modification has to be complete. If any entry modification
519              * fails, we need to stop, restore the already modified entries, and
520              * declare failure.
521              */
522             Status retModify = null;
523             int i = 0;
524             int size = toInstallList.size();
525             while (i < size) {
526                 // Modify and update database
527                 retModify = modifyEntryInternal(installedList.get(i), toInstallList.get(i), async);
528                 if (retModify.isSuccess()) {
529                     i++;
530                 } else {
531                     break;
532                 }
533             }
534             // Check if uncompleted modify
535             if (i < size) {
536                 log.warn("Unable to perform a complete modify for all  the container flows merged entries");
537                 // Restore original entries
538                 int j = 0;
539                 while (j < i) {
540                     log.info("Attempting to restore initial entries");
541                     retExt = modifyEntryInternal(toInstallList.get(i), installedList.get(i), async);
542                     if (retExt.isSuccess()) {
543                         j++;
544                     } else {
545                         break;
546                     }
547                 }
548                 // Fatal error, recovery failed
549                 if (j < i) {
550                     String msg = "Flow recovery failed ! Unrecoverable Error";
551                     log.error(msg);
552                     return new Status(StatusCode.INTERNALERROR, msg);
553                 }
554             }
555             succeeded = retModify;
556         }
557         /*
558          * The first successful status response will be returned. For the
559          * asynchronous call, we can discard the container flow complication for
560          * now and assume we will always deal with one flow only per request
561          */
562         return succeeded;
563     }
564
565     /**
566      * This is the function that modifies the final container flows merged
567      * entries on the network node and update the database. It expects that all
568      * the validity checks are passed
569      *
570      * @param currentEntries
571      * @param newEntries
572      * @param async
573      *            the flag indicating if this is a asynchronous request
574      * @return the status of this request. In case of asynchronous call, it will
575      *         contain the unique id assigned to this request
576      */
577     private Status modifyEntryInternal(FlowEntryInstall currentEntries, FlowEntryInstall newEntries, boolean async) {
578         FlowEntryDistributionOrderFutureTask futureStatus =
579                 distributeWorkOrder(currentEntries, newEntries, UpdateType.CHANGED);
580         if (futureStatus != null) {
581             Status retStatus = new Status(StatusCode.UNDEFINED);
582             try {
583                 retStatus = futureStatus.get();
584                 if (retStatus.getCode()
585                         .equals(StatusCode.TIMEOUT)) {
586                     // A timeout happened, lets cleanup the workMonitor
587                     workMonitor.remove(futureStatus.getOrder());
588                 }
589             } catch (InterruptedException e) {
590                 log.error("", e);
591             } catch (ExecutionException e) {
592                 log.error("", e);
593             }
594             return retStatus;
595         } else {
596             // Modify the flow on the network node
597             Status status = async ? programmer.modifyFlowAsync(currentEntries.getNode(), currentEntries.getInstall()
598                     .getFlow(), newEntries.getInstall()
599                     .getFlow()) : programmer.modifyFlow(currentEntries.getNode(), currentEntries.getInstall()
600                     .getFlow(), newEntries.getInstall()
601                     .getFlow());
602
603             if (!status.isSuccess()) {
604                 log.trace("SDN Plugin failed to program the flow: {}. The failure is: {}", newEntries.getInstall(),
605                         status.getDescription());
606                 return status;
607             }
608
609             log.trace("Modified {} => {}", currentEntries.getInstall(), newEntries.getInstall());
610
611             // Update DB
612             newEntries.setRequestId(status.getRequestId());
613             updateLocalDatabase(currentEntries, false);
614             updateLocalDatabase(newEntries, true);
615
616             return status;
617         }
618     }
619
620     /**
621      * Remove a flow entry. If the entry is not present in the software view
622      * (entry or node not present), it return successfully
623      *
624      * @param flowEntry
625      *            the flow entry to remove
626      * @param async
627      *            the flag indicating if this is a asynchronous request
628      * @return the status of this request. In case of asynchronous call, it will
629      *         contain the unique id assigned to this request
630      */
631     private Status removeEntry(FlowEntry flowEntry, boolean async) {
632         Status error = new Status(null, null);
633
634         // Sanity Check
635         if (flowEntry == null || flowEntry.getNode() == null || flowEntry.getFlow() == null) {
636             String logMsg = INVALID_FLOW_ENTRY + ": {}";
637             log.warn(logMsg, flowEntry);
638             return new Status(StatusCode.NOTACCEPTABLE, INVALID_FLOW_ENTRY);
639         }
640
641         // Derive the container flows merged installed entries
642         List<FlowEntryInstall> installedList = deriveInstallEntries(flowEntry.clone(), container.getContainerFlows());
643
644         Status succeeded = null;
645         boolean atLeastOneRemoved = false;
646         for (FlowEntryInstall entry : installedList) {
647             if (!installedSwView.containsKey(entry)) {
648                 String logMsg = "Removal skipped (not present in software view) for flow entry: {}";
649                 log.debug(logMsg, flowEntry);
650                 if (installedList.size() == 1) {
651                     // If we had only one entry to remove, we are done
652                     return new Status(StatusCode.SUCCESS);
653                 } else {
654                     continue;
655                 }
656             }
657
658             // Remove and update DB
659             Status ret = removeEntryInternal(entry, async);
660
661             if (!ret.isSuccess()) {
662                 error = ret;
663                 log.trace("Failed to remove the entry: {}. The failure is: {}", entry.getInstall(), ret.getDescription());
664                 if (installedList.size() == 1) {
665                     // If we had only one entry to remove, this is fatal failure
666                     return error;
667                 }
668             } else {
669                 succeeded = ret;
670                 atLeastOneRemoved = true;
671             }
672         }
673
674         /*
675          * No worries if full removal failed. Consistency checker will take care
676          * of removing the stale entries later, or adjusting the software
677          * database if not in sync with hardware
678          */
679         return (atLeastOneRemoved) ? succeeded : error;
680     }
681
682     /**
683      * This is the function that removes the final container flows merged entry
684      * from the network node and update the database. It expects that all the
685      * validity checks are passed
686      *
687      * @param entry
688      *            the flow entry to remove
689      * @param async
690      *            the flag indicating if this is a asynchronous request
691      * @return the status of this request. In case of asynchronous call, it will
692      *         contain the unique id assigned to this request
693      */
694     private Status removeEntryInternal(FlowEntryInstall entry, boolean async) {
695         FlowEntryDistributionOrderFutureTask futureStatus = distributeWorkOrder(entry, null, UpdateType.REMOVED);
696         if (futureStatus != null) {
697             Status retStatus = new Status(StatusCode.UNDEFINED);
698             try {
699                 retStatus = futureStatus.get();
700                 if (retStatus.getCode()
701                         .equals(StatusCode.TIMEOUT)) {
702                     // A timeout happened, lets cleanup the workMonitor
703                     workMonitor.remove(futureStatus.getOrder());
704                 }
705             } catch (InterruptedException e) {
706                 log.error("", e);
707             } catch (ExecutionException e) {
708                 log.error("", e);
709             }
710             return retStatus;
711         } else {
712             // Mark the entry to be deleted (for CC just in case we fail)
713             entry.toBeDeleted();
714
715             // Remove from node
716             Status status = async ? programmer.removeFlowAsync(entry.getNode(), entry.getInstall()
717                     .getFlow()) : programmer.removeFlow(entry.getNode(), entry.getInstall()
718                     .getFlow());
719
720             if (!status.isSuccess()) {
721                 log.trace("SDN Plugin failed to remove the flow: {}. The failure is: {}", entry.getInstall(),
722                         status.getDescription());
723                 return status;
724             }
725             log.trace("Removed  {}", entry.getInstall());
726
727             // Update DB
728             updateLocalDatabase(entry, false);
729
730             return status;
731         }
732     }
733
734     /**
735      * This is the function that installs the final container flow merged entry
736      * on the network node and updates the database. It expects that all the
737      * validity and conflict checks are passed. That means it does not check
738      * whether this flow would conflict or overwrite an existing one.
739      *
740      * @param entry
741      *            the flow entry to install
742      * @param async
743      *            the flag indicating if this is a asynchronous request
744      * @return the status of this request. In case of asynchronous call, it will
745      *         contain the unique id assigned to this request
746      */
747     private Status addEntriesInternal(FlowEntryInstall entry, boolean async) {
748         FlowEntryDistributionOrderFutureTask futureStatus = distributeWorkOrder(entry, null, UpdateType.ADDED);
749         if (futureStatus != null) {
750             Status retStatus = new Status(StatusCode.UNDEFINED);
751             try {
752                 retStatus = futureStatus.get();
753                 if (retStatus.getCode()
754                         .equals(StatusCode.TIMEOUT)) {
755                     // A timeout happened, lets cleanup the workMonitor
756                     workMonitor.remove(futureStatus.getOrder());
757                 }
758             } catch (InterruptedException e) {
759                 log.error("", e);
760             } catch (ExecutionException e) {
761                 log.error("", e);
762             }
763             return retStatus;
764         } else {
765             // Install the flow on the network node
766             Status status = async ? programmer.addFlowAsync(entry.getNode(), entry.getInstall()
767                     .getFlow()) : programmer.addFlow(entry.getNode(), entry.getInstall()
768                     .getFlow());
769
770             if (!status.isSuccess()) {
771                 log.trace("SDN Plugin failed to program the flow: {}. The failure is: {}", entry.getInstall(),
772                         status.getDescription());
773                 return status;
774             }
775
776             log.trace("Added    {}", entry.getInstall());
777
778             // Update DB
779             entry.setRequestId(status.getRequestId());
780             updateLocalDatabase(entry, true);
781
782             return status;
783         }
784     }
785
786     /**
787      * Returns true if the flow conflicts with all the container's flows. This
788      * means that if the function returns true, the passed flow entry is
789      * congruent with at least one container flow, hence it is good to be
790      * installed on this container.
791      *
792      * @param flowEntry
793      * @return true if flow conflicts with all the container flows, false
794      *         otherwise
795      */
796     private boolean entryConflictsWithContainerFlows(FlowEntry flowEntry) {
797         List<ContainerFlow> cFlowList = container.getContainerFlows();
798
799         // Validity check and avoid unnecessary computation
800         // Also takes care of default container where no container flows are
801         // present
802         if (cFlowList == null || cFlowList.isEmpty()) {
803             return false;
804         }
805
806         for (ContainerFlow cFlow : cFlowList) {
807             if (cFlow.allowsFlow(flowEntry.getFlow())) {
808                 // Entry is allowed by at least one container flow: good to go
809                 return false;
810             }
811         }
812         return true;
813     }
814
815     private ConcurrentMap.Entry<Integer, FlowConfig> getStaticFlowEntry(String name, Node node) {
816         for (ConcurrentMap.Entry<Integer, FlowConfig> flowEntry : staticFlows.entrySet()) {
817             FlowConfig flowConfig = flowEntry.getValue();
818             if (flowConfig.isByNameAndNodeIdEqual(name, node)) {
819                 return flowEntry;
820             }
821         }
822         return null;
823     }
824
825     private void updateLocalDatabase(FlowEntryInstall entry, boolean add) {
826         // Update the software view
827         updateSwViewes(entry, add);
828
829         // Update node indexed flow database
830         updateNodeFlowsDB(entry, add);
831
832         // Update group indexed flow database
833         updateGroupFlowsDB(entry, add);
834     }
835
836     /*
837      * Update the node mapped flows database
838      */
839     private void updateSwViewes(FlowEntryInstall flowEntries, boolean add) {
840         if (add) {
841             originalSwView.put(flowEntries.getOriginal(), flowEntries.getOriginal());
842             installedSwView.put(flowEntries, flowEntries);
843         } else {
844             originalSwView.remove(flowEntries.getOriginal());
845             installedSwView.remove(flowEntries);
846         }
847     }
848
849     /*
850      * Update the node mapped flows database
851      */
852     private void updateNodeFlowsDB(FlowEntryInstall flowEntries, boolean add) {
853         Node node = flowEntries.getNode();
854
855         List<FlowEntryInstall> nodeIndeces = this.nodeFlows.get(node);
856         if (nodeIndeces == null) {
857             if (!add) {
858                 return;
859             } else {
860                 nodeIndeces = new ArrayList<FlowEntryInstall>();
861             }
862         }
863
864         if (add) {
865             nodeIndeces.add(flowEntries);
866         } else {
867             nodeIndeces.remove(flowEntries);
868         }
869
870         // Update cache across cluster
871         if (nodeIndeces.isEmpty()) {
872             this.nodeFlows.remove(node);
873         } else {
874             this.nodeFlows.put(node, nodeIndeces);
875         }
876     }
877
878     /*
879      * Update the group name mapped flows database
880      */
881     private void updateGroupFlowsDB(FlowEntryInstall flowEntries, boolean add) {
882         String groupName = flowEntries.getGroupName();
883
884         // Flow may not be part of a group
885         if (groupName == null) {
886             return;
887         }
888
889         List<FlowEntryInstall> indices = this.groupFlows.get(groupName);
890         if (indices == null) {
891             if (!add) {
892                 return;
893             } else {
894                 indices = new ArrayList<FlowEntryInstall>();
895             }
896         }
897
898         if (add) {
899             indices.add(flowEntries);
900         } else {
901             indices.remove(flowEntries);
902         }
903
904         // Update cache across cluster
905         if (indices.isEmpty()) {
906             this.groupFlows.remove(groupName);
907         } else {
908             this.groupFlows.put(groupName, indices);
909         }
910     }
911
912     /**
913      * Remove a flow entry that has been added previously First checks if the
914      * entry is effectively present in the local database
915      */
916     @SuppressWarnings("unused")
917     private Status removeEntry(Node node, String flowName) {
918         FlowEntryInstall target = null;
919
920         // Find in database
921         for (FlowEntryInstall entry : installedSwView.values()) {
922             if (entry.equalsByNodeAndName(node, flowName)) {
923                 target = entry;
924                 break;
925             }
926         }
927
928         // If it is not there, stop any further processing
929         if (target == null) {
930             return new Status(StatusCode.SUCCESS, "Entry is not present");
931         }
932
933         // Remove from node
934         Status status = programmer.removeFlow(target.getNode(), target.getInstall().getFlow());
935
936         // Update DB
937         if (status.isSuccess()) {
938             updateLocalDatabase(target, false);
939         } else {
940             // log the error
941             log.trace("SDN Plugin failed to remove the flow: {}. The failure is: {}", target.getInstall(),
942                     status.getDescription());
943         }
944
945         return status;
946     }
947
948     @Override
949     public Status installFlowEntry(FlowEntry flowEntry) {
950         Status status;
951         if (isContainerModeAllowed(flowEntry)) {
952             status = addEntry(flowEntry, false);
953         } else {
954             String msg = "Controller in container mode: Install Refused";
955             String logMsg = msg + ": {}";
956             status = new Status(StatusCode.NOTACCEPTABLE, msg);
957             log.warn(logMsg, flowEntry);
958         }
959         return status;
960     }
961
962     @Override
963     public Status installFlowEntryAsync(FlowEntry flowEntry) {
964         Status status;
965         if (isContainerModeAllowed(flowEntry)) {
966             status = addEntry(flowEntry, true);
967         } else {
968             String msg = "Controller in container mode: Install Refused";
969             status = new Status(StatusCode.NOTACCEPTABLE, msg);
970             log.warn(msg);
971         }
972         return status;
973     }
974
975     @Override
976     public Status uninstallFlowEntry(FlowEntry flowEntry) {
977         Status status;
978         if (isContainerModeAllowed(flowEntry)) {
979             status = removeEntry(flowEntry, false);
980         } else {
981             String msg = "Controller in container mode: Uninstall Refused";
982             String logMsg = msg + ": {}";
983             status = new Status(StatusCode.NOTACCEPTABLE, msg);
984             log.warn(logMsg, flowEntry);
985         }
986         return status;
987     }
988
989     @Override
990     public Status uninstallFlowEntryAsync(FlowEntry flowEntry) {
991         Status status;
992         if (isContainerModeAllowed(flowEntry)) {
993             status = removeEntry(flowEntry, true);
994         } else {
995             String msg = "Controller in container mode: Uninstall Refused";
996             status = new Status(StatusCode.NOTACCEPTABLE, msg);
997             log.warn(msg);
998         }
999         return status;
1000     }
1001
1002     @Override
1003     public Status modifyFlowEntry(FlowEntry currentFlowEntry, FlowEntry newFlowEntry) {
1004         Status status = null;
1005         if (isContainerModeAllowed(currentFlowEntry)) {
1006             status = modifyEntry(currentFlowEntry, newFlowEntry, false);
1007         } else {
1008             String msg = "Controller in container mode: Modify Refused";
1009             String logMsg = msg + ": {}";
1010             status = new Status(StatusCode.NOTACCEPTABLE, msg);
1011             log.warn(logMsg, newFlowEntry);
1012         }
1013         return status;
1014     }
1015
1016     @Override
1017     public Status modifyFlowEntryAsync(FlowEntry currentFlowEntry, FlowEntry newFlowEntry) {
1018         Status status = null;
1019         if (isContainerModeAllowed(currentFlowEntry)) {
1020             status = modifyEntry(currentFlowEntry, newFlowEntry, true);
1021         } else {
1022             String msg = "Controller in container mode: Modify Refused";
1023             status = new Status(StatusCode.NOTACCEPTABLE, msg);
1024             log.warn(msg);
1025         }
1026         return status;
1027     }
1028
1029     /**
1030      * Returns whether the specified flow entry is allowed to be
1031      * installed/removed/modified based on the current container mode status.
1032      * This call always returns true in the container instance of forwarding
1033      * rules manager. It is meant for the global instance only (default
1034      * container) of forwarding rules manager. Idea is that for assuring
1035      * container isolation of traffic, flow installation in default container is
1036      * blocked when in container mode (containers are present). The only flows
1037      * that are allowed in container mode in the default container are the
1038      * proactive flows, the ones automatically installed on the network node
1039      * which forwarding mode has been configured to "proactive". These flows are
1040      * needed by controller to discover the nodes topology and to discover the
1041      * attached hosts for some SDN switches.
1042      *
1043      * @param flowEntry
1044      *            The flow entry to be installed/removed/modified
1045      * @return true if not in container mode or if flowEntry is internally
1046      *         generated
1047      */
1048     private boolean isContainerModeAllowed(FlowEntry flowEntry) {
1049         return (!inContainerMode) ? true : flowEntry.isInternal();
1050     }
1051
1052     @Override
1053     public Status modifyOrAddFlowEntry(FlowEntry newFlowEntry) {
1054         /*
1055          * Run a check on the original entries to decide whether to go with a
1056          * add or modify method. A loose check means only check against the
1057          * original flow entry requests and not against the installed flow
1058          * entries which are the result of the original entry merged with the
1059          * container flow(s) (if any). The modifyFlowEntry method in presence of
1060          * conflicts with the Container flows (if any) would revert back to a
1061          * delete + add pattern
1062          */
1063         FlowEntry currentFlowEntry = originalSwView.get(newFlowEntry);
1064
1065         if (currentFlowEntry != null) {
1066             return modifyFlowEntry(currentFlowEntry, newFlowEntry);
1067         } else {
1068             return installFlowEntry(newFlowEntry);
1069         }
1070     }
1071
1072     @Override
1073     public Status modifyOrAddFlowEntryAsync(FlowEntry newFlowEntry) {
1074         /*
1075          * Run a check on the original entries to decide whether to go with a
1076          * add or modify method. A loose check means only check against the
1077          * original flow entry requests and not against the installed flow
1078          * entries which are the result of the original entry merged with the
1079          * container flow(s) (if any). The modifyFlowEntry method in presence of
1080          * conflicts with the Container flows (if any) would revert back to a
1081          * delete + add pattern
1082          */
1083         FlowEntry currentFlowEntry = originalSwView.get(newFlowEntry);
1084
1085         if (currentFlowEntry != null) {
1086             return modifyFlowEntryAsync(currentFlowEntry, newFlowEntry);
1087         } else {
1088             return installFlowEntryAsync(newFlowEntry);
1089         }
1090     }
1091
1092     @Override
1093     public Status uninstallFlowEntryGroup(String groupName) {
1094         if (groupName == null || groupName.isEmpty()) {
1095             return new Status(StatusCode.BADREQUEST, "Invalid group name");
1096         }
1097         if (groupName.equals(FlowConfig.INTERNALSTATICFLOWGROUP)) {
1098             return new Status(StatusCode.BADREQUEST, "Internal static flows group cannot be deleted through this api");
1099         }
1100         if (inContainerMode) {
1101             String msg = "Controller in container mode: Group Uninstall Refused";
1102             String logMsg = msg + ": {}";
1103             log.warn(logMsg, groupName);
1104             return new Status(StatusCode.NOTACCEPTABLE, msg);
1105         }
1106         int toBeRemoved = 0;
1107         String error = "";
1108         if (groupFlows.containsKey(groupName)) {
1109             List<FlowEntryInstall> list = new ArrayList<FlowEntryInstall>(groupFlows.get(groupName));
1110             toBeRemoved = list.size();
1111             for (FlowEntryInstall entry : list) {
1112                 Status status = this.removeEntry(entry.getOriginal(), false);
1113                 if (status.isSuccess()) {
1114                     toBeRemoved -= 1;
1115                 } else {
1116                     error = status.getDescription();
1117                 }
1118             }
1119         }
1120         return (toBeRemoved == 0) ? new Status(StatusCode.SUCCESS) : new Status(StatusCode.INTERNALERROR,
1121                 "Not all the flows were removed: " + error);
1122     }
1123
1124     @Override
1125     public Status uninstallFlowEntryGroupAsync(String groupName) {
1126         if (groupName == null || groupName.isEmpty()) {
1127             return new Status(StatusCode.BADREQUEST, "Invalid group name");
1128         }
1129         if (groupName.equals(FlowConfig.INTERNALSTATICFLOWGROUP)) {
1130             return new Status(StatusCode.BADREQUEST, "Static flows group cannot be deleted through this api");
1131         }
1132         if (inContainerMode) {
1133             String msg = "Controller in container mode: Group Uninstall Refused";
1134             String logMsg = msg + ": {}";
1135             log.warn(logMsg, groupName);
1136             return new Status(StatusCode.NOTACCEPTABLE, msg);
1137         }
1138         if (groupFlows.containsKey(groupName)) {
1139             List<FlowEntryInstall> list = new ArrayList<FlowEntryInstall>(groupFlows.get(groupName));
1140             for (FlowEntryInstall entry : list) {
1141                 this.removeEntry(entry.getOriginal(), true);
1142             }
1143         }
1144         return new Status(StatusCode.SUCCESS);
1145     }
1146
1147     @Override
1148     public boolean checkFlowEntryConflict(FlowEntry flowEntry) {
1149         return entryConflictsWithContainerFlows(flowEntry);
1150     }
1151
1152     /**
1153      * Updates all installed flows because the container flow got updated This
1154      * is obtained in two phases on per node basis: 1) Uninstall of all flows 2)
1155      * Reinstall of all flows This is needed because a new container flows
1156      * merged flow may conflict with an existing old container flows merged flow
1157      * on the network node
1158      */
1159     protected void updateFlowsContainerFlow() {
1160         Set<FlowEntry> toReInstall = new HashSet<FlowEntry>();
1161         // First remove all installed entries
1162         for (ConcurrentMap.Entry<FlowEntryInstall, FlowEntryInstall> entry : installedSwView.entrySet()) {
1163             FlowEntryInstall current = entry.getValue();
1164             // Store the original entry
1165             toReInstall.add(current.getOriginal());
1166             // Remove the old couples. No validity checks to be run, use the
1167             // internal remove
1168             this.removeEntryInternal(current, false);
1169         }
1170         // Then reinstall the original entries
1171         for (FlowEntry entry : toReInstall) {
1172             // Reinstall the original flow entries, via the regular path: new
1173             // cFlow merge + validations
1174             this.installFlowEntry(entry);
1175         }
1176     }
1177
1178     private void nonClusterObjectCreate() {
1179         originalSwView = new ConcurrentHashMap<FlowEntry, FlowEntry>();
1180         installedSwView = new ConcurrentHashMap<FlowEntryInstall, FlowEntryInstall>();
1181         nodeFlows = new ConcurrentHashMap<Node, List<FlowEntryInstall>>();
1182         groupFlows = new ConcurrentHashMap<String, List<FlowEntryInstall>>();
1183         TSPolicies = new ConcurrentHashMap<String, Object>();
1184         staticFlowsOrdinal = new ConcurrentHashMap<Integer, Integer>();
1185         portGroupConfigs = new ConcurrentHashMap<String, PortGroupConfig>();
1186         portGroupData = new ConcurrentHashMap<PortGroupConfig, Map<Node, PortGroup>>();
1187         staticFlows = new ConcurrentHashMap<Integer, FlowConfig>();
1188         inactiveFlows = new ConcurrentHashMap<FlowEntry, FlowEntry>();
1189     }
1190
1191     private void registerWithOSGIConsole() {
1192         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
1193         bundleContext.registerService(CommandProvider.class.getName(), this, null);
1194     }
1195
1196     @Override
1197     public void setTSPolicyData(String policyname, Object o, boolean add) {
1198
1199         if (add) {
1200             /* Check if this policy already exists */
1201             if (!(TSPolicies.containsKey(policyname))) {
1202                 TSPolicies.put(policyname, o);
1203             }
1204         } else {
1205             TSPolicies.remove(policyname);
1206         }
1207         if (frmAware != null) {
1208             synchronized (frmAware) {
1209                 for (IForwardingRulesManagerAware frma : frmAware) {
1210                     try {
1211                         frma.policyUpdate(policyname, add);
1212                     } catch (Exception e) {
1213                         log.warn("Exception on callback", e);
1214                     }
1215                 }
1216             }
1217         }
1218     }
1219
1220     @Override
1221     public Map<String, Object> getTSPolicyData() {
1222         return TSPolicies;
1223     }
1224
1225     @Override
1226     public Object getTSPolicyData(String policyName) {
1227         if (TSPolicies.containsKey(policyName)) {
1228             return TSPolicies.get(policyName);
1229         } else {
1230             return null;
1231         }
1232     }
1233
1234     @Override
1235     public List<FlowEntry> getFlowEntriesForGroup(String policyName) {
1236         List<FlowEntry> list = new ArrayList<FlowEntry>();
1237         if (policyName != null && !policyName.trim().isEmpty()) {
1238             for (Map.Entry<FlowEntry, FlowEntry> entry : this.originalSwView.entrySet()) {
1239                 if (policyName.equals(entry.getKey().getGroupName())) {
1240                     list.add(entry.getKey().clone());
1241                 }
1242             }
1243         }
1244         return list;
1245     }
1246
1247     @Override
1248     public List<FlowEntry> getInstalledFlowEntriesForGroup(String policyName) {
1249         List<FlowEntry> list = new ArrayList<FlowEntry>();
1250         if (policyName != null && !policyName.trim().isEmpty()) {
1251             for (Map.Entry<FlowEntryInstall, FlowEntryInstall> entry : this.installedSwView.entrySet()) {
1252                 if (policyName.equals(entry.getKey().getGroupName())) {
1253                     list.add(entry.getKey().getInstall().clone());
1254                 }
1255             }
1256         }
1257         return list;
1258     }
1259
1260     @Override
1261     public void addOutputPort(Node node, String flowName, List<NodeConnector> portList) {
1262
1263         for (FlowEntryInstall flow : this.nodeFlows.get(node)) {
1264             if (flow.getFlowName().equals(flowName)) {
1265                 FlowEntry currentFlowEntry = flow.getOriginal();
1266                 FlowEntry newFlowEntry = currentFlowEntry.clone();
1267                 for (NodeConnector dstPort : portList) {
1268                     newFlowEntry.getFlow().addAction(new Output(dstPort));
1269                 }
1270                 Status error = modifyEntry(currentFlowEntry, newFlowEntry, false);
1271                 if (error.isSuccess()) {
1272                     log.info("Ports {} added to FlowEntry {}", portList, flowName);
1273                 } else {
1274                     log.warn("Failed to add ports {} to Flow entry {}. The failure is: {}", portList,
1275                             currentFlowEntry.toString(), error.getDescription());
1276                 }
1277                 return;
1278             }
1279         }
1280         log.warn("Failed to add ports to Flow {} on Node {}: Entry Not Found", flowName, node);
1281     }
1282
1283     @Override
1284     public void removeOutputPort(Node node, String flowName, List<NodeConnector> portList) {
1285         for (FlowEntryInstall index : this.nodeFlows.get(node)) {
1286             FlowEntryInstall flow = this.installedSwView.get(index);
1287             if (flow.getFlowName().equals(flowName)) {
1288                 FlowEntry currentFlowEntry = flow.getOriginal();
1289                 FlowEntry newFlowEntry = currentFlowEntry.clone();
1290                 for (NodeConnector dstPort : portList) {
1291                     Action action = new Output(dstPort);
1292                     newFlowEntry.getFlow().removeAction(action);
1293                 }
1294                 Status status = modifyEntry(currentFlowEntry, newFlowEntry, false);
1295                 if (status.isSuccess()) {
1296                     log.info("Ports {} removed from FlowEntry {}", portList, flowName);
1297                 } else {
1298                     log.warn("Failed to remove ports {} from Flow entry {}. The failure is: {}", portList,
1299                             currentFlowEntry.toString(), status.getDescription());
1300                 }
1301                 return;
1302             }
1303         }
1304         log.warn("Failed to remove ports from Flow {} on Node {}: Entry Not Found", flowName, node);
1305     }
1306
1307     /*
1308      * This function assumes the target flow has only one output port
1309      */
1310     @Override
1311     public void replaceOutputPort(Node node, String flowName, NodeConnector outPort) {
1312         FlowEntry currentFlowEntry = null;
1313         FlowEntry newFlowEntry = null;
1314
1315         // Find the flow
1316         for (FlowEntryInstall index : this.nodeFlows.get(node)) {
1317             FlowEntryInstall flow = this.installedSwView.get(index);
1318             if (flow.getFlowName().equals(flowName)) {
1319                 currentFlowEntry = flow.getOriginal();
1320                 break;
1321             }
1322         }
1323         if (currentFlowEntry == null) {
1324             log.warn("Failed to replace output port for flow {} on node {}: Entry Not Found", flowName, node);
1325             return;
1326         }
1327
1328         // Create a flow copy with the new output port
1329         newFlowEntry = currentFlowEntry.clone();
1330         Action target = null;
1331         for (Action action : newFlowEntry.getFlow().getActions()) {
1332             if (action.getType() == ActionType.OUTPUT) {
1333                 target = action;
1334                 break;
1335             }
1336         }
1337         newFlowEntry.getFlow().removeAction(target);
1338         newFlowEntry.getFlow().addAction(new Output(outPort));
1339
1340         // Modify on network node
1341         Status status = modifyEntry(currentFlowEntry, newFlowEntry, false);
1342
1343         if (status.isSuccess()) {
1344             log.info("Output port replaced with {} for flow {} on node {}", outPort, flowName, node);
1345         } else {
1346             log.warn("Failed to replace output port for flow {} on node {}. The failure is: {}", flowName, node,
1347                     status.getDescription());
1348         }
1349         return;
1350     }
1351
1352     @Override
1353     public NodeConnector getOutputPort(Node node, String flowName) {
1354         for (FlowEntryInstall index : this.nodeFlows.get(node)) {
1355             FlowEntryInstall flow = this.installedSwView.get(index);
1356             if (flow.getFlowName().equals(flowName)) {
1357                 for (Action action : flow.getOriginal().getFlow().getActions()) {
1358                     if (action.getType() == ActionType.OUTPUT) {
1359                         return ((Output) action).getPort();
1360                     }
1361                 }
1362             }
1363         }
1364         return null;
1365     }
1366
1367     private void cacheStartup() {
1368         allocateCaches();
1369         retrieveCaches();
1370     }
1371
1372     private void allocateCaches() {
1373         if (this.clusterContainerService == null) {
1374             log.warn("Un-initialized clusterContainerService, can't create cache");
1375             return;
1376         }
1377
1378         log.debug("Allocating caches for Container {}", container.getName());
1379
1380         try {
1381             clusterContainerService.createCache("frm.originalSwView",
1382                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1383
1384             clusterContainerService.createCache("frm.installedSwView",
1385                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1386
1387             clusterContainerService.createCache("frm.inactiveFlows",
1388                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1389
1390             clusterContainerService.createCache("frm.nodeFlows",
1391                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1392
1393             clusterContainerService.createCache("frm.groupFlows",
1394                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1395
1396             clusterContainerService.createCache("frm.staticFlows",
1397                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1398
1399             clusterContainerService.createCache("frm.staticFlowsOrdinal",
1400                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1401
1402             clusterContainerService.createCache("frm.portGroupConfigs",
1403                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1404
1405             clusterContainerService.createCache("frm.portGroupData",
1406                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1407
1408             clusterContainerService.createCache("frm.TSPolicies",
1409                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
1410
1411             clusterContainerService.createCache(WORK_STATUS_CACHE,
1412                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL, IClusterServices.cacheMode.ASYNC));
1413
1414             clusterContainerService.createCache(WORK_ORDER_CACHE,
1415                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL, IClusterServices.cacheMode.ASYNC));
1416
1417         } catch (CacheConfigException cce) {
1418             log.error("CacheConfigException");
1419         } catch (CacheExistException cce) {
1420             log.error("CacheExistException");
1421         }
1422     }
1423
1424     @SuppressWarnings({ "unchecked" })
1425     private void retrieveCaches() {
1426         ConcurrentMap<?, ?> map;
1427
1428         if (this.clusterContainerService == null) {
1429             log.warn("un-initialized clusterContainerService, can't retrieve cache");
1430             nonClusterObjectCreate();
1431             return;
1432         }
1433
1434         log.debug("Retrieving Caches for Container {}", container.getName());
1435
1436         map = clusterContainerService.getCache("frm.originalSwView");
1437         if (map != null) {
1438             originalSwView = (ConcurrentMap<FlowEntry, FlowEntry>) map;
1439         } else {
1440             log.error("Retrieval of frm.originalSwView cache failed for Container {}", container.getName());
1441         }
1442
1443         map = clusterContainerService.getCache("frm.installedSwView");
1444         if (map != null) {
1445             installedSwView = (ConcurrentMap<FlowEntryInstall, FlowEntryInstall>) map;
1446         } else {
1447             log.error("Retrieval of frm.installedSwView cache failed for Container {}", container.getName());
1448         }
1449
1450         map = clusterContainerService.getCache("frm.inactiveFlows");
1451         if (map != null) {
1452             inactiveFlows = (ConcurrentMap<FlowEntry, FlowEntry>) map;
1453         } else {
1454             log.error("Retrieval of frm.inactiveFlows cache failed for Container {}", container.getName());
1455         }
1456
1457         map = clusterContainerService.getCache("frm.nodeFlows");
1458         if (map != null) {
1459             nodeFlows = (ConcurrentMap<Node, List<FlowEntryInstall>>) map;
1460         } else {
1461             log.error("Retrieval of cache failed for Container {}", container.getName());
1462         }
1463
1464         map = clusterContainerService.getCache("frm.groupFlows");
1465         if (map != null) {
1466             groupFlows = (ConcurrentMap<String, List<FlowEntryInstall>>) map;
1467         } else {
1468             log.error("Retrieval of frm.groupFlows cache failed for Container {}", container.getName());
1469         }
1470
1471         map = clusterContainerService.getCache("frm.staticFlows");
1472         if (map != null) {
1473             staticFlows = (ConcurrentMap<Integer, FlowConfig>) map;
1474         } else {
1475             log.error("Retrieval of frm.staticFlows cache failed for Container {}", container.getName());
1476         }
1477
1478         map = clusterContainerService.getCache("frm.staticFlowsOrdinal");
1479         if (map != null) {
1480             staticFlowsOrdinal = (ConcurrentMap<Integer, Integer>) map;
1481         } else {
1482             log.error("Retrieval of frm.staticFlowsOrdinal cache failed for Container {}", container.getName());
1483         }
1484
1485         map = clusterContainerService.getCache("frm.portGroupConfigs");
1486         if (map != null) {
1487             portGroupConfigs = (ConcurrentMap<String, PortGroupConfig>) map;
1488         } else {
1489             log.error("Retrieval of frm.portGroupConfigs cache failed for Container {}", container.getName());
1490         }
1491
1492         map = clusterContainerService.getCache("frm.portGroupData");
1493         if (map != null) {
1494             portGroupData = (ConcurrentMap<PortGroupConfig, Map<Node, PortGroup>>) map;
1495         } else {
1496             log.error("Retrieval of frm.portGroupData allocation failed for Container {}", container.getName());
1497         }
1498
1499         map = clusterContainerService.getCache("frm.TSPolicies");
1500         if (map != null) {
1501             TSPolicies = (ConcurrentMap<String, Object>) map;
1502         } else {
1503             log.error("Retrieval of frm.TSPolicies cache failed for Container {}", container.getName());
1504         }
1505
1506         map = clusterContainerService.getCache(WORK_ORDER_CACHE);
1507         if (map != null) {
1508             workOrder = (ConcurrentMap<FlowEntryDistributionOrder, FlowEntryInstall>) map;
1509         } else {
1510             log.error("Retrieval of " + WORK_ORDER_CACHE + " cache failed for Container {}", container.getName());
1511         }
1512
1513         map = clusterContainerService.getCache(WORK_STATUS_CACHE);
1514         if (map != null) {
1515             workStatus = (ConcurrentMap<FlowEntryDistributionOrder, Status>) map;
1516         } else {
1517             log.error("Retrieval of " + WORK_STATUS_CACHE + " cache failed for Container {}", container.getName());
1518         }
1519     }
1520
1521     private boolean flowConfigExists(FlowConfig config) {
1522         // Flow name has to be unique on per node id basis
1523         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1524             if (entry.getValue().isByNameAndNodeIdEqual(config)) {
1525                 return true;
1526             }
1527         }
1528         return false;
1529     }
1530
1531     @Override
1532     public Status addStaticFlow(FlowConfig config) {
1533         // Configuration object validation
1534         Status status = config.validate(container);
1535         if (!status.isSuccess()) {
1536             log.warn("Invalid Configuration for flow {}. The failure is {}", config, status.getDescription());
1537             String error = "Invalid Configuration (" + status.getDescription() + ")";
1538             config.setStatus(error);
1539             return new Status(StatusCode.BADREQUEST, error);
1540         }
1541         return addStaticFlowInternal(config, false);
1542     }
1543
1544     /**
1545      * Private method to add a static flow configuration which does not run any
1546      * validation on the passed FlowConfig object. If restore is set to true,
1547      * configuration is stored in configuration database regardless the
1548      * installation on the network node was successful. This is useful at boot
1549      * when static flows are present in startup configuration and are read
1550      * before the switches connects.
1551      *
1552      * @param config
1553      *            The static flow configuration
1554      * @param restore
1555      *            if true, the configuration is stored regardless the
1556      *            installation on the network node was successful
1557      * @return The status of this request
1558      */
1559     private Status addStaticFlowInternal(FlowConfig config, boolean restore) {
1560         boolean multipleFlowPush = false;
1561         String error;
1562         Status status;
1563         config.setStatus(StatusCode.SUCCESS.toString());
1564
1565         // Presence check
1566         if (flowConfigExists(config)) {
1567             error = "Entry with this name on specified switch already exists";
1568             log.warn("Entry with this name on specified switch already exists: {}", config);
1569             config.setStatus(error);
1570             return new Status(StatusCode.CONFLICT, error);
1571         }
1572
1573         if ((config.getIngressPort() == null) && config.getPortGroup() != null) {
1574             for (String portGroupName : portGroupConfigs.keySet()) {
1575                 if (portGroupName.equalsIgnoreCase(config.getPortGroup())) {
1576                     multipleFlowPush = true;
1577                     break;
1578                 }
1579             }
1580             if (!multipleFlowPush) {
1581                 log.warn("Invalid Configuration(Invalid PortGroup Name) for flow {}", config);
1582                 error = "Invalid Configuration (Invalid PortGroup Name)";
1583                 config.setStatus(error);
1584                 return new Status(StatusCode.BADREQUEST, error);
1585             }
1586         }
1587
1588         /*
1589          * If requested program the entry in hardware first before updating the
1590          * StaticFlow DB
1591          */
1592         if (!multipleFlowPush) {
1593             // Program hw
1594             if (config.installInHw()) {
1595                 FlowEntry entry = config.getFlowEntry();
1596                 status = this.installFlowEntry(entry);
1597                 if (!status.isSuccess()) {
1598                     config.setStatus(status.getDescription());
1599                     if (!restore) {
1600                         return status;
1601                     }
1602                 }
1603             }
1604         }
1605
1606         /*
1607          * When the control reaches this point, either of the following
1608          * conditions is true 1. This is a single entry configuration (non
1609          * PortGroup) and the hardware installation is successful 2. This is a
1610          * multiple entry configuration (PortGroup) and hardware installation is
1611          * NOT done directly on this event. 3. The User prefers to retain the
1612          * configuration in Controller and skip hardware installation.
1613          *
1614          * Hence it is safe to update the StaticFlow DB at this point.
1615          *
1616          * Note : For the case of PortGrouping, it is essential to have this DB
1617          * populated before the PortGroupListeners can query for the DB
1618          * triggered using portGroupChanged event...
1619          */
1620         Integer ordinal = staticFlowsOrdinal.get(0);
1621         staticFlowsOrdinal.put(0, ++ordinal);
1622         staticFlows.put(ordinal, config);
1623
1624         if (multipleFlowPush) {
1625             PortGroupConfig pgconfig = portGroupConfigs.get(config.getPortGroup());
1626             Map<Node, PortGroup> existingData = portGroupData.get(pgconfig);
1627             if (existingData != null) {
1628                 portGroupChanged(pgconfig, existingData, true);
1629             }
1630         }
1631         return new Status(StatusCode.SUCCESS);
1632     }
1633
1634     private void addStaticFlowsToSwitch(Node node) {
1635         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1636             FlowConfig config = entry.getValue();
1637             if (config.isPortGroupEnabled()) {
1638                 continue;
1639             }
1640             if (config.getNode().equals(node)) {
1641                 if (config.installInHw() && !config.getStatus().equals(StatusCode.SUCCESS.toString())) {
1642                     Status status = this.installFlowEntryAsync(config.getFlowEntry());
1643                     config.setStatus(status.getDescription());
1644                 }
1645             }
1646         }
1647         // Update cluster cache
1648         refreshClusterStaticFlowsStatus(node);
1649     }
1650
1651     private void updateStaticFlowConfigsOnNodeDown(Node node) {
1652         log.trace("Updating Static Flow configs on node down: {}", node);
1653
1654         List<Integer> toRemove = new ArrayList<Integer>();
1655         for (Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1656
1657             FlowConfig config = entry.getValue();
1658
1659             if (config.isPortGroupEnabled()) {
1660                 continue;
1661             }
1662
1663             if (config.installInHw() && config.getNode().equals(node)) {
1664                 if (config.isInternalFlow()) {
1665                     // Take note of this controller generated static flow
1666                     toRemove.add(entry.getKey());
1667                 } else {
1668                     config.setStatus(NODEDOWN);
1669                 }
1670             }
1671         }
1672         // Remove controller generated static flows for this node
1673         for (Integer index : toRemove) {
1674             staticFlows.remove(index);
1675         }
1676         // Update cluster cache
1677         refreshClusterStaticFlowsStatus(node);
1678
1679     }
1680
1681     private void updateStaticFlowConfigsOnContainerModeChange(UpdateType update) {
1682         log.trace("Updating Static Flow configs on container mode change: {}", update);
1683
1684         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1685             FlowConfig config = entry.getValue();
1686             if (config.isPortGroupEnabled()) {
1687                 continue;
1688             }
1689             if (config.installInHw() && !config.isInternalFlow()) {
1690                 switch (update) {
1691                 case ADDED:
1692                     config.setStatus("Removed from node because in container mode");
1693                     break;
1694                 case REMOVED:
1695                     config.setStatus(StatusCode.SUCCESS.toString());
1696                     break;
1697                 default:
1698                 }
1699             }
1700         }
1701         // Update cluster cache
1702         refreshClusterStaticFlowsStatus(null);
1703     }
1704
1705     @Override
1706     public Status removeStaticFlow(FlowConfig config) {
1707         /*
1708          * No config.isInternal() check as NB does not take this path and GUI
1709          * cannot issue a delete on an internal generated flow. We need this
1710          * path to be accessible when switch mode is changed from proactive to
1711          * reactive, so that we can remove the internal generated LLDP and ARP
1712          * punt flows
1713          */
1714
1715         // Look for the target configuration entry
1716         Integer key = 0;
1717         FlowConfig target = null;
1718         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1719             if (entry.getValue().isByNameAndNodeIdEqual(config)) {
1720                 key = entry.getKey();
1721                 target = entry.getValue();
1722                 break;
1723             }
1724         }
1725         if (target == null) {
1726             return new Status(StatusCode.NOTFOUND, "Entry Not Present");
1727         }
1728
1729         // Program the network node
1730         Status status = this.uninstallFlowEntry(config.getFlowEntry());
1731
1732         // Update configuration database if programming was successful
1733         if (status.isSuccess()) {
1734             staticFlows.remove(key);
1735         }
1736
1737         return status;
1738     }
1739
1740     @Override
1741     public Status removeStaticFlow(String name, Node node) {
1742         // Look for the target configuration entry
1743         Integer key = 0;
1744         FlowConfig target = null;
1745         for (ConcurrentMap.Entry<Integer, FlowConfig> mapEntry : staticFlows.entrySet()) {
1746             if (mapEntry.getValue().isByNameAndNodeIdEqual(name, node)) {
1747                 key = mapEntry.getKey();
1748                 target = mapEntry.getValue();
1749                 break;
1750             }
1751         }
1752         if (target == null) {
1753             return new Status(StatusCode.NOTFOUND, "Entry Not Present");
1754         }
1755
1756         // Validity check for api3 entry point
1757         if (target.isInternalFlow()) {
1758             String msg = "Invalid operation: Controller generated flow cannot be deleted";
1759             String logMsg = msg + ": {}";
1760             log.warn(logMsg, name);
1761             return new Status(StatusCode.NOTACCEPTABLE, msg);
1762         }
1763
1764         if (target.isPortGroupEnabled()) {
1765             String msg = "Invalid operation: Port Group flows cannot be deleted through this API";
1766             String logMsg = msg + ": {}";
1767             log.warn(logMsg, name);
1768             return new Status(StatusCode.NOTACCEPTABLE, msg);
1769         }
1770
1771         // Program the network node
1772         Status status = this.removeEntry(target.getFlowEntry(), false);
1773
1774         // Update configuration database if programming was successful
1775         if (status.isSuccess()) {
1776             staticFlows.remove(key);
1777         }
1778
1779         return status;
1780     }
1781
1782     @Override
1783     public Status modifyStaticFlow(FlowConfig newFlowConfig) {
1784         // Validity check for api3 entry point
1785         if (newFlowConfig.isInternalFlow()) {
1786             String msg = "Invalid operation: Controller generated flow cannot be modified";
1787             String logMsg = msg + ": {}";
1788             log.warn(logMsg, newFlowConfig);
1789             return new Status(StatusCode.NOTACCEPTABLE, msg);
1790         }
1791
1792         // Validity Check
1793         Status status = newFlowConfig.validate(container);
1794         if (!status.isSuccess()) {
1795             String msg = "Invalid Configuration (" + status.getDescription() + ")";
1796             newFlowConfig.setStatus(msg);
1797             log.warn("Invalid Configuration for flow {}. The failure is {}", newFlowConfig, status.getDescription());
1798             return new Status(StatusCode.BADREQUEST, msg);
1799         }
1800
1801         FlowConfig oldFlowConfig = null;
1802         Integer index = null;
1803         for (ConcurrentMap.Entry<Integer, FlowConfig> mapEntry : staticFlows.entrySet()) {
1804             FlowConfig entry = mapEntry.getValue();
1805             if (entry.isByNameAndNodeIdEqual(newFlowConfig.getName(), newFlowConfig.getNode())) {
1806                 oldFlowConfig = entry;
1807                 index = mapEntry.getKey();
1808                 break;
1809             }
1810         }
1811
1812         if (oldFlowConfig == null) {
1813             String msg = "Attempt to modify a non existing static flow";
1814             String logMsg = msg + ": {}";
1815             log.warn(logMsg, newFlowConfig);
1816             return new Status(StatusCode.NOTFOUND, msg);
1817         }
1818
1819         // Do not attempt to reinstall the flow, warn user
1820         if (newFlowConfig.equals(oldFlowConfig)) {
1821             String msg = "No modification detected";
1822             log.info("Static flow modification skipped. New flow and old flow are the same: {}", newFlowConfig);
1823             return new Status(StatusCode.SUCCESS, msg);
1824         }
1825
1826         // If flow is installed, program the network node
1827         status = new Status(StatusCode.SUCCESS, "Saved in config");
1828         if (oldFlowConfig.installInHw()) {
1829             status = this.modifyFlowEntry(oldFlowConfig.getFlowEntry(), newFlowConfig.getFlowEntry());
1830         }
1831
1832         // Update configuration database if programming was successful
1833         if (status.isSuccess()) {
1834             newFlowConfig.setStatus(status.getDescription());
1835             staticFlows.put(index, newFlowConfig);
1836         }
1837
1838         return status;
1839     }
1840
1841     @Override
1842     public Status toggleStaticFlowStatus(String name, Node node) {
1843         return toggleStaticFlowStatus(getStaticFlow(name, node));
1844     }
1845
1846     @Override
1847     public Status toggleStaticFlowStatus(FlowConfig config) {
1848         if (config == null) {
1849             String msg = "Invalid request: null flow config";
1850             log.warn(msg);
1851             return new Status(StatusCode.BADREQUEST, msg);
1852         }
1853         // Validity check for api3 entry point
1854         if (config.isInternalFlow()) {
1855             String msg = "Invalid operation: Controller generated flow cannot be modified";
1856             String logMsg = msg + ": {}";
1857             log.warn(logMsg, config);
1858             return new Status(StatusCode.NOTACCEPTABLE, msg);
1859         }
1860
1861         // Find the config entry
1862         Integer key = 0;
1863         FlowConfig target = null;
1864         for (Map.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1865             FlowConfig conf = entry.getValue();
1866             if (conf.isByNameAndNodeIdEqual(config)) {
1867                 key = entry.getKey();
1868                 target = conf;
1869                 break;
1870             }
1871         }
1872         if (target != null) {
1873             Status status = target.validate(container);
1874             if (!status.isSuccess()) {
1875                 log.warn(status.getDescription());
1876                 return status;
1877             }
1878             status = (target.installInHw()) ? this.uninstallFlowEntry(target.getFlowEntry()) : this
1879                                     .installFlowEntry(target.getFlowEntry());
1880             if (status.isSuccess()) {
1881                 // Update Configuration database
1882                 target.setStatus(StatusCode.SUCCESS.toString());
1883                 target.toggleInstallation();
1884                 staticFlows.put(key, target);
1885             }
1886             return status;
1887         }
1888
1889         return new Status(StatusCode.NOTFOUND, "Unable to locate the entry. Failed to toggle status");
1890     }
1891
1892     /**
1893      * Reinsert all static flows entries in the cache to force cache updates in
1894      * the cluster. This is useful when only some parameters were changed in the
1895      * entries, like the status.
1896      *
1897      * @param node
1898      *            The node for which the static flow configurations have to be
1899      *            refreshed. If null, all nodes static flows will be refreshed.
1900      */
1901     private void refreshClusterStaticFlowsStatus(Node node) {
1902         // Refresh cluster cache
1903         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1904             if (node == null || entry.getValue().getNode().equals(node)) {
1905                 staticFlows.put(entry.getKey(), entry.getValue());
1906             }
1907         }
1908     }
1909
1910     /**
1911      * Uninstall all the non-internal Flow Entries present in the software view.
1912      * If requested, a copy of each original flow entry will be stored in the
1913      * inactive list so that it can be re-applied when needed (This is typically
1914      * the case when running in the default container and controller moved to
1915      * container mode) NOTE WELL: The routine as long as does a bulk change will
1916      * operate only on the entries for nodes locally attached so to avoid
1917      * redundant operations initiated by multiple nodes
1918      *
1919      * @param preserveFlowEntries
1920      *            if true, a copy of each original entry is stored in the
1921      *            inactive list
1922      */
1923     private void uninstallAllFlowEntries(boolean preserveFlowEntries) {
1924         log.info("Uninstalling all non-internal flows");
1925
1926         List<FlowEntryInstall> toRemove = new ArrayList<FlowEntryInstall>();
1927
1928         // Store entries / create target list
1929         for (ConcurrentMap.Entry<FlowEntryInstall, FlowEntryInstall> mapEntry : installedSwView.entrySet()) {
1930             FlowEntryInstall flowEntries = mapEntry.getValue();
1931             // Skip internal generated static flows
1932             if (!flowEntries.isInternal()) {
1933                 toRemove.add(flowEntries);
1934                 // Store the original entries if requested
1935                 if (preserveFlowEntries) {
1936                     inactiveFlows.put(flowEntries.getOriginal(), flowEntries.getOriginal());
1937                 }
1938             }
1939         }
1940
1941         // Now remove the entries
1942         for (FlowEntryInstall flowEntryHw : toRemove) {
1943             Node n = flowEntryHw.getNode();
1944             if (n != null && connectionManager.getLocalityStatus(n) == ConnectionLocality.LOCAL) {
1945                 Status status = this.removeEntryInternal(flowEntryHw, false);
1946                 if (!status.isSuccess()) {
1947                     log.trace("Failed to remove entry: {}. The failure is: {}", flowEntryHw, status.getDescription());
1948                 }
1949             } else {
1950                 log.debug("Not removing entry {} because not connected locally, the remote guy will do it's job",
1951                         flowEntryHw);
1952             }
1953         }
1954     }
1955
1956     /**
1957      * Re-install all the Flow Entries present in the inactive list The inactive
1958      * list will be empty at the end of this call This function is called on the
1959      * default container instance of FRM only when the last container is deleted
1960      */
1961     private void reinstallAllFlowEntries() {
1962         log.info("Reinstalling all inactive flows");
1963
1964         for (FlowEntry flowEntry : this.inactiveFlows.keySet()) {
1965             this.addEntry(flowEntry, false);
1966         }
1967
1968         // Empty inactive list in any case
1969         inactiveFlows.clear();
1970     }
1971
1972     @Override
1973     public List<FlowConfig> getStaticFlows() {
1974         return getStaticFlowsOrderedList(staticFlows, staticFlowsOrdinal.get(0).intValue());
1975     }
1976
1977     // TODO: need to come out with a better algorithm for maintaining the order
1978     // of the configuration entries
1979     // with actual one, index associated to deleted entries cannot be reused and
1980     // map grows...
1981     private List<FlowConfig> getStaticFlowsOrderedList(ConcurrentMap<Integer, FlowConfig> flowMap, int maxKey) {
1982         List<FlowConfig> orderedList = new ArrayList<FlowConfig>();
1983         for (int i = 0; i <= maxKey; i++) {
1984             FlowConfig entry = flowMap.get(i);
1985             if (entry != null) {
1986                 orderedList.add(entry);
1987             }
1988         }
1989         return orderedList;
1990     }
1991
1992     @Override
1993     public FlowConfig getStaticFlow(String name, Node node) {
1994         ConcurrentMap.Entry<Integer, FlowConfig> entry = getStaticFlowEntry(name, node);
1995         if(entry != null) {
1996             return entry.getValue();
1997         }
1998         return null;
1999     }
2000
2001     @Override
2002     public List<FlowConfig> getStaticFlows(Node node) {
2003         List<FlowConfig> list = new ArrayList<FlowConfig>();
2004         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
2005             if (entry.getValue().onNode(node)) {
2006                 list.add(entry.getValue());
2007             }
2008         }
2009         return list;
2010     }
2011
2012     @Override
2013     public List<String> getStaticFlowNamesForNode(Node node) {
2014         List<String> list = new ArrayList<String>();
2015         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
2016             if (entry.getValue().onNode(node)) {
2017                 list.add(entry.getValue().getName());
2018             }
2019         }
2020         return list;
2021     }
2022
2023     @Override
2024     public List<Node> getListNodeWithConfiguredFlows() {
2025         Set<Node> set = new HashSet<Node>();
2026         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
2027             set.add(entry.getValue().getNode());
2028         }
2029         return new ArrayList<Node>(set);
2030     }
2031
2032     @SuppressWarnings("unchecked")
2033     private void loadFlowConfiguration() {
2034         ObjectReader objReader = new ObjectReader();
2035         ConcurrentMap<Integer, FlowConfig> confList = (ConcurrentMap<Integer, FlowConfig>) objReader.read(this,
2036                 frmFileName);
2037
2038         ConcurrentMap<String, PortGroupConfig> pgConfig = (ConcurrentMap<String, PortGroupConfig>) objReader.read(this,
2039                 portGroupFileName);
2040
2041         if (pgConfig != null) {
2042             for (ConcurrentMap.Entry<String, PortGroupConfig> entry : pgConfig.entrySet()) {
2043                 addPortGroupConfig(entry.getKey(), entry.getValue().getMatchString(), true);
2044             }
2045         }
2046
2047         if (confList == null) {
2048             return;
2049         }
2050
2051         int maxKey = 0;
2052         for (Integer key : confList.keySet()) {
2053             if (key.intValue() > maxKey) {
2054                 maxKey = key.intValue();
2055             }
2056         }
2057
2058         for (FlowConfig conf : getStaticFlowsOrderedList(confList, maxKey)) {
2059             addStaticFlowInternal(conf, true);
2060         }
2061     }
2062
2063     @Override
2064     public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
2065         return ois.readObject();
2066     }
2067
2068     @Override
2069     public Status saveConfig() {
2070         return saveConfigInternal();
2071     }
2072
2073     private Status saveConfigInternal() {
2074         ObjectWriter objWriter = new ObjectWriter();
2075         ConcurrentMap<Integer, FlowConfig> nonDynamicFlows = new ConcurrentHashMap<Integer, FlowConfig>();
2076         for (Integer ordinal : staticFlows.keySet()) {
2077             FlowConfig config = staticFlows.get(ordinal);
2078             // Do not save dynamic and controller generated static flows
2079             if (config.isDynamic() || config.isInternalFlow()) {
2080                 continue;
2081             }
2082             nonDynamicFlows.put(ordinal, config);
2083         }
2084         objWriter.write(nonDynamicFlows, frmFileName);
2085         objWriter.write(new ConcurrentHashMap<String, PortGroupConfig>(portGroupConfigs), portGroupFileName);
2086         return new Status(StatusCode.SUCCESS, null);
2087     }
2088
2089     @Override
2090     public void subnetNotify(Subnet sub, boolean add) {
2091     }
2092
2093     private void installImplicitARPReplyPunt(Node node) {
2094
2095         if (node == null) {
2096             return;
2097         }
2098
2099         List<String> puntAction = new ArrayList<String>();
2100         puntAction.add(ActionType.CONTROLLER.toString());
2101
2102         FlowConfig allowARP = new FlowConfig();
2103         allowARP.setInstallInHw(true);
2104         allowARP.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Punt ARP Reply" + FlowConfig.INTERNALSTATICFLOWEND);
2105         allowARP.setPriority("500");
2106         allowARP.setNode(node);
2107         allowARP.setEtherType("0x" + Integer.toHexString(EtherTypes.ARP.intValue()).toUpperCase());
2108         allowARP.setDstMac(HexEncode.bytesToHexString(switchManager.getControllerMAC()));
2109         allowARP.setActions(puntAction);
2110         addStaticFlowInternal(allowARP, true); // skip validation on internal static flow name
2111     }
2112
2113     /**
2114      * (non-Javadoc)
2115      *
2116      * @see org.opendaylight.controller.switchmanager.ISwitchManagerAware#modeChangeNotify(org.opendaylight.controller.sal.core.Node,
2117      *      boolean)
2118      *
2119      *      This method can be called from within the OSGi framework context,
2120      *      given the programming operation can take sometime, it not good
2121      *      pratice to have in it's context operations that can take time,
2122      *      hence moving off to a different thread for async processing.
2123      */
2124     private ExecutorService executor;
2125     @Override
2126     public void modeChangeNotify(final Node node, final boolean proactive) {
2127         Callable<Status> modeChangeCallable = new Callable<Status>() {
2128             @Override
2129             public Status call() throws Exception {
2130                 List<FlowConfig> defaultConfigs = new ArrayList<FlowConfig>();
2131
2132                 List<String> puntAction = new ArrayList<String>();
2133                 puntAction.add(ActionType.CONTROLLER.toString());
2134
2135                 FlowConfig allowARP = new FlowConfig();
2136                 allowARP.setInstallInHw(true);
2137                 allowARP.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Punt ARP" + FlowConfig.INTERNALSTATICFLOWEND);
2138                 allowARP.setPriority("1");
2139                 allowARP.setNode(node);
2140                 allowARP.setEtherType("0x" + Integer.toHexString(EtherTypes.ARP.intValue())
2141                         .toUpperCase());
2142                 allowARP.setActions(puntAction);
2143                 defaultConfigs.add(allowARP);
2144
2145                 FlowConfig allowLLDP = new FlowConfig();
2146                 allowLLDP.setInstallInHw(true);
2147                 allowLLDP.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Punt LLDP" + FlowConfig.INTERNALSTATICFLOWEND);
2148                 allowLLDP.setPriority("1");
2149                 allowLLDP.setNode(node);
2150                 allowLLDP.setEtherType("0x" + Integer.toHexString(EtherTypes.LLDP.intValue())
2151                         .toUpperCase());
2152                 allowLLDP.setActions(puntAction);
2153                 defaultConfigs.add(allowLLDP);
2154
2155                 List<String> dropAction = new ArrayList<String>();
2156                 dropAction.add(ActionType.DROP.toString());
2157
2158                 FlowConfig dropAllConfig = new FlowConfig();
2159                 dropAllConfig.setInstallInHw(true);
2160                 dropAllConfig.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Catch-All Drop"
2161                         + FlowConfig.INTERNALSTATICFLOWEND);
2162                 dropAllConfig.setPriority("0");
2163                 dropAllConfig.setNode(node);
2164                 dropAllConfig.setActions(dropAction);
2165                 defaultConfigs.add(dropAllConfig);
2166
2167                 log.info("Forwarding mode for node {} set to {}", node, (proactive ? "proactive" : "reactive"));
2168                 for (FlowConfig fc : defaultConfigs) {
2169                     Status status = (proactive) ? addStaticFlowInternal(fc, false) : removeStaticFlow(fc);
2170                     if (status.isSuccess()) {
2171                         log.info("{} Proactive Static flow: {}", (proactive ? "Installed" : "Removed"), fc.getName());
2172                     } else {
2173                         log.warn("Failed to {} Proactive Static flow: {}", (proactive ? "install" : "remove"),
2174                                 fc.getName());
2175                     }
2176                 }
2177                 return new Status(StatusCode.SUCCESS);
2178             }
2179         };
2180
2181         /*
2182          * Execute the work outside the caller context, this could be an
2183          * expensive operation and we don't want to block the caller for it.
2184          */
2185         this.executor.submit(modeChangeCallable);
2186     }
2187
2188     /**
2189      * Remove from the databases all the flows installed on the node
2190      *
2191      * @param node
2192      */
2193     private void cleanDatabaseForNode(Node node) {
2194         log.info("Cleaning Flow database for Node {}", node);
2195         if (nodeFlows.containsKey(node)) {
2196             List<FlowEntryInstall> toRemove = new ArrayList<FlowEntryInstall>(nodeFlows.get(node));
2197
2198             for (FlowEntryInstall entry : toRemove) {
2199                 updateLocalDatabase(entry, false);
2200             }
2201         }
2202     }
2203
2204     private boolean doesFlowContainNodeConnector(Flow flow, NodeConnector nc) {
2205         if (nc == null) {
2206             return false;
2207         }
2208
2209         Match match = flow.getMatch();
2210         if (match.isPresent(MatchType.IN_PORT)) {
2211             NodeConnector matchPort = (NodeConnector) match.getField(MatchType.IN_PORT).getValue();
2212             if (matchPort.equals(nc)) {
2213                 return true;
2214             }
2215         }
2216         List<Action> actionsList = flow.getActions();
2217         if (actionsList != null) {
2218             for (Action action : actionsList) {
2219                 if (action instanceof Output) {
2220                     NodeConnector actionPort = ((Output) action).getPort();
2221                     if (actionPort.equals(nc)) {
2222                         return true;
2223                     }
2224                 }
2225             }
2226         }
2227         return false;
2228     }
2229
2230     @Override
2231     public void notifyNode(Node node, UpdateType type, Map<String, Property> propMap) {
2232         this.pendingEvents.offer(new NodeUpdateEvent(type, node));
2233     }
2234
2235     @Override
2236     public void notifyNodeConnector(NodeConnector nodeConnector, UpdateType type, Map<String, Property> propMap) {
2237         boolean updateStaticFlowCluster = false;
2238
2239         switch (type) {
2240         case ADDED:
2241             break;
2242         case CHANGED:
2243             Config config = (propMap == null) ? null : (Config) propMap.get(Config.ConfigPropName);
2244             if (config != null) {
2245                 switch (config.getValue()) {
2246                 case Config.ADMIN_DOWN:
2247                     log.trace("Port {} is administratively down: uninstalling interested flows", nodeConnector);
2248                     updateStaticFlowCluster = removeFlowsOnNodeConnectorDown(nodeConnector);
2249                     break;
2250                 case Config.ADMIN_UP:
2251                     log.trace("Port {} is administratively up: installing interested flows", nodeConnector);
2252                     updateStaticFlowCluster = installFlowsOnNodeConnectorUp(nodeConnector);
2253                     break;
2254                 case Config.ADMIN_UNDEF:
2255                     break;
2256                 default:
2257                 }
2258             }
2259             break;
2260         case REMOVED:
2261             // This is the case where a switch port is removed from the SDN agent space
2262             log.trace("Port {} was removed from our control: uninstalling interested flows", nodeConnector);
2263             updateStaticFlowCluster = removeFlowsOnNodeConnectorDown(nodeConnector);
2264             break;
2265         default:
2266
2267         }
2268
2269         if (updateStaticFlowCluster) {
2270             refreshClusterStaticFlowsStatus(nodeConnector.getNode());
2271         }
2272     }
2273
2274     /*
2275      * It goes through the static flows configuration, it identifies the ones
2276      * which have the specified node connector as input or output port and
2277      * install them on the network node if they are marked to be installed in
2278      * hardware and their status shows they were not installed yet
2279      */
2280     private boolean installFlowsOnNodeConnectorUp(NodeConnector nodeConnector) {
2281         boolean updated = false;
2282         List<FlowConfig> flowConfigForNode = getStaticFlows(nodeConnector.getNode());
2283         for (FlowConfig flowConfig : flowConfigForNode) {
2284             if (doesFlowContainNodeConnector(flowConfig.getFlow(), nodeConnector)) {
2285                 if (flowConfig.installInHw() && !flowConfig.getStatus().equals(StatusCode.SUCCESS.toString())) {
2286                     Status status = this.installFlowEntry(flowConfig.getFlowEntry());
2287                     if (!status.isSuccess()) {
2288                         flowConfig.setStatus(status.getDescription());
2289                     } else {
2290                         flowConfig.setStatus(StatusCode.SUCCESS.toString());
2291                     }
2292                     updated = true;
2293                 }
2294             }
2295         }
2296         return updated;
2297     }
2298
2299     /*
2300      * Remove from the network node all the flows which have the specified node
2301      * connector as input or output port. If any of the flow entry is a static
2302      * flow, it updates the correspondent configuration.
2303      */
2304     private boolean removeFlowsOnNodeConnectorDown(NodeConnector nodeConnector) {
2305         boolean updated = false;
2306         List<FlowEntryInstall> nodeFlowEntries = nodeFlows.get(nodeConnector.getNode());
2307         if (nodeFlowEntries == null) {
2308             return updated;
2309         }
2310         for (FlowEntryInstall fei : new ArrayList<FlowEntryInstall>(nodeFlowEntries)) {
2311             if (doesFlowContainNodeConnector(fei.getInstall().getFlow(), nodeConnector)) {
2312                 Status status = this.removeEntryInternal(fei, true);
2313                 if (!status.isSuccess()) {
2314                     continue;
2315                 }
2316                 /*
2317                  * If the flow entry is a static flow, then update its
2318                  * configuration
2319                  */
2320                 if (fei.getGroupName().equals(FlowConfig.STATICFLOWGROUP)) {
2321                     FlowConfig flowConfig = getStaticFlow(fei.getFlowName(), fei.getNode());
2322                     if (flowConfig != null) {
2323                         flowConfig.setStatus(PORTREMOVED);
2324                         updated = true;
2325                     }
2326                 }
2327             }
2328         }
2329         return updated;
2330     }
2331
2332     private FlowConfig getDerivedFlowConfig(FlowConfig original, String configName, Short port) {
2333         FlowConfig derivedFlow = new FlowConfig(original);
2334         derivedFlow.setDynamic(true);
2335         derivedFlow.setPortGroup(null);
2336         derivedFlow.setName(original.getName() + "_" + configName + "_" + port);
2337         derivedFlow.setIngressPort(port + "");
2338         return derivedFlow;
2339     }
2340
2341     private void addPortGroupFlows(PortGroupConfig config, Node node, PortGroup data) {
2342         for (FlowConfig staticFlow : staticFlows.values()) {
2343             if (staticFlow.getPortGroup() == null) {
2344                 continue;
2345             }
2346             if ((staticFlow.getNode().equals(node)) && (staticFlow.getPortGroup().equals(config.getName()))) {
2347                 for (Short port : data.getPorts()) {
2348                     FlowConfig derivedFlow = getDerivedFlowConfig(staticFlow, config.getName(), port);
2349                     addStaticFlowInternal(derivedFlow, false);
2350                 }
2351             }
2352         }
2353     }
2354
2355     private void removePortGroupFlows(PortGroupConfig config, Node node, PortGroup data) {
2356         for (FlowConfig staticFlow : staticFlows.values()) {
2357             if (staticFlow.getPortGroup() == null) {
2358                 continue;
2359             }
2360             if (staticFlow.getNode().equals(node) && staticFlow.getPortGroup().equals(config.getName())) {
2361                 for (Short port : data.getPorts()) {
2362                     FlowConfig derivedFlow = getDerivedFlowConfig(staticFlow, config.getName(), port);
2363                     removeStaticFlow(derivedFlow);
2364                 }
2365             }
2366         }
2367     }
2368
2369     @Override
2370     public void portGroupChanged(PortGroupConfig config, Map<Node, PortGroup> data, boolean add) {
2371         log.info("PortGroup Changed for: {} Data: {}", config, portGroupData);
2372         Map<Node, PortGroup> existingData = portGroupData.get(config);
2373         if (existingData != null) {
2374             for (Map.Entry<Node, PortGroup> entry : data.entrySet()) {
2375                 PortGroup existingPortGroup = existingData.get(entry.getKey());
2376                 if (existingPortGroup == null) {
2377                     if (add) {
2378                         existingData.put(entry.getKey(), entry.getValue());
2379                         addPortGroupFlows(config, entry.getKey(), entry.getValue());
2380                     }
2381                 } else {
2382                     if (add) {
2383                         existingPortGroup.getPorts().addAll(entry.getValue().getPorts());
2384                         addPortGroupFlows(config, entry.getKey(), entry.getValue());
2385                     } else {
2386                         existingPortGroup.getPorts().removeAll(entry.getValue().getPorts());
2387                         removePortGroupFlows(config, entry.getKey(), entry.getValue());
2388                     }
2389                 }
2390             }
2391         } else {
2392             if (add) {
2393                 portGroupData.put(config, data);
2394                 for (Node swid : data.keySet()) {
2395                     addPortGroupFlows(config, swid, data.get(swid));
2396                 }
2397             }
2398         }
2399     }
2400
2401     @Override
2402     public boolean addPortGroupConfig(String name, String regex, boolean restore) {
2403         PortGroupConfig config = portGroupConfigs.get(name);
2404         if (config != null) {
2405             return false;
2406         }
2407
2408         if ((portGroupProvider == null) && !restore) {
2409             return false;
2410         }
2411         if ((portGroupProvider != null) && (!portGroupProvider.isMatchCriteriaSupported(regex))) {
2412             return false;
2413         }
2414
2415         config = new PortGroupConfig(name, regex);
2416         portGroupConfigs.put(name, config);
2417         if (portGroupProvider != null) {
2418             portGroupProvider.createPortGroupConfig(config);
2419         }
2420         return true;
2421     }
2422
2423     @Override
2424     public boolean delPortGroupConfig(String name) {
2425         PortGroupConfig config = portGroupConfigs.get(name);
2426         if (config == null) {
2427             return false;
2428         }
2429
2430         if (portGroupProvider != null) {
2431             portGroupProvider.deletePortGroupConfig(config);
2432         }
2433         portGroupConfigs.remove(name);
2434         return true;
2435     }
2436
2437     private void usePortGroupConfig(String name) {
2438         PortGroupConfig config = portGroupConfigs.get(name);
2439         if (config == null) {
2440             return;
2441         }
2442         if (portGroupProvider != null) {
2443             Map<Node, PortGroup> data = portGroupProvider.getPortGroupData(config);
2444             portGroupData.put(config, data);
2445         }
2446     }
2447
2448     @Override
2449     public Map<String, PortGroupConfig> getPortGroupConfigs() {
2450         return portGroupConfigs;
2451     }
2452
2453     public boolean isPortGroupSupported() {
2454         if (portGroupProvider == null) {
2455             return false;
2456         }
2457         return true;
2458     }
2459
2460     public void setIContainer(IContainer s) {
2461         this.container = s;
2462     }
2463
2464     public void unsetIContainer(IContainer s) {
2465         if (this.container == s) {
2466             this.container = null;
2467         }
2468     }
2469
2470     @Override
2471     public PortGroupProvider getPortGroupProvider() {
2472         return portGroupProvider;
2473     }
2474
2475     public void unsetPortGroupProvider(PortGroupProvider portGroupProvider) {
2476         this.portGroupProvider = null;
2477     }
2478
2479     public void setPortGroupProvider(PortGroupProvider portGroupProvider) {
2480         this.portGroupProvider = portGroupProvider;
2481         portGroupProvider.registerPortGroupChange(this);
2482         for (PortGroupConfig config : portGroupConfigs.values()) {
2483             portGroupProvider.createPortGroupConfig(config);
2484         }
2485     }
2486
2487     public void setFrmAware(IForwardingRulesManagerAware obj) {
2488         this.frmAware.add(obj);
2489     }
2490
2491     public void unsetFrmAware(IForwardingRulesManagerAware obj) {
2492         this.frmAware.remove(obj);
2493     }
2494
2495     void setClusterContainerService(IClusterContainerServices s) {
2496         log.debug("Cluster Service set");
2497         this.clusterContainerService = s;
2498     }
2499
2500     void unsetClusterContainerService(IClusterContainerServices s) {
2501         if (this.clusterContainerService == s) {
2502             log.debug("Cluster Service removed!");
2503             this.clusterContainerService = null;
2504         }
2505     }
2506
2507     private String getContainerName() {
2508         if (container == null) {
2509             return GlobalConstants.DEFAULT.toString();
2510         }
2511         return container.getName();
2512     }
2513
2514     /**
2515      * Function called by the dependency manager when all the required
2516      * dependencies are satisfied
2517      *
2518      */
2519     void init() {
2520         frmFileName = GlobalConstants.STARTUPHOME.toString() + "frm_staticflows_" + this.getContainerName() + ".conf";
2521         portGroupFileName = GlobalConstants.STARTUPHOME.toString() + "portgroup_" + this.getContainerName() + ".conf";
2522
2523         inContainerMode = false;
2524
2525         if (portGroupProvider != null) {
2526             portGroupProvider.registerPortGroupChange(this);
2527         }
2528
2529         cacheStartup();
2530
2531         registerWithOSGIConsole();
2532
2533         /*
2534          * If we are not the first cluster node to come up, do not initialize
2535          * the static flow entries ordinal
2536          */
2537         if (staticFlowsOrdinal.size() == 0) {
2538             staticFlowsOrdinal.put(0, Integer.valueOf(0));
2539         }
2540
2541         pendingEvents = new LinkedBlockingQueue<FRMEvent>();
2542
2543         // Initialize the event handler thread
2544         frmEventHandler = new Thread(new Runnable() {
2545             @Override
2546             public void run() {
2547                 while (!stopping) {
2548                     try {
2549                         final FRMEvent event = pendingEvents.take();
2550                         if (event == null) {
2551                             log.warn("Dequeued null event");
2552                             continue;
2553                         }
2554                         log.trace("Dequeued {} event", event.getClass().getSimpleName());
2555                         if (event instanceof NodeUpdateEvent) {
2556                             NodeUpdateEvent update = (NodeUpdateEvent) event;
2557                             Node node = update.getNode();
2558                             switch (update.getUpdateType()) {
2559                             case ADDED:
2560                                 addStaticFlowsToSwitch(node);
2561                                 break;
2562                             case REMOVED:
2563                                 cleanDatabaseForNode(node);
2564                                 updateStaticFlowConfigsOnNodeDown(node);
2565                                 break;
2566                             default:
2567                             }
2568                         } else if (event instanceof ErrorReportedEvent) {
2569                             ErrorReportedEvent errEvent = (ErrorReportedEvent) event;
2570                             processErrorEvent(errEvent);
2571                         } else if (event instanceof WorkOrderEvent) {
2572                             /*
2573                              * Take care of handling the remote Work request
2574                              */
2575                             Runnable r = new Runnable() {
2576                                 @Override
2577                                 public void run() {
2578                                     WorkOrderEvent work = (WorkOrderEvent) event;
2579                                     FlowEntryDistributionOrder fe = work.getFe();
2580                                     if (fe != null) {
2581                                         logsync.trace("Executing the workOrder {}", fe);
2582                                         Status gotStatus = null;
2583                                         FlowEntryInstall feiCurrent = fe.getEntry();
2584                                         FlowEntryInstall feiNew = workOrder.get(fe);
2585                                         switch (fe.getUpType()) {
2586                                         case ADDED:
2587                                             gotStatus = addEntriesInternal(feiCurrent, false);
2588                                             break;
2589                                         case CHANGED:
2590                                             gotStatus = modifyEntryInternal(feiCurrent, feiNew, false);
2591                                             break;
2592                                         case REMOVED:
2593                                             gotStatus = removeEntryInternal(feiCurrent, false);
2594                                             break;
2595                                         }
2596                                         // Remove the Order
2597                                         workOrder.remove(fe);
2598                                         logsync.trace(
2599                                                 "The workOrder has been executed and now the status is being returned {}", fe);
2600                                         // Place the status
2601                                         workStatus.put(fe, gotStatus);
2602                                     } else {
2603                                         log.warn("Not expected null WorkOrder", work);
2604                                     }
2605                                 }
2606                             };
2607                             if(executor != null) {
2608                                 executor.execute(r);
2609                             }
2610                         } else if (event instanceof WorkStatusCleanup) {
2611                             /*
2612                              * Take care of handling the remote Work request
2613                              */
2614                             WorkStatusCleanup work = (WorkStatusCleanup) event;
2615                             FlowEntryDistributionOrder fe = work.getFe();
2616                             if (fe != null) {
2617                                 logsync.trace("The workStatus {} is being removed", fe);
2618                                 workStatus.remove(fe);
2619                             } else {
2620                                 log.warn("Not expected null WorkStatus", work);
2621                             }
2622                         }  else if (event instanceof ContainerFlowChangeEvent) {
2623                             /*
2624                              * Whether it is an addition or removal, we have to
2625                              * recompute the merged flows entries taking into
2626                              * account all the current container flows because
2627                              * flow merging is not an injective function
2628                              */
2629                             updateFlowsContainerFlow();
2630                         } else {
2631                             log.warn("Dequeued unknown event {}", event.getClass()
2632                                     .getSimpleName());
2633                         }
2634                     } catch (InterruptedException e) {
2635                         // clear pending events
2636                         pendingEvents.clear();
2637                     }
2638                 }
2639             }
2640         }, "FRM EventHandler Collector");
2641     }
2642
2643     /**
2644      * Function called by the dependency manager when at least one dependency
2645      * become unsatisfied or when the component is shutting down because for
2646      * example bundle is being stopped.
2647      *
2648      */
2649     void destroy() {
2650         // Interrupt the thread
2651         frmEventHandler.interrupt();
2652         // Clear the pendingEvents queue
2653         pendingEvents.clear();
2654         frmAware.clear();
2655         workMonitor.clear();
2656     }
2657
2658     /**
2659      * Function called by dependency manager after "init ()" is called and after
2660      * the services provided by the class are registered in the service registry
2661      *
2662      */
2663     void start() {
2664         /*
2665          * If running in default container, need to know if controller is in
2666          * container mode
2667          */
2668         if (GlobalConstants.DEFAULT.toString().equals(this.getContainerName())) {
2669             inContainerMode = containerManager.inContainerMode();
2670         }
2671
2672         // Initialize graceful stop flag
2673         stopping = false;
2674
2675         // Allocate the executor service
2676         this.executor = Executors.newFixedThreadPool(maxPoolSize);
2677
2678         // Start event handler thread
2679         frmEventHandler.start();
2680
2681         /*
2682          * Read startup and build database if we have not already gotten the
2683          * configurations synced from another node
2684          */
2685         if (staticFlows.isEmpty()) {
2686             loadFlowConfiguration();
2687         }
2688     }
2689
2690     /**
2691      * Function called by the dependency manager before the services exported by
2692      * the component are unregistered, this will be followed by a "destroy ()"
2693      * calls
2694      */
2695     void stop() {
2696         stopping = true;
2697         uninstallAllFlowEntries(false);
2698         // Shutdown executor
2699         this.executor.shutdownNow();
2700         // Now walk all the workMonitor and wake up the one sleeping because
2701         // destruction is happening
2702         for (FlowEntryDistributionOrder fe : workMonitor.keySet()) {
2703             FlowEntryDistributionOrderFutureTask task = workMonitor.get(fe);
2704             task.cancel(true);
2705         }
2706     }
2707
2708     public void setFlowProgrammerService(IFlowProgrammerService service) {
2709         this.programmer = service;
2710     }
2711
2712     public void unsetFlowProgrammerService(IFlowProgrammerService service) {
2713         if (this.programmer == service) {
2714             this.programmer = null;
2715         }
2716     }
2717
2718     public void setSwitchManager(ISwitchManager switchManager) {
2719         this.switchManager = switchManager;
2720     }
2721
2722     public void unsetSwitchManager(ISwitchManager switchManager) {
2723         if (this.switchManager == switchManager) {
2724             this.switchManager = null;
2725         }
2726     }
2727
2728     @Override
2729     public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) {
2730         if (!container.getName().equals(containerName)) {
2731             return;
2732         }
2733     }
2734
2735     @Override
2736     public void containerFlowUpdated(String containerName, ContainerFlow previous, ContainerFlow current, UpdateType t) {
2737         if (!container.getName().equals(containerName)) {
2738             return;
2739         }
2740         log.trace("Container {}: Updating installed flows because of container flow change: {} {}",
2741                 container.getName(), t, current);
2742         ContainerFlowChangeEvent ev = new ContainerFlowChangeEvent(previous, current, t);
2743         pendingEvents.offer(ev);
2744     }
2745
2746     @Override
2747     public void nodeConnectorUpdated(String containerName, NodeConnector nc, UpdateType t) {
2748         if (!container.getName().equals(containerName)) {
2749             return;
2750         }
2751
2752         boolean updateStaticFlowCluster = false;
2753
2754         switch (t) {
2755         case REMOVED:
2756             log.trace("Port {} was removed from container: uninstalling interested flows", nc);
2757             updateStaticFlowCluster = removeFlowsOnNodeConnectorDown(nc);
2758             break;
2759         case ADDED:
2760             log.trace("Port {} was added to container: reinstall interested flows", nc);
2761             updateStaticFlowCluster = installFlowsOnNodeConnectorUp(nc);
2762
2763             break;
2764         case CHANGED:
2765             break;
2766         default:
2767         }
2768
2769         if (updateStaticFlowCluster) {
2770             refreshClusterStaticFlowsStatus(nc.getNode());
2771         }
2772     }
2773
2774     @Override
2775     public void containerModeUpdated(UpdateType update) {
2776         // Only default container instance reacts on this event
2777         if (!container.getName().equals(GlobalConstants.DEFAULT.toString())) {
2778             return;
2779         }
2780         switch (update) {
2781         case ADDED:
2782             /*
2783              * Controller is moving to container mode. We are in the default
2784              * container context, we need to remove all our non-internal flows
2785              * to prevent any container isolation breakage. We also need to
2786              * preserve our flow so that they can be re-installed if we move
2787              * back to non container mode (no containers).
2788              */
2789             this.inContainerMode = true;
2790             this.uninstallAllFlowEntries(true);
2791             break;
2792         case REMOVED:
2793             this.inContainerMode = false;
2794             this.reinstallAllFlowEntries();
2795             break;
2796         default:
2797         }
2798
2799         // Update our configuration DB
2800         updateStaticFlowConfigsOnContainerModeChange(update);
2801     }
2802
2803     protected abstract class FRMEvent {
2804
2805     }
2806
2807     private class NodeUpdateEvent extends FRMEvent {
2808         private final Node node;
2809         private final UpdateType update;
2810
2811         public NodeUpdateEvent(UpdateType update, Node node) {
2812             this.update = update;
2813             this.node = node;
2814         }
2815
2816         public UpdateType getUpdateType() {
2817             return update;
2818         }
2819
2820         public Node getNode() {
2821             return node;
2822         }
2823     }
2824
2825     private class ErrorReportedEvent extends FRMEvent {
2826         private final long rid;
2827         private final Node node;
2828         private final Object error;
2829
2830         public ErrorReportedEvent(long rid, Node node, Object error) {
2831             this.rid = rid;
2832             this.node = node;
2833             this.error = error;
2834         }
2835
2836         public long getRequestId() {
2837             return rid;
2838         }
2839
2840         public Object getError() {
2841             return error;
2842         }
2843
2844         public Node getNode() {
2845             return node;
2846         }
2847     }
2848
2849     private class WorkOrderEvent extends FRMEvent {
2850         private FlowEntryDistributionOrder fe;
2851         private FlowEntryInstall newEntry;
2852
2853         /**
2854          * @param fe
2855          * @param newEntry
2856          */
2857         WorkOrderEvent(FlowEntryDistributionOrder fe, FlowEntryInstall newEntry) {
2858             this.fe = fe;
2859             this.newEntry = newEntry;
2860         }
2861
2862         /**
2863          * @return the fe
2864          */
2865         public FlowEntryDistributionOrder getFe() {
2866             return fe;
2867         }
2868
2869         /**
2870          * @return the newEntry
2871          */
2872         public FlowEntryInstall getNewEntry() {
2873             return newEntry;
2874         }
2875     }
2876     private class ContainerFlowChangeEvent extends FRMEvent {
2877         private final ContainerFlow previous;
2878         private final ContainerFlow current;
2879         private final UpdateType type;
2880
2881         public ContainerFlowChangeEvent(ContainerFlow previous, ContainerFlow current, UpdateType type) {
2882             this.previous = previous;
2883             this.current = current;
2884             this.type = type;
2885         }
2886
2887         public ContainerFlow getPrevious() {
2888             return this.previous;
2889         }
2890
2891         public ContainerFlow getCurrent() {
2892             return this.current;
2893         }
2894
2895         public UpdateType getType() {
2896             return this.type;
2897         }
2898     }
2899
2900
2901     private class WorkStatusCleanup extends FRMEvent {
2902         private FlowEntryDistributionOrder fe;
2903
2904         /**
2905          * @param fe
2906          */
2907         WorkStatusCleanup(FlowEntryDistributionOrder fe) {
2908             this.fe = fe;
2909         }
2910
2911         /**
2912          * @return the fe
2913          */
2914         public FlowEntryDistributionOrder getFe() {
2915             return fe;
2916         }
2917     }
2918
2919     /*
2920      * OSGI COMMANDS
2921      */
2922     @Override
2923     public String getHelp() {
2924         StringBuffer help = new StringBuffer();
2925         help.append("---FRM Matrix Application---\n");
2926         help.append("\t printMatrixData        - Prints the Matrix Configs\n");
2927         help.append("\t addMatrixConfig <name> <regex>\n");
2928         help.append("\t delMatrixConfig <name>\n");
2929         help.append("\t useMatrixConfig <name>\n");
2930         return help.toString();
2931     }
2932
2933     public void _printMatrixData(CommandInterpreter ci) {
2934         ci.println("Configs : ");
2935         ci.println("---------");
2936         ci.println(portGroupConfigs);
2937
2938         ci.println("Data : ");
2939         ci.println("------");
2940         ci.println(portGroupData);
2941     }
2942
2943     public void _addMatrixConfig(CommandInterpreter ci) {
2944         String name = ci.nextArgument();
2945         String regex = ci.nextArgument();
2946         addPortGroupConfig(name, regex, false);
2947     }
2948
2949     public void _delMatrixConfig(CommandInterpreter ci) {
2950         String name = ci.nextArgument();
2951         delPortGroupConfig(name);
2952     }
2953
2954     public void _useMatrixConfig(CommandInterpreter ci) {
2955         String name = ci.nextArgument();
2956         usePortGroupConfig(name);
2957     }
2958
2959     public void _arpPunt(CommandInterpreter ci) {
2960         String switchId = ci.nextArgument();
2961         long swid = HexEncode.stringToLong(switchId);
2962         Node node = NodeCreator.createOFNode(swid);
2963         installImplicitARPReplyPunt(node);
2964     }
2965
2966     public void _frmaddflow(CommandInterpreter ci) throws UnknownHostException {
2967         Node node = null;
2968         String nodeId = ci.nextArgument();
2969         if (nodeId == null) {
2970             ci.print("Node id not specified");
2971             return;
2972         }
2973         try {
2974             node = NodeCreator.createOFNode(Long.valueOf(nodeId));
2975         } catch (NumberFormatException e) {
2976             ci.print("Node id not a number");
2977             return;
2978         }
2979         ci.println(this.programmer.addFlow(node, getSampleFlow(node)));
2980     }
2981
2982     public void _frmremoveflow(CommandInterpreter ci) throws UnknownHostException {
2983         Node node = null;
2984         String nodeId = ci.nextArgument();
2985         if (nodeId == null) {
2986             ci.print("Node id not specified");
2987             return;
2988         }
2989         try {
2990             node = NodeCreator.createOFNode(Long.valueOf(nodeId));
2991         } catch (NumberFormatException e) {
2992             ci.print("Node id not a number");
2993             return;
2994         }
2995         ci.println(this.programmer.removeFlow(node, getSampleFlow(node)));
2996     }
2997
2998     private Flow getSampleFlow(Node node) throws UnknownHostException {
2999         NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
3000         NodeConnector oport = NodeConnectorCreator.createOFNodeConnector((short) 30, node);
3001         byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
3002         byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
3003         InetAddress srcIP = InetAddress.getByName("172.28.30.50");
3004         InetAddress dstIP = InetAddress.getByName("171.71.9.52");
3005         InetAddress ipMask = InetAddress.getByName("255.255.255.0");
3006         InetAddress ipMask2 = InetAddress.getByName("255.0.0.0");
3007         short ethertype = EtherTypes.IPv4.shortValue();
3008         short vlan = (short) 27;
3009         byte vlanPr = 3;
3010         Byte tos = 4;
3011         byte proto = IPProtocols.TCP.byteValue();
3012         short src = (short) 55000;
3013         short dst = 80;
3014
3015         /*
3016          * Create a SAL Flow aFlow
3017          */
3018         Match match = new Match();
3019         match.setField(MatchType.IN_PORT, port);
3020         match.setField(MatchType.DL_SRC, srcMac);
3021         match.setField(MatchType.DL_DST, dstMac);
3022         match.setField(MatchType.DL_TYPE, ethertype);
3023         match.setField(MatchType.DL_VLAN, vlan);
3024         match.setField(MatchType.DL_VLAN_PR, vlanPr);
3025         match.setField(MatchType.NW_SRC, srcIP, ipMask);
3026         match.setField(MatchType.NW_DST, dstIP, ipMask2);
3027         match.setField(MatchType.NW_TOS, tos);
3028         match.setField(MatchType.NW_PROTO, proto);
3029         match.setField(MatchType.TP_SRC, src);
3030         match.setField(MatchType.TP_DST, dst);
3031
3032         List<Action> actions = new ArrayList<Action>();
3033         actions.add(new Output(oport));
3034         actions.add(new PopVlan());
3035         actions.add(new Flood());
3036         actions.add(new Controller());
3037         return new Flow(match, actions);
3038     }
3039
3040     @Override
3041     public Status saveConfiguration() {
3042         return saveConfig();
3043     }
3044
3045     public void _frmNodeFlows(CommandInterpreter ci) {
3046         String nodeId = ci.nextArgument();
3047         Node node = Node.fromString(nodeId);
3048         if (node == null) {
3049             ci.println("frmNodeFlows <node> [verbose]");
3050             return;
3051         }
3052         boolean verbose = false;
3053         String verboseCheck = ci.nextArgument();
3054         if (verboseCheck != null) {
3055             verbose = verboseCheck.equals("true");
3056         }
3057
3058         if (!nodeFlows.containsKey(node)) {
3059             return;
3060         }
3061         // Dump per node database
3062         for (FlowEntryInstall entry : nodeFlows.get(node)) {
3063             if (!verbose) {
3064                 ci.println(node + " " + installedSwView.get(entry).getFlowName());
3065             } else {
3066                 ci.println(node + " " + installedSwView.get(entry).toString());
3067             }
3068         }
3069     }
3070
3071     public void _frmGroupFlows(CommandInterpreter ci) {
3072         String group = ci.nextArgument();
3073         if (group == null) {
3074             ci.println("frmGroupFlows <group> [verbose]");
3075             return;
3076         }
3077         boolean verbose = false;
3078         String verboseCheck = ci.nextArgument();
3079         if (verboseCheck != null) {
3080             verbose = verboseCheck.equalsIgnoreCase("true");
3081         }
3082
3083         if (!groupFlows.containsKey(group)) {
3084             return;
3085         }
3086         // Dump per node database
3087         ci.println("Group " + group + ":\n");
3088         for (FlowEntryInstall flowEntry : groupFlows.get(group)) {
3089             if (!verbose) {
3090                 ci.println(flowEntry.getNode() + " " + flowEntry.getFlowName());
3091             } else {
3092                 ci.println(flowEntry.getNode() + " " + flowEntry.toString());
3093             }
3094         }
3095     }
3096
3097     public void _frmProcessErrorEvent(CommandInterpreter ci) throws UnknownHostException {
3098         Node node = null;
3099         long reqId = 0L;
3100         String nodeId = ci.nextArgument();
3101         if (nodeId == null) {
3102             ci.print("Node id not specified");
3103             return;
3104         }
3105         String requestId = ci.nextArgument();
3106         if (requestId == null) {
3107             ci.print("Request id not specified");
3108             return;
3109         }
3110         try {
3111             node = NodeCreator.createOFNode(Long.valueOf(nodeId));
3112         } catch (NumberFormatException e) {
3113             ci.print("Node id not a number");
3114             return;
3115         }
3116         try {
3117             reqId = Long.parseLong(requestId);
3118         } catch (NumberFormatException e) {
3119             ci.print("Request id not a number");
3120             return;
3121         }
3122         // null for error object is good enough for now
3123         ErrorReportedEvent event = new ErrorReportedEvent(reqId, node, null);
3124         this.processErrorEvent(event);
3125     }
3126
3127     @Override
3128     public void flowRemoved(Node node, Flow flow) {
3129         log.trace("Received flow removed notification on {} for {}", node, flow);
3130
3131         // For flow entry identification, only node, match and priority matter
3132         FlowEntryInstall test = new FlowEntryInstall(new FlowEntry("", "", flow, node), null);
3133         FlowEntryInstall installedEntry = this.installedSwView.get(test);
3134         if (installedEntry == null) {
3135             log.trace("Entry is not known to us");
3136             return;
3137         }
3138
3139         // Update Static flow status
3140         Integer key = 0;
3141         FlowConfig target = null;
3142         for (Map.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
3143             FlowConfig conf = entry.getValue();
3144             if (conf.isByNameAndNodeIdEqual(installedEntry.getFlowName(), node)) {
3145                 key = entry.getKey();
3146                 target = conf;
3147                 break;
3148             }
3149         }
3150         if (target != null) {
3151             // Update Configuration database
3152             target.toggleInstallation();
3153             target.setStatus(StatusCode.SUCCESS.toString());
3154             staticFlows.put(key, target);
3155         }
3156
3157         // Update software views
3158         this.updateLocalDatabase(installedEntry, false);
3159     }
3160
3161     @Override
3162     public void flowErrorReported(Node node, long rid, Object err) {
3163         log.trace("Got error {} for message rid {} from node {}", new Object[] { err, rid, node });
3164         pendingEvents.offer(new ErrorReportedEvent(rid, node, err));
3165     }
3166
3167     private void processErrorEvent(ErrorReportedEvent event) {
3168         Node node = event.getNode();
3169         long rid = event.getRequestId();
3170         Object error = event.getError();
3171         String errorString = (error == null) ? "Not provided" : error.toString();
3172         /*
3173          * If this was for a flow install, remove the corresponding entry from
3174          * the software view. If it was a Looking for the rid going through the
3175          * software database. TODO: A more efficient rid <-> FlowEntryInstall
3176          * mapping will have to be added in future
3177          */
3178         FlowEntryInstall target = null;
3179         List<FlowEntryInstall> flowEntryInstallList = nodeFlows.get(node);
3180         // flowEntryInstallList could be null.
3181         // so check for it.
3182         if(flowEntryInstallList != null) {
3183             for (FlowEntryInstall index : flowEntryInstallList) {
3184                 FlowEntryInstall entry = installedSwView.get(index);
3185                 if(entry != null) {
3186                     if (entry.getRequestId() == rid) {
3187                         target = entry;
3188                         break;
3189                     }
3190                 }
3191             }
3192         }
3193         if (target != null) {
3194             // This was a flow install, update database
3195             this.updateLocalDatabase(target, false);
3196             // also update the config
3197             if(FlowConfig.STATICFLOWGROUP.equals(target.getGroupName())) {
3198                 ConcurrentMap.Entry<Integer, FlowConfig> staticFlowEntry = getStaticFlowEntry(target.getFlowName(),target.getNode());
3199                 // staticFlowEntry should never be null.
3200                 // the null check is just an extra defensive check.
3201                 if(staticFlowEntry != null) {
3202                     staticFlows.remove(staticFlowEntry.getKey());
3203                 }
3204             }
3205         }
3206
3207         // Notify listeners
3208         if (frmAware != null) {
3209             synchronized (frmAware) {
3210                 for (IForwardingRulesManagerAware frma : frmAware) {
3211                     try {
3212                         frma.requestFailed(rid, errorString);
3213                     } catch (Exception e) {
3214                         log.warn("Failed to notify {}", frma);
3215                     }
3216                 }
3217             }
3218         }
3219     }
3220
3221     @Override
3222     public Status solicitStatusResponse(Node node, boolean blocking) {
3223         Status rv = new Status(StatusCode.INTERNALERROR);
3224
3225         if (this.programmer != null) {
3226             if (blocking) {
3227                 rv = programmer.syncSendBarrierMessage(node);
3228             } else {
3229                 rv = programmer.asyncSendBarrierMessage(node);
3230             }
3231         }
3232
3233         return rv;
3234     }
3235
3236     public void unsetIConnectionManager(IConnectionManager s) {
3237         if (s == this.connectionManager) {
3238             this.connectionManager = null;
3239         }
3240     }
3241
3242     public void setIConnectionManager(IConnectionManager s) {
3243         this.connectionManager = s;
3244     }
3245
3246     public void unsetIContainerManager(IContainerManager s) {
3247         if (s == this.containerManager) {
3248             this.containerManager = null;
3249         }
3250     }
3251
3252     public void setIContainerManager(IContainerManager s) {
3253         this.containerManager = s;
3254     }
3255
3256     @Override
3257     public void entryCreated(Object key, String cacheName, boolean originLocal) {
3258         /*
3259          * Do nothing
3260          */
3261     }
3262
3263     @Override
3264     public void entryUpdated(Object key, Object new_value, String cacheName, boolean originLocal) {
3265         if (originLocal) {
3266             /*
3267              * Local updates are of no interest
3268              */
3269             return;
3270         }
3271         if (cacheName.equals(WORK_ORDER_CACHE)) {
3272             logsync.trace("Got a WorkOrderCacheUpdate for {}", key);
3273             /*
3274              * This is the case of one workOrder becoming available, so we need
3275              * to dispatch the work to the appropriate handler
3276              */
3277             FlowEntryDistributionOrder fe = (FlowEntryDistributionOrder) key;
3278             FlowEntryInstall fei = fe.getEntry();
3279             if (fei == null) {
3280                 return;
3281             }
3282             Node n = fei.getNode();
3283             if (connectionManager.getLocalityStatus(n) == ConnectionLocality.LOCAL) {
3284                 logsync.trace("workOrder for fe {} processed locally", fe);
3285                 // I'm the controller in charge for the request, queue it for
3286                 // processing
3287                 pendingEvents.offer(new WorkOrderEvent(fe, (FlowEntryInstall) new_value));
3288             }
3289         } else if (cacheName.equals(WORK_STATUS_CACHE)) {
3290             logsync.trace("Got a WorkStatusCacheUpdate for {}", key);
3291             /*
3292              * This is the case of one workOrder being completed and a status
3293              * returned
3294              */
3295             FlowEntryDistributionOrder fe = (FlowEntryDistributionOrder) key;
3296             /*
3297              * Check if the order was initiated by this controller in that case
3298              * we need to actually look at the status returned
3299              */
3300             if (fe.getRequestorController()
3301                     .equals(clusterContainerService.getMyAddress())) {
3302                 FlowEntryDistributionOrderFutureTask fet = workMonitor.remove(fe);
3303                 if (fet != null) {
3304                     logsync.trace("workStatus response is for us {}", fe);
3305                     // Signal we got the status
3306                     fet.gotStatus(fe, workStatus.get(fe));
3307                     pendingEvents.offer(new WorkStatusCleanup(fe));
3308                 }
3309             }
3310         }
3311     }
3312
3313     @Override
3314     public void entryDeleted(Object key, String cacheName, boolean originLocal) {
3315         /*
3316          * Do nothing
3317          */
3318     }
3319
3320     /**
3321      * {@inheritDoc}
3322      */
3323     @Override
3324     public List<FlowEntry> getFlowEntriesForNode(Node node) {
3325         List<FlowEntry> list = new ArrayList<FlowEntry>();
3326         if (node != null) {
3327             for (Map.Entry<FlowEntry, FlowEntry> entry : this.originalSwView.entrySet()) {
3328                 if (node.equals(entry.getKey().getNode())) {
3329                     list.add(entry.getKey().clone());
3330                 }
3331             }
3332         }
3333         return list;
3334     }
3335
3336     /**
3337      * {@inheritDoc}
3338      */
3339     @Override
3340     public List<FlowEntry> getInstalledFlowEntriesForNode(Node node) {
3341         List<FlowEntry> list = new ArrayList<FlowEntry>();
3342         if (node != null) {
3343             List<FlowEntryInstall> flowEntryInstallList = this.nodeFlows.get(node);
3344             if(flowEntryInstallList != null) {
3345                 for(FlowEntryInstall fi: flowEntryInstallList) {
3346                     list.add(fi.getInstall().clone());
3347                 }
3348             }
3349         }
3350         return list;
3351     }
3352 }