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