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