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