d4588d620b6a7f0900dc50df39aab3ac43efa215
[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         for (ConcurrentMap.Entry<FlowEntryInstall, FlowEntryInstall> entry : installedSwView.entrySet()) {
933             FlowEntryInstall current = entry.getValue();
934             FlowEntry reInstall = current.getOriginal();
935             // Remove the old couples. No validity checks to be run, use the
936             // internal remove
937             this.removeEntryInternal(current, false);
938
939             // Reinstall the original flow entries, via the regular path: new
940             // cFlow merge + validations
941             this.installFlowEntry(reInstall);
942         }
943     }
944
945     public void nonClusterObjectCreate() {
946         originalSwView = new ConcurrentHashMap<FlowEntry, FlowEntry>();
947         installedSwView = new ConcurrentHashMap<FlowEntryInstall, FlowEntryInstall>();
948         nodeFlows = new ConcurrentHashMap<Node, List<FlowEntryInstall>>();
949         groupFlows = new ConcurrentHashMap<String, List<FlowEntryInstall>>();
950         TSPolicies = new ConcurrentHashMap<String, Object>();
951         staticFlowsOrdinal = new ConcurrentHashMap<Integer, Integer>();
952         portGroupConfigs = new ConcurrentHashMap<String, PortGroupConfig>();
953         portGroupData = new ConcurrentHashMap<PortGroupConfig, Map<Node, PortGroup>>();
954         staticFlows = new ConcurrentHashMap<Integer, FlowConfig>();
955         flowsSaveEvent = new HashMap<Long, String>();
956         inactiveFlows = new ArrayList<FlowEntry>(1);
957     }
958
959     private void registerWithOSGIConsole() {
960         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
961         bundleContext.registerService(CommandProvider.class.getName(), this, null);
962     }
963
964     @Override
965     public void setTSPolicyData(String policyname, Object o, boolean add) {
966
967         if (add) {
968             /* Check if this policy already exists */
969             if (!(TSPolicies.containsKey(policyname))) {
970                 TSPolicies.put(policyname, o);
971             }
972         } else {
973             TSPolicies.remove(policyname);
974         }
975         if (frmAware != null) {
976             synchronized (frmAware) {
977                 for (IForwardingRulesManagerAware frma : frmAware) {
978                     try {
979                         frma.policyUpdate(policyname, add);
980                     } catch (Exception e) {
981                         log.warn("Exception on callback", e);
982                     }
983                 }
984             }
985         }
986     }
987
988     @Override
989     public Map<String, Object> getTSPolicyData() {
990         return TSPolicies;
991     }
992
993     @Override
994     public Object getTSPolicyData(String policyName) {
995         if (TSPolicies.containsKey(policyName)) {
996             return TSPolicies.get(policyName);
997         } else {
998             return null;
999         }
1000     }
1001
1002     @Override
1003     public List<FlowEntry> getFlowEntriesForGroup(String policyName) {
1004         List<FlowEntry> list = new ArrayList<FlowEntry>();
1005         if (policyName != null && !policyName.trim().isEmpty()) {
1006             for (Map.Entry<FlowEntry, FlowEntry> entry : this.originalSwView.entrySet()) {
1007                 if (policyName.equals(entry.getKey().getGroupName())) {
1008                     list.add(entry.getKey().clone());
1009                 }
1010             }
1011         }
1012         return list;
1013     }
1014
1015     @Override
1016     public void addOutputPort(Node node, String flowName, List<NodeConnector> portList) {
1017
1018         for (FlowEntryInstall flow : this.nodeFlows.get(node)) {
1019             if (flow.getFlowName().equals(flowName)) {
1020                 FlowEntry currentFlowEntry = flow.getOriginal();
1021                 FlowEntry newFlowEntry = currentFlowEntry.clone();
1022                 for (NodeConnector dstPort : portList) {
1023                     newFlowEntry.getFlow().addAction(new Output(dstPort));
1024                 }
1025                 Status error = modifyEntry(currentFlowEntry, newFlowEntry, false);
1026                 if (error.isSuccess()) {
1027                     log.info("Ports {} added to FlowEntry {}", portList, flowName);
1028                 } else {
1029                     log.warn("Failed to add ports {} to Flow entry {}. The failure is: {}", portList,
1030                             currentFlowEntry.toString(), error.getDescription());
1031                 }
1032                 return;
1033             }
1034         }
1035         log.warn("Failed to add ports to Flow {} on Node {}: Entry Not Found", flowName, node);
1036     }
1037
1038     @Override
1039     public void removeOutputPort(Node node, String flowName, List<NodeConnector> portList) {
1040         for (FlowEntryInstall index : this.nodeFlows.get(node)) {
1041             FlowEntryInstall flow = this.installedSwView.get(index);
1042             if (flow.getFlowName().equals(flowName)) {
1043                 FlowEntry currentFlowEntry = flow.getOriginal();
1044                 FlowEntry newFlowEntry = currentFlowEntry.clone();
1045                 for (NodeConnector dstPort : portList) {
1046                     Action action = new Output(dstPort);
1047                     newFlowEntry.getFlow().removeAction(action);
1048                 }
1049                 Status status = modifyEntry(currentFlowEntry, newFlowEntry, false);
1050                 if (status.isSuccess()) {
1051                     log.info("Ports {} removed from FlowEntry {}", portList, flowName);
1052                 } else {
1053                     log.warn("Failed to remove ports {} from Flow entry {}. The failure is: {}", portList,
1054                             currentFlowEntry.toString(), status.getDescription());
1055                 }
1056                 return;
1057             }
1058         }
1059         log.warn("Failed to remove ports from Flow {} on Node {}: Entry Not Found", flowName, node);
1060     }
1061
1062     /*
1063      * This function assumes the target flow has only one output port
1064      */
1065     @Override
1066     public void replaceOutputPort(Node node, String flowName, NodeConnector outPort) {
1067         FlowEntry currentFlowEntry = null;
1068         FlowEntry newFlowEntry = null;
1069
1070         // Find the flow
1071         for (FlowEntryInstall index : this.nodeFlows.get(node)) {
1072             FlowEntryInstall flow = this.installedSwView.get(index);
1073             if (flow.getFlowName().equals(flowName)) {
1074                 currentFlowEntry = flow.getOriginal();
1075                 break;
1076             }
1077         }
1078         if (currentFlowEntry == null) {
1079             log.warn("Failed to replace output port for flow {} on node {}: Entry Not Found", flowName, node);
1080             return;
1081         }
1082
1083         // Create a flow copy with the new output port
1084         newFlowEntry = currentFlowEntry.clone();
1085         Action target = null;
1086         for (Action action : newFlowEntry.getFlow().getActions()) {
1087             if (action.getType() == ActionType.OUTPUT) {
1088                 target = action;
1089                 break;
1090             }
1091         }
1092         newFlowEntry.getFlow().removeAction(target);
1093         newFlowEntry.getFlow().addAction(new Output(outPort));
1094
1095         // Modify on network node
1096         Status status = modifyEntry(currentFlowEntry, newFlowEntry, false);
1097
1098         if (status.isSuccess()) {
1099             log.info("Output port replaced with {} for flow {} on node {}", outPort, flowName, node);
1100         } else {
1101             log.warn("Failed to replace output port for flow {} on node {}. The failure is: {}", flowName, node,
1102                     status.getDescription());
1103         }
1104         return;
1105     }
1106
1107     @Override
1108     public NodeConnector getOutputPort(Node node, String flowName) {
1109         for (FlowEntryInstall index : this.nodeFlows.get(node)) {
1110             FlowEntryInstall flow = this.installedSwView.get(index);
1111             if (flow.getFlowName().equals(flowName)) {
1112                 for (Action action : flow.getOriginal().getFlow().getActions()) {
1113                     if (action.getType() == ActionType.OUTPUT) {
1114                         return ((Output) action).getPort();
1115                     }
1116                 }
1117             }
1118         }
1119         return null;
1120     }
1121
1122     private void cacheStartup() {
1123         allocateCaches();
1124         retrieveCaches();
1125     }
1126
1127     @SuppressWarnings("deprecation")
1128     private void allocateCaches() {
1129         if (this.clusterContainerService == null) {
1130             log.warn("Un-initialized clusterContainerService, can't create cache");
1131             return;
1132         }
1133
1134         log.debug("FRM allocateCaches for Container {}", container);
1135
1136         try {
1137             clusterContainerService.createCache("frm.originalSwView",
1138                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1139
1140             clusterContainerService.createCache("frm.installedSwView",
1141                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1142
1143             clusterContainerService.createCache("frm.nodeFlows",
1144                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1145
1146             clusterContainerService.createCache("frm.groupFlows",
1147                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1148
1149             clusterContainerService.createCache("frm.staticFlows",
1150                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1151
1152             clusterContainerService.createCache("frm.flowsSaveEvent",
1153                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1154
1155             clusterContainerService.createCache("frm.staticFlowsOrdinal",
1156                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1157
1158             clusterContainerService.createCache("frm.portGroupConfigs",
1159                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1160
1161             clusterContainerService.createCache("frm.portGroupData",
1162                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1163
1164             clusterContainerService.createCache("frm.TSPolicies",
1165                     EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
1166
1167         } catch (CacheConfigException cce) {
1168             log.error("FRM CacheConfigException");
1169         } catch (CacheExistException cce) {
1170             log.error("FRM CacheExistException");
1171         }
1172     }
1173
1174     @SuppressWarnings({ "unchecked", "deprecation" })
1175     private void retrieveCaches() {
1176         ConcurrentMap<?, ?> map;
1177
1178         if (this.clusterContainerService == null) {
1179             log.warn("un-initialized clusterContainerService, can't retrieve cache");
1180             return;
1181         }
1182
1183         log.debug("FRM retrieveCaches for Container {}", container);
1184
1185         map = clusterContainerService.getCache("frm.originalSwView");
1186         if (map != null) {
1187             originalSwView = (ConcurrentMap<FlowEntry, FlowEntry>) map;
1188         } else {
1189             log.error("FRM Cache frm.originalSwView allocation failed for Container {}", container.getName());
1190         }
1191
1192         map = clusterContainerService.getCache("frm.installedSwView");
1193         if (map != null) {
1194             installedSwView = (ConcurrentMap<FlowEntryInstall, FlowEntryInstall>) map;
1195         } else {
1196             log.error("FRM Cache frm.installedSwView allocation failed for Container {}", container.getName());
1197         }
1198
1199         map = clusterContainerService.getCache("frm.nodeFlows");
1200         if (map != null) {
1201             nodeFlows = (ConcurrentMap<Node, List<FlowEntryInstall>>) map;
1202         } else {
1203             log.error("FRM Cache frm.nodeFlows allocation failed for Container {}", container.getName());
1204         }
1205
1206         map = clusterContainerService.getCache("frm.groupFlows");
1207         if (map != null) {
1208             groupFlows = (ConcurrentMap<String, List<FlowEntryInstall>>) map;
1209         } else {
1210             log.error("FRM Cache frm.groupFlows allocation failed for Container {}", container.getName());
1211         }
1212
1213         map = clusterContainerService.getCache("frm.staticFlows");
1214         if (map != null) {
1215             staticFlows = (ConcurrentMap<Integer, FlowConfig>) map;
1216         } else {
1217             log.error("FRM Cache frm.staticFlows allocation failed for Container {}", container.getName());
1218         }
1219
1220         map = clusterContainerService.getCache("frm.flowsSaveEvent");
1221         if (map != null) {
1222             flowsSaveEvent = (ConcurrentMap<Long, String>) map;
1223         } else {
1224             log.error("FRM Cache frm.flowsSaveEvent allocation failed for Container {}", container.getName());
1225         }
1226
1227         map = clusterContainerService.getCache("frm.staticFlowsOrdinal");
1228         if (map != null) {
1229             staticFlowsOrdinal = (ConcurrentMap<Integer, Integer>) map;
1230         } else {
1231             log.error("FRM Cache frm.staticFlowsOrdinal allocation failed for Container {}", container.getName());
1232         }
1233
1234         map = clusterContainerService.getCache("frm.portGroupConfigs");
1235         if (map != null) {
1236             portGroupConfigs = (ConcurrentMap<String, PortGroupConfig>) map;
1237         } else {
1238             log.error("FRM Cache frm.portGroupConfigs allocation failed for Container {}", container.getName());
1239         }
1240
1241         map = clusterContainerService.getCache("frm.portGroupData");
1242         if (map != null) {
1243             portGroupData = (ConcurrentMap<PortGroupConfig, Map<Node, PortGroup>>) map;
1244         } else {
1245             log.error("FRM Cache frm.portGroupData allocation failed for Container {}", container.getName());
1246         }
1247
1248         map = clusterContainerService.getCache("frm.TSPolicies");
1249         if (map != null) {
1250             TSPolicies = (ConcurrentMap<String, Object>) map;
1251         } else {
1252             log.error("FRM Cache frm.TSPolicies allocation failed for Container {}", container.getName());
1253         }
1254
1255     }
1256
1257     private boolean flowConfigExists(FlowConfig config) {
1258         // Flow name has to be unique on per node id basis
1259         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1260             if (entry.getValue().isByNameAndNodeIdEqual(config)) {
1261                 return true;
1262             }
1263         }
1264         return false;
1265     }
1266
1267     @Override
1268     public Status addStaticFlow(FlowConfig config, boolean restore) {
1269         boolean multipleFlowPush = false;
1270         String error;
1271         Status status;
1272         config.setStatus(SUCCESS);
1273
1274         // Skip validation check if we are trying to restore a saved config
1275         if (!restore && !(status = config.validate(container)).isSuccess()) {
1276             log.warn("Invalid Configuration for flow {}. The failure is {}", config, status.getDescription());
1277             error = "Invalid Configuration (" + status.getDescription() + ")";
1278             config.setStatus(error);
1279             return new Status(StatusCode.BADREQUEST, error);
1280         }
1281
1282         // Presence check
1283         if (flowConfigExists(config)) {
1284             error = "Entry with this name on specified switch already exists";
1285             log.warn("Entry with this name on specified switch already exists: {}", config);
1286             config.setStatus(error);
1287             return new Status(StatusCode.CONFLICT, error);
1288         }
1289
1290         if ((config.getIngressPort() == null) && config.getPortGroup() != null) {
1291             for (String portGroupName : portGroupConfigs.keySet()) {
1292                 if (portGroupName.equalsIgnoreCase(config.getPortGroup())) {
1293                     multipleFlowPush = true;
1294                     break;
1295                 }
1296             }
1297             if (!multipleFlowPush) {
1298                 log.warn("Invalid Configuration(Invalid PortGroup Name) for flow {}", config);
1299                 error = "Invalid Configuration (Invalid PortGroup Name)";
1300                 config.setStatus(error);
1301                 return new Status(StatusCode.BADREQUEST, error);
1302             }
1303         }
1304
1305         /*
1306          * If requested program the entry in hardware first before updating the
1307          * StaticFlow DB
1308          */
1309         if (!multipleFlowPush) {
1310             // Program hw
1311             if (config.installInHw()) {
1312                 FlowEntry entry = config.getFlowEntry();
1313                 status = this.installFlowEntry(entry);
1314                 if (!status.isSuccess()) {
1315                     config.setStatus(status.getDescription());
1316                     if (!restore) {
1317                         return status;
1318                     }
1319                 }
1320             }
1321         }
1322
1323         /*
1324          * When the control reaches this point, either of the following
1325          * conditions is true 1. This is a single entry configuration (non
1326          * PortGroup) and the hardware installation is successful 2. This is a
1327          * multiple entry configuration (PortGroup) and hardware installation is
1328          * NOT done directly on this event. 3. The User prefers to retain the
1329          * configuration in Controller and skip hardware installation.
1330          *
1331          * Hence it is safe to update the StaticFlow DB at this point.
1332          *
1333          * Note : For the case of PortGrouping, it is essential to have this DB
1334          * populated before the PortGroupListeners can query for the DB
1335          * triggered using portGroupChanged event...
1336          */
1337         Integer ordinal = staticFlowsOrdinal.get(0);
1338         staticFlowsOrdinal.put(0, ++ordinal);
1339         staticFlows.put(ordinal, config);
1340
1341         if (multipleFlowPush) {
1342             PortGroupConfig pgconfig = portGroupConfigs.get(config.getPortGroup());
1343             Map<Node, PortGroup> existingData = portGroupData.get(pgconfig);
1344             if (existingData != null) {
1345                 portGroupChanged(pgconfig, existingData, true);
1346             }
1347         }
1348         return new Status(StatusCode.SUCCESS);
1349     }
1350
1351     private void addStaticFlowsToSwitch(Node node) {
1352         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1353             FlowConfig config = entry.getValue();
1354             if (config.isPortGroupEnabled()) {
1355                 continue;
1356             }
1357             if (config.getNode().equals(node)) {
1358                 if (config.installInHw() && !config.getStatus().equals(SUCCESS)) {
1359                     Status status = this.installFlowEntryAsync(config.getFlowEntry());
1360                     config.setStatus(status.getDescription());
1361                 }
1362             }
1363         }
1364         // Update cluster cache
1365         refreshClusterStaticFlowsStatus(node);
1366     }
1367
1368     private void updateStaticFlowConfigsOnNodeDown(Node node) {
1369         log.trace("Updating Static Flow configs on node down: {}", node);
1370
1371         List<Integer> toRemove = new ArrayList<Integer>();
1372         for (Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1373
1374             FlowConfig config = entry.getValue();
1375
1376             if (config.isPortGroupEnabled()) {
1377                 continue;
1378             }
1379
1380             if (config.installInHw() && config.getNode().equals(node)) {
1381                 if (config.isInternalFlow()) {
1382                     // Take note of this controller generated static flow
1383                     toRemove.add(entry.getKey());
1384                 } else {
1385                     config.setStatus(NODEDOWN);
1386                 }
1387             }
1388         }
1389         // Remove controller generated static flows for this node
1390         for (Integer index : toRemove) {
1391             staticFlows.remove(index);
1392         }
1393         // Update cluster cache
1394         refreshClusterStaticFlowsStatus(node);
1395
1396     }
1397
1398     private void updateStaticFlowConfigsOnContainerModeChange(UpdateType update) {
1399         log.trace("Updating Static Flow configs on container mode change: {}", update);
1400
1401         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1402             FlowConfig config = entry.getValue();
1403             if (config.isPortGroupEnabled()) {
1404                 continue;
1405             }
1406             if (config.installInHw() && !config.isInternalFlow()) {
1407                 switch (update) {
1408                 case ADDED:
1409                     config.setStatus("Removed from node because in container mode");
1410                     break;
1411                 case REMOVED:
1412                     config.setStatus(SUCCESS);
1413                     break;
1414                 default:
1415                 }
1416             }
1417         }
1418         // Update cluster cache
1419         refreshClusterStaticFlowsStatus(null);
1420     }
1421
1422     @Override
1423     public Status removeStaticFlow(FlowConfig config) {
1424         /*
1425          * No config.isInternal() check as NB does not take this path and GUI
1426          * cannot issue a delete on an internal generated flow. We need this
1427          * path to be accessible when switch mode is changed from proactive to
1428          * reactive, so that we can remove the internal generated LLDP and ARP
1429          * punt flows
1430          */
1431
1432         // Look for the target configuration entry
1433         Integer key = 0;
1434         FlowConfig target = null;
1435         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1436             if (entry.getValue().isByNameAndNodeIdEqual(config)) {
1437                 key = entry.getKey();
1438                 target = entry.getValue();
1439                 break;
1440             }
1441         }
1442         if (target == null) {
1443             return new Status(StatusCode.NOTFOUND, "Entry Not Present");
1444         }
1445
1446         // Program the network node
1447         Status status = this.removeEntry(config.getFlowEntry(), false);
1448
1449         // Update configuration database if programming was successful
1450         if (status.isSuccess()) {
1451             staticFlows.remove(key);
1452         }
1453
1454         return status;
1455     }
1456
1457     @Override
1458     public Status removeStaticFlow(String name, Node node) {
1459         // Look for the target configuration entry
1460         Integer key = 0;
1461         FlowConfig target = null;
1462         for (ConcurrentMap.Entry<Integer, FlowConfig> mapEntry : staticFlows.entrySet()) {
1463             if (mapEntry.getValue().isByNameAndNodeIdEqual(name, node)) {
1464                 key = mapEntry.getKey();
1465                 target = mapEntry.getValue();
1466                 break;
1467             }
1468         }
1469         if (target == null) {
1470             return new Status(StatusCode.NOTFOUND, "Entry Not Present");
1471         }
1472
1473         // Validity check for api3 entry point
1474         if (target.isInternalFlow()) {
1475             String msg = "Invalid operation: Controller generated flow cannot be deleted";
1476             String logMsg = msg + ": {}";
1477             log.warn(logMsg, name);
1478             return new Status(StatusCode.NOTACCEPTABLE, msg);
1479         }
1480
1481         if (target.isPortGroupEnabled()) {
1482             String msg = "Invalid operation: Port Group flows cannot be deleted through this API";
1483             String logMsg = msg + ": {}";
1484             log.warn(logMsg, name);
1485             return new Status(StatusCode.NOTACCEPTABLE, msg);
1486         }
1487
1488         // Program the network node
1489         Status status = this.removeEntry(target.getFlowEntry(), false);
1490
1491         // Update configuration database if programming was successful
1492         if (status.isSuccess()) {
1493             staticFlows.remove(key);
1494         }
1495
1496         return status;
1497     }
1498
1499     @Override
1500     public Status modifyStaticFlow(FlowConfig newFlowConfig) {
1501         // Validity check for api3 entry point
1502         if (newFlowConfig.isInternalFlow()) {
1503             String msg = "Invalid operation: Controller generated flow cannot be modified";
1504             String logMsg = msg + ": {}";
1505             log.warn(logMsg, newFlowConfig);
1506             return new Status(StatusCode.NOTACCEPTABLE, msg);
1507         }
1508
1509         // Validity Check
1510         Status status = newFlowConfig.validate(container);
1511         if (!status.isSuccess()) {
1512             String msg = "Invalid Configuration (" + status.getDescription() + ")";
1513             newFlowConfig.setStatus(msg);
1514             log.warn("Invalid Configuration for flow {}. The failure is {}", newFlowConfig, status.getDescription());
1515             return new Status(StatusCode.BADREQUEST, msg);
1516         }
1517
1518         FlowConfig oldFlowConfig = null;
1519         Integer index = null;
1520         for (ConcurrentMap.Entry<Integer, FlowConfig> mapEntry : staticFlows.entrySet()) {
1521             FlowConfig entry = mapEntry.getValue();
1522             if (entry.isByNameAndNodeIdEqual(newFlowConfig.getName(), newFlowConfig.getNode())) {
1523                 oldFlowConfig = entry;
1524                 index = mapEntry.getKey();
1525                 break;
1526             }
1527         }
1528
1529         if (oldFlowConfig == null) {
1530             String msg = "Attempt to modify a non existing static flow";
1531             String logMsg = msg + ": {}";
1532             log.warn(logMsg, newFlowConfig);
1533             return new Status(StatusCode.NOTFOUND, msg);
1534         }
1535
1536         // Do not attempt to reinstall the flow, warn user
1537         if (newFlowConfig.equals(oldFlowConfig)) {
1538             String msg = "No modification detected";
1539             log.info("Static flow modification skipped. New flow and old flow are the same: {}", newFlowConfig);
1540             return new Status(StatusCode.SUCCESS, msg);
1541         }
1542
1543         // If flow is installed, program the network node
1544         status = new Status(StatusCode.SUCCESS, "Saved in config");
1545         if (oldFlowConfig.installInHw()) {
1546             status = this.modifyEntry(oldFlowConfig.getFlowEntry(), newFlowConfig.getFlowEntry(), false);
1547         }
1548
1549         // Update configuration database if programming was successful
1550         if (status.isSuccess()) {
1551             newFlowConfig.setStatus(status.getDescription());
1552             staticFlows.put(index, newFlowConfig);
1553         }
1554
1555         return status;
1556     }
1557
1558     @Override
1559     public Status toggleStaticFlowStatus(String name, Node node) {
1560         return toggleStaticFlowStatus(getStaticFlow(name, node));
1561     }
1562
1563     @Override
1564     public Status toggleStaticFlowStatus(FlowConfig config) {
1565         if (config == null) {
1566             String msg = "Invalid request: null flow config";
1567             log.warn(msg);
1568             return new Status(StatusCode.BADREQUEST, msg);
1569         }
1570         // Validity check for api3 entry point
1571         if (config.isInternalFlow()) {
1572             String msg = "Invalid operation: Controller generated flow cannot be modified";
1573             String logMsg = msg + ": {}";
1574             log.warn(logMsg, config);
1575             return new Status(StatusCode.NOTACCEPTABLE, msg);
1576         }
1577
1578         // Find the config entry
1579         Integer key = 0;
1580         FlowConfig target = null;
1581         for (Map.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1582             FlowConfig conf = entry.getValue();
1583             if (conf.isByNameAndNodeIdEqual(config)) {
1584                 key = entry.getKey();
1585                 target = conf;
1586                 break;
1587             }
1588         }
1589         if (target != null) {
1590             // Program the network node
1591             Status status;
1592             if (target.installInHw()) {
1593                 status = this.removeEntry(target.getFlowEntry(), false);
1594             } else {
1595                 status = this.addEntry(target.getFlowEntry(), false);
1596             }
1597             // Update Configuration database
1598             target.setStatus(SUCCESS);
1599             target.toggleInstallation();
1600             staticFlows.put(key, target);
1601             return status;
1602         }
1603
1604         return new Status(StatusCode.NOTFOUND, "Unable to locate the entry. Failed to toggle status");
1605     }
1606
1607     /**
1608      * Reinsert all static flows entries in the cache to force cache updates in
1609      * the cluster. This is useful when only some parameters were changed in the
1610      * entries, like the status.
1611      *
1612      * @param node
1613      *            The node for which the static flow configurations have to be
1614      *            refreshed. If null, all nodes static flows will be refreshed.
1615      */
1616     private void refreshClusterStaticFlowsStatus(Node node) {
1617         // Refresh cluster cache
1618         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1619             if (node == null || entry.getValue().getNode().equals(node)) {
1620                 staticFlows.put(entry.getKey(), entry.getValue());
1621             }
1622         }
1623     }
1624
1625     /**
1626      * Uninstall all the non-internal Flow Entries present in the software view.
1627      * A copy of each entry is stored in the inactive list so that it can be
1628      * re-applied when needed. This function is called on the global instance of
1629      * FRM only, when the first container is created
1630      */
1631     private void uninstallAllFlowEntries() {
1632         log.info("Uninstalling all non-internal flows");
1633
1634         // Store entries / create target list
1635         for (ConcurrentMap.Entry<FlowEntryInstall, FlowEntryInstall> mapEntry : installedSwView.entrySet()) {
1636             FlowEntryInstall flowEntries = mapEntry.getValue();
1637             // Skip internal generated static flows
1638             if (!flowEntries.isInternal()) {
1639                 inactiveFlows.add(flowEntries.getOriginal());
1640             }
1641         }
1642
1643         // Now remove the entries
1644         for (FlowEntry flowEntry : inactiveFlows) {
1645             Status status = this.removeEntry(flowEntry, false);
1646             if (!status.isSuccess()) {
1647                 log.warn("Failed to remove entry: {}. The failure is: {}", flowEntry, status.getDescription());
1648             }
1649         }
1650     }
1651
1652     /**
1653      * Re-install all the Flow Entries present in the inactive list The inactive
1654      * list will be empty at the end of this call This function is called on the
1655      * default container instance of FRM only when the last container is deleted
1656      */
1657     private void reinstallAllFlowEntries() {
1658         log.info("Reinstalling all inactive flows");
1659
1660         for (FlowEntry flowEntry : this.inactiveFlows) {
1661             this.addEntry(flowEntry, false);
1662         }
1663
1664         // Empty inactive list in any case
1665         inactiveFlows.clear();
1666     }
1667
1668     @Override
1669     public List<FlowConfig> getStaticFlows() {
1670         return getStaticFlowsOrderedList(staticFlows, staticFlowsOrdinal.get(0).intValue());
1671     }
1672
1673     // TODO: need to come out with a better algorithm for maintaining the order
1674     // of the configuration entries
1675     // with actual one, index associated to deleted entries cannot be reused and
1676     // map grows...
1677     private List<FlowConfig> getStaticFlowsOrderedList(ConcurrentMap<Integer, FlowConfig> flowMap, int maxKey) {
1678         List<FlowConfig> orderedList = new ArrayList<FlowConfig>();
1679         for (int i = 0; i <= maxKey; i++) {
1680             FlowConfig entry = flowMap.get(i);
1681             if (entry != null) {
1682                 orderedList.add(entry);
1683             }
1684         }
1685         return orderedList;
1686     }
1687
1688     @Override
1689     public FlowConfig getStaticFlow(String name, Node node) {
1690         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1691             if (entry.getValue().isByNameAndNodeIdEqual(name, node)) {
1692                 return entry.getValue();
1693             }
1694         }
1695         return null;
1696     }
1697
1698     @Override
1699     public List<FlowConfig> getStaticFlows(Node node) {
1700         List<FlowConfig> list = new ArrayList<FlowConfig>();
1701         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1702             if (entry.getValue().onNode(node)) {
1703                 list.add(entry.getValue());
1704             }
1705         }
1706         return list;
1707     }
1708
1709     @Override
1710     public List<String> getStaticFlowNamesForNode(Node node) {
1711         List<String> list = new ArrayList<String>();
1712         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1713             if (entry.getValue().onNode(node)) {
1714                 list.add(entry.getValue().getName());
1715             }
1716         }
1717         return list;
1718     }
1719
1720     @Override
1721     public List<Node> getListNodeWithConfiguredFlows() {
1722         Set<Node> set = new HashSet<Node>();
1723         for (ConcurrentMap.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
1724             set.add(entry.getValue().getNode());
1725         }
1726         return new ArrayList<Node>(set);
1727     }
1728
1729     @SuppressWarnings("unchecked")
1730     private void loadFlowConfiguration() {
1731         ObjectReader objReader = new ObjectReader();
1732         ConcurrentMap<Integer, FlowConfig> confList = (ConcurrentMap<Integer, FlowConfig>) objReader.read(this,
1733                 frmFileName);
1734
1735         ConcurrentMap<String, PortGroupConfig> pgConfig = (ConcurrentMap<String, PortGroupConfig>) objReader.read(this,
1736                 portGroupFileName);
1737
1738         if (pgConfig != null) {
1739             for (ConcurrentMap.Entry<String, PortGroupConfig> entry : pgConfig.entrySet()) {
1740                 addPortGroupConfig(entry.getKey(), entry.getValue().getMatchString(), true);
1741             }
1742         }
1743
1744         if (confList == null) {
1745             return;
1746         }
1747
1748         int maxKey = 0;
1749         for (Integer key : confList.keySet()) {
1750             if (key.intValue() > maxKey) {
1751                 maxKey = key.intValue();
1752             }
1753         }
1754
1755         for (FlowConfig conf : getStaticFlowsOrderedList(confList, maxKey)) {
1756             addStaticFlow(conf, true);
1757         }
1758     }
1759
1760     @Override
1761     public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
1762         return ois.readObject();
1763     }
1764
1765     @Override
1766     public Status saveConfig() {
1767         // Publish the save config event to the cluster nodes
1768         flowsSaveEvent.put(new Date().getTime(), SAVE);
1769         return saveConfigInternal();
1770     }
1771
1772     private Status saveConfigInternal() {
1773         ObjectWriter objWriter = new ObjectWriter();
1774         ConcurrentMap<Integer, FlowConfig> nonDynamicFlows = new ConcurrentHashMap<Integer, FlowConfig>();
1775         for (Integer ordinal : staticFlows.keySet()) {
1776             FlowConfig config = staticFlows.get(ordinal);
1777             // Do not save dynamic and controller generated static flows
1778             if (config.isDynamic() || config.isInternalFlow()) {
1779                 continue;
1780             }
1781             nonDynamicFlows.put(ordinal, config);
1782         }
1783         objWriter.write(nonDynamicFlows, frmFileName);
1784         objWriter.write(new ConcurrentHashMap<String, PortGroupConfig>(portGroupConfigs), portGroupFileName);
1785         return new Status(StatusCode.SUCCESS, null);
1786     }
1787
1788     @Override
1789     public void entryCreated(Long key, String cacheName, boolean local) {
1790     }
1791
1792     @Override
1793     public void entryUpdated(Long key, String new_value, String cacheName, boolean originLocal) {
1794         saveConfigInternal();
1795     }
1796
1797     @Override
1798     public void entryDeleted(Long key, String cacheName, boolean originLocal) {
1799     }
1800
1801     @Override
1802     public void subnetNotify(Subnet sub, boolean add) {
1803     }
1804
1805     private void installImplicitARPReplyPunt(Node node) {
1806
1807         if (node == null) {
1808             return;
1809         }
1810
1811         List<String> puntAction = new ArrayList<String>();
1812         puntAction.add(ActionType.CONTROLLER.toString());
1813
1814         FlowConfig allowARP = new FlowConfig();
1815         allowARP.setInstallInHw(true);
1816         allowARP.setName("**Punt ARP Reply");
1817         allowARP.setPriority("500");
1818         allowARP.setNode(node);
1819         allowARP.setEtherType("0x" + Integer.toHexString(EtherTypes.ARP.intValue()).toUpperCase());
1820         allowARP.setDstMac(HexEncode.bytesToHexString(switchManager.getControllerMAC()));
1821         allowARP.setActions(puntAction);
1822         addStaticFlow(allowARP, false);
1823     }
1824
1825     @Override
1826     public void modeChangeNotify(Node node, boolean proactive) {
1827         List<FlowConfig> defaultConfigs = new ArrayList<FlowConfig>();
1828
1829         List<String> puntAction = new ArrayList<String>();
1830         puntAction.add(ActionType.CONTROLLER.toString());
1831
1832         FlowConfig allowARP = new FlowConfig();
1833         allowARP.setInstallInHw(true);
1834         allowARP.setName("**Punt ARP");
1835         allowARP.setPriority("1");
1836         allowARP.setNode(node);
1837         allowARP.setEtherType("0x" + Integer.toHexString(EtherTypes.ARP.intValue()).toUpperCase());
1838         allowARP.setActions(puntAction);
1839         defaultConfigs.add(allowARP);
1840
1841         FlowConfig allowLLDP = new FlowConfig();
1842         allowLLDP.setInstallInHw(true);
1843         allowLLDP.setName("**Punt LLDP");
1844         allowLLDP.setPriority("1");
1845         allowLLDP.setNode(node);
1846         allowLLDP.setEtherType("0x" + Integer.toHexString(EtherTypes.LLDP.intValue()).toUpperCase());
1847         allowLLDP.setActions(puntAction);
1848         defaultConfigs.add(allowLLDP);
1849
1850         List<String> dropAction = new ArrayList<String>();
1851         dropAction.add(ActionType.DROP.toString());
1852
1853         FlowConfig dropAllConfig = new FlowConfig();
1854         dropAllConfig.setInstallInHw(true);
1855         dropAllConfig.setName("**Catch-All Drop");
1856         dropAllConfig.setPriority("0");
1857         dropAllConfig.setNode(node);
1858         dropAllConfig.setActions(dropAction);
1859         defaultConfigs.add(dropAllConfig);
1860
1861         for (FlowConfig fc : defaultConfigs) {
1862             if (proactive) {
1863                 addStaticFlow(fc, false);
1864             } else {
1865                 removeStaticFlow(fc);
1866             }
1867         }
1868
1869         log.info("Set Switch {} Mode to {}", node, (proactive ? "proactive" : "reactive"));
1870     }
1871
1872     /**
1873      * Remove from the databases all the flows installed on the node
1874      *
1875      * @param node
1876      */
1877     private void cleanDatabaseForNode(Node node) {
1878         log.info("Cleaning Flow database for Node {}", node);
1879         if (nodeFlows.containsKey(node)) {
1880             List<FlowEntryInstall> toRemove = new ArrayList<FlowEntryInstall>(nodeFlows.get(node));
1881
1882             for (FlowEntryInstall entry : toRemove) {
1883                 updateLocalDatabase(entry, false);
1884             }
1885         }
1886     }
1887
1888     @Override
1889     public void notifyNode(Node node, UpdateType type, Map<String, Property> propMap) {
1890         this.pendingEvents.offer(new NodeUpdateEvent(type, node));
1891     }
1892
1893     @Override
1894     public void notifyNodeConnector(NodeConnector nodeConnector, UpdateType type, Map<String, Property> propMap) {
1895
1896     }
1897
1898     private FlowConfig getDerivedFlowConfig(FlowConfig original, String configName, Short port) {
1899         FlowConfig derivedFlow = new FlowConfig(original);
1900         derivedFlow.setDynamic(true);
1901         derivedFlow.setPortGroup(null);
1902         derivedFlow.setName(original.getName() + "_" + configName + "_" + port);
1903         derivedFlow.setIngressPort(port + "");
1904         return derivedFlow;
1905     }
1906
1907     private void addPortGroupFlows(PortGroupConfig config, Node node, PortGroup data) {
1908         for (FlowConfig staticFlow : staticFlows.values()) {
1909             if (staticFlow.getPortGroup() == null) {
1910                 continue;
1911             }
1912             if ((staticFlow.getNode().equals(node)) && (staticFlow.getPortGroup().equals(config.getName()))) {
1913                 for (Short port : data.getPorts()) {
1914                     FlowConfig derivedFlow = getDerivedFlowConfig(staticFlow, config.getName(), port);
1915                     addStaticFlow(derivedFlow, false);
1916                 }
1917             }
1918         }
1919     }
1920
1921     private void removePortGroupFlows(PortGroupConfig config, Node node, PortGroup data) {
1922         for (FlowConfig staticFlow : staticFlows.values()) {
1923             if (staticFlow.getPortGroup() == null) {
1924                 continue;
1925             }
1926             if (staticFlow.getNode().equals(node) && staticFlow.getPortGroup().equals(config.getName())) {
1927                 for (Short port : data.getPorts()) {
1928                     FlowConfig derivedFlow = getDerivedFlowConfig(staticFlow, config.getName(), port);
1929                     removeStaticFlow(derivedFlow);
1930                 }
1931             }
1932         }
1933     }
1934
1935     @Override
1936     public void portGroupChanged(PortGroupConfig config, Map<Node, PortGroup> data, boolean add) {
1937         log.info("PortGroup Changed for: {} Data: {}", config, portGroupData);
1938         Map<Node, PortGroup> existingData = portGroupData.get(config);
1939         if (existingData != null) {
1940             for (Map.Entry<Node, PortGroup> entry : data.entrySet()) {
1941                 PortGroup existingPortGroup = existingData.get(entry.getKey());
1942                 if (existingPortGroup == null) {
1943                     if (add) {
1944                         existingData.put(entry.getKey(), entry.getValue());
1945                         addPortGroupFlows(config, entry.getKey(), entry.getValue());
1946                     }
1947                 } else {
1948                     if (add) {
1949                         existingPortGroup.getPorts().addAll(entry.getValue().getPorts());
1950                         addPortGroupFlows(config, entry.getKey(), entry.getValue());
1951                     } else {
1952                         existingPortGroup.getPorts().removeAll(entry.getValue().getPorts());
1953                         removePortGroupFlows(config, entry.getKey(), entry.getValue());
1954                     }
1955                 }
1956             }
1957         } else {
1958             if (add) {
1959                 portGroupData.put(config, data);
1960                 for (Node swid : data.keySet()) {
1961                     addPortGroupFlows(config, swid, data.get(swid));
1962                 }
1963             }
1964         }
1965     }
1966
1967     @Override
1968     public boolean addPortGroupConfig(String name, String regex, boolean restore) {
1969         PortGroupConfig config = portGroupConfigs.get(name);
1970         if (config != null) {
1971             return false;
1972         }
1973
1974         if ((portGroupProvider == null) && !restore) {
1975             return false;
1976         }
1977         if ((portGroupProvider != null) && (!portGroupProvider.isMatchCriteriaSupported(regex))) {
1978             return false;
1979         }
1980
1981         config = new PortGroupConfig(name, regex);
1982         portGroupConfigs.put(name, config);
1983         if (portGroupProvider != null) {
1984             portGroupProvider.createPortGroupConfig(config);
1985         }
1986         return true;
1987     }
1988
1989     @Override
1990     public boolean delPortGroupConfig(String name) {
1991         PortGroupConfig config = portGroupConfigs.get(name);
1992         if (config == null) {
1993             return false;
1994         }
1995
1996         if (portGroupProvider != null) {
1997             portGroupProvider.deletePortGroupConfig(config);
1998         }
1999         portGroupConfigs.remove(name);
2000         return true;
2001     }
2002
2003     private void usePortGroupConfig(String name) {
2004         PortGroupConfig config = portGroupConfigs.get(name);
2005         if (config == null) {
2006             return;
2007         }
2008         if (portGroupProvider != null) {
2009             Map<Node, PortGroup> data = portGroupProvider.getPortGroupData(config);
2010             portGroupData.put(config, data);
2011         }
2012     }
2013
2014     @Override
2015     public Map<String, PortGroupConfig> getPortGroupConfigs() {
2016         return portGroupConfigs;
2017     }
2018
2019     public boolean isPortGroupSupported() {
2020         if (portGroupProvider == null) {
2021             return false;
2022         }
2023         return true;
2024     }
2025
2026     public void setIContainer(IContainer s) {
2027         this.container = s;
2028     }
2029
2030     public void unsetIContainer(IContainer s) {
2031         if (this.container == s) {
2032             this.container = null;
2033         }
2034     }
2035
2036     @Override
2037     public PortGroupProvider getPortGroupProvider() {
2038         return portGroupProvider;
2039     }
2040
2041     public void unsetPortGroupProvider(PortGroupProvider portGroupProvider) {
2042         this.portGroupProvider = null;
2043     }
2044
2045     public void setPortGroupProvider(PortGroupProvider portGroupProvider) {
2046         this.portGroupProvider = portGroupProvider;
2047         portGroupProvider.registerPortGroupChange(this);
2048         for (PortGroupConfig config : portGroupConfigs.values()) {
2049             portGroupProvider.createPortGroupConfig(config);
2050         }
2051     }
2052
2053     public void setHostFinder(IfIptoHost hostFinder) {
2054         this.hostFinder = hostFinder;
2055     }
2056
2057     public void unsetHostFinder(IfIptoHost hostFinder) {
2058         if (this.hostFinder == hostFinder) {
2059             this.hostFinder = null;
2060         }
2061     }
2062
2063     public void setFrmAware(IForwardingRulesManagerAware obj) {
2064         this.frmAware.add(obj);
2065     }
2066
2067     public void unsetFrmAware(IForwardingRulesManagerAware obj) {
2068         this.frmAware.remove(obj);
2069     }
2070
2071     void setClusterContainerService(IClusterContainerServices s) {
2072         log.debug("Cluster Service set");
2073         this.clusterContainerService = s;
2074     }
2075
2076     void unsetClusterContainerService(IClusterContainerServices s) {
2077         if (this.clusterContainerService == s) {
2078             log.debug("Cluster Service removed!");
2079             this.clusterContainerService = null;
2080         }
2081     }
2082
2083     private String getContainerName() {
2084         if (container == null) {
2085             return GlobalConstants.DEFAULT.toString();
2086         }
2087         return container.getName();
2088     }
2089
2090     /**
2091      * Function called by the dependency manager when all the required
2092      * dependencies are satisfied
2093      *
2094      */
2095     void init() {
2096         frmAware = Collections.synchronizedSet(new HashSet<IForwardingRulesManagerAware>());
2097         frmFileName = GlobalConstants.STARTUPHOME.toString() + "frm_staticflows_" + this.getContainerName() + ".conf";
2098         portGroupFileName = GlobalConstants.STARTUPHOME.toString() + "portgroup_" + this.getContainerName() + ".conf";
2099
2100         inContainerMode = false;
2101
2102         if (portGroupProvider != null) {
2103             portGroupProvider.registerPortGroupChange(this);
2104         }
2105
2106         nonClusterObjectCreate();
2107
2108         cacheStartup();
2109
2110         registerWithOSGIConsole();
2111
2112         /*
2113          * If we are not the first cluster node to come up, do not initialize
2114          * the static flow entries ordinal
2115          */
2116         if (staticFlowsOrdinal.size() == 0) {
2117             staticFlowsOrdinal.put(0, Integer.valueOf(0));
2118         }
2119
2120         pendingEvents = new LinkedBlockingQueue<FRMEvent>();
2121
2122         // Initialize the event handler thread
2123         frmEventHandler = new Thread(new Runnable() {
2124             @Override
2125             public void run() {
2126                 while (!stopping) {
2127                     try {
2128                         FRMEvent event = pendingEvents.take();
2129                         if (event == null) {
2130                             log.warn("Dequeued null event");
2131                             continue;
2132                         }
2133                         if (event instanceof NodeUpdateEvent) {
2134                             NodeUpdateEvent update = (NodeUpdateEvent) event;
2135                             Node node = update.getNode();
2136                             switch (update.getUpdateType()) {
2137                             case ADDED:
2138                                 addStaticFlowsToSwitch(node);
2139                                 break;
2140                             case REMOVED:
2141                                 cleanDatabaseForNode(node);
2142                                 updateStaticFlowConfigsOnNodeDown(node);
2143                                 break;
2144                             default:
2145                             }
2146                         } else if (event instanceof ErrorReportedEvent) {
2147                             ErrorReportedEvent errEvent = (ErrorReportedEvent) event;
2148                             processErrorEvent(errEvent);
2149                         } else {
2150                             log.warn("Dequeued unknown event {}", event.getClass().getSimpleName());
2151                         }
2152                     } catch (InterruptedException e) {
2153                         log.warn("FRM EventHandler thread interrupted", e);
2154                     }
2155                 }
2156             }
2157         }, "FRM EventHandler Collector");
2158     }
2159
2160     /**
2161      * Function called by the dependency manager when at least one dependency
2162      * become unsatisfied or when the component is shutting down because for
2163      * example bundle is being stopped.
2164      *
2165      */
2166     void destroy() {
2167     }
2168
2169     /**
2170      * Function called by dependency manager after "init ()" is called and after
2171      * the services provided by the class are registered in the service registry
2172      *
2173      */
2174     void start() {
2175         // Initialize graceful stop flag
2176         stopping = false;
2177
2178         // Start event handler thread
2179         frmEventHandler.start();
2180
2181         /*
2182          * Read startup and build database if we have not already gotten the
2183          * configurations synced from another node
2184          */
2185         if (staticFlows.isEmpty()) {
2186             loadFlowConfiguration();
2187         }
2188     }
2189
2190     /**
2191      * Function called by the dependency manager before the services exported by
2192      * the component are unregistered, this will be followed by a "destroy ()"
2193      * calls
2194      */
2195     void stop() {
2196         stopping = true;
2197         uninstallAllFlowEntries();
2198     }
2199
2200     public void setFlowProgrammerService(IFlowProgrammerService service) {
2201         this.programmer = service;
2202     }
2203
2204     public void unsetFlowProgrammerService(IFlowProgrammerService service) {
2205         if (this.programmer == service) {
2206             this.programmer = null;
2207         }
2208     }
2209
2210     public void setSwitchManager(ISwitchManager switchManager) {
2211         this.switchManager = switchManager;
2212     }
2213
2214     public void unsetSwitchManager(ISwitchManager switchManager) {
2215         if (this.switchManager == switchManager) {
2216             this.switchManager = null;
2217         }
2218     }
2219
2220     @Override
2221     public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) {
2222
2223     }
2224
2225     @Override
2226     public void containerFlowUpdated(String containerName, ContainerFlow previousFlow, ContainerFlow currentFlow,
2227             UpdateType t) {
2228         /*
2229          * Whether it is an addition or removal, we have to recompute the merged
2230          * flows entries taking into account all the current container flows
2231          * because flow merging is not an injective function
2232          */
2233         updateFlowsContainerFlow();
2234     }
2235
2236     @Override
2237     public void nodeConnectorUpdated(String containerName, NodeConnector p, UpdateType t) {
2238         // No action
2239     }
2240
2241     @Override
2242     public void containerModeUpdated(UpdateType update) {
2243         switch (update) {
2244         case ADDED:
2245             this.inContainerMode = true;
2246             this.uninstallAllFlowEntries();
2247             break;
2248         case REMOVED:
2249             this.inContainerMode = false;
2250             this.reinstallAllFlowEntries();
2251             break;
2252         default:
2253         }
2254
2255         // Update our configuration DB
2256         updateStaticFlowConfigsOnContainerModeChange(update);
2257     }
2258
2259     protected abstract class FRMEvent {
2260
2261     }
2262
2263     private class NodeUpdateEvent extends FRMEvent {
2264         private final Node node;
2265         private final UpdateType update;
2266
2267         public NodeUpdateEvent(UpdateType update, Node node) {
2268             this.update = update;
2269             this.node = node;
2270         }
2271
2272         public UpdateType getUpdateType() {
2273             return update;
2274         }
2275
2276         public Node getNode() {
2277             return node;
2278         }
2279     }
2280
2281     private class ErrorReportedEvent extends FRMEvent {
2282         private final long rid;
2283         private final Node node;
2284         private final Object error;
2285
2286         public ErrorReportedEvent(long rid, Node node, Object error) {
2287             this.rid = rid;
2288             this.node = node;
2289             this.error = error;
2290         }
2291
2292         public long getRequestId() {
2293             return rid;
2294         }
2295
2296         public Object getError() {
2297             return error;
2298         }
2299
2300         public Node getNode() {
2301             return node;
2302         }
2303     }
2304
2305     /*
2306      * OSGI COMMANDS
2307      */
2308     @Override
2309     public String getHelp() {
2310         StringBuffer help = new StringBuffer();
2311         help.append("---FRM Matrix Application---\n");
2312         help.append("\t printMatrixData        - Prints the Matrix Configs\n");
2313         help.append("\t addMatrixConfig <name> <regex>\n");
2314         help.append("\t delMatrixConfig <name>\n");
2315         help.append("\t useMatrixConfig <name>\n");
2316         return help.toString();
2317     }
2318
2319     public void _printMatrixData(CommandInterpreter ci) {
2320         ci.println("Configs : ");
2321         ci.println("---------");
2322         ci.println(portGroupConfigs);
2323
2324         ci.println("Data : ");
2325         ci.println("------");
2326         ci.println(portGroupData);
2327     }
2328
2329     public void _addMatrixConfig(CommandInterpreter ci) {
2330         String name = ci.nextArgument();
2331         String regex = ci.nextArgument();
2332         addPortGroupConfig(name, regex, false);
2333     }
2334
2335     public void _delMatrixConfig(CommandInterpreter ci) {
2336         String name = ci.nextArgument();
2337         delPortGroupConfig(name);
2338     }
2339
2340     public void _useMatrixConfig(CommandInterpreter ci) {
2341         String name = ci.nextArgument();
2342         usePortGroupConfig(name);
2343     }
2344
2345     public void _arpPunt(CommandInterpreter ci) {
2346         String switchId = ci.nextArgument();
2347         long swid = HexEncode.stringToLong(switchId);
2348         Node node = NodeCreator.createOFNode(swid);
2349         installImplicitARPReplyPunt(node);
2350     }
2351
2352     public void _frmaddflow(CommandInterpreter ci) throws UnknownHostException {
2353         Node node = null;
2354         String nodeId = ci.nextArgument();
2355         if (nodeId == null) {
2356             ci.print("Node id not specified");
2357             return;
2358         }
2359         try {
2360             node = NodeCreator.createOFNode(Long.valueOf(nodeId));
2361         } catch (NumberFormatException e) {
2362             ci.print("Node id not a number");
2363             return;
2364         }
2365         ci.println(this.programmer.addFlow(node, getSampleFlow(node)));
2366     }
2367
2368     public void _frmremoveflow(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.removeFlow(node, getSampleFlow(node)));
2382     }
2383
2384     private Flow getSampleFlow(Node node) throws UnknownHostException {
2385         NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
2386         NodeConnector oport = NodeConnectorCreator.createOFNodeConnector((short) 30, node);
2387         byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
2388         byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
2389         InetAddress srcIP = InetAddress.getByName("172.28.30.50");
2390         InetAddress dstIP = InetAddress.getByName("171.71.9.52");
2391         InetAddress ipMask = InetAddress.getByName("255.255.255.0");
2392         InetAddress ipMask2 = InetAddress.getByName("255.0.0.0");
2393         short ethertype = EtherTypes.IPv4.shortValue();
2394         short vlan = (short) 27;
2395         byte vlanPr = 3;
2396         Byte tos = 4;
2397         byte proto = IPProtocols.TCP.byteValue();
2398         short src = (short) 55000;
2399         short dst = 80;
2400
2401         /*
2402          * Create a SAL Flow aFlow
2403          */
2404         Match match = new Match();
2405         match.setField(MatchType.IN_PORT, port);
2406         match.setField(MatchType.DL_SRC, srcMac);
2407         match.setField(MatchType.DL_DST, dstMac);
2408         match.setField(MatchType.DL_TYPE, ethertype);
2409         match.setField(MatchType.DL_VLAN, vlan);
2410         match.setField(MatchType.DL_VLAN_PR, vlanPr);
2411         match.setField(MatchType.NW_SRC, srcIP, ipMask);
2412         match.setField(MatchType.NW_DST, dstIP, ipMask2);
2413         match.setField(MatchType.NW_TOS, tos);
2414         match.setField(MatchType.NW_PROTO, proto);
2415         match.setField(MatchType.TP_SRC, src);
2416         match.setField(MatchType.TP_DST, dst);
2417
2418         List<Action> actions = new ArrayList<Action>();
2419         actions.add(new Output(oport));
2420         actions.add(new PopVlan());
2421         actions.add(new Flood());
2422         actions.add(new Controller());
2423         return new Flow(match, actions);
2424     }
2425
2426     @Override
2427     public Status saveConfiguration() {
2428         return saveConfig();
2429     }
2430
2431     public void _frmNodeFlows(CommandInterpreter ci) {
2432         String nodeId = ci.nextArgument();
2433         Node node = Node.fromString(nodeId);
2434         if (node == null) {
2435             ci.println("frmNodeFlows <node> [verbose]");
2436             return;
2437         }
2438         boolean verbose = false;
2439         String verboseCheck = ci.nextArgument();
2440         if (verboseCheck != null) {
2441             verbose = verboseCheck.equals("true");
2442         }
2443
2444         if (!nodeFlows.containsKey(node)) {
2445             return;
2446         }
2447         // Dump per node database
2448         for (FlowEntryInstall entry : nodeFlows.get(node)) {
2449             if (!verbose) {
2450                 ci.println(node + " " + installedSwView.get(entry).getFlowName());
2451             } else {
2452                 ci.println(node + " " + installedSwView.get(entry).toString());
2453             }
2454         }
2455     }
2456
2457     public void _frmGroupFlows(CommandInterpreter ci) {
2458         String group = ci.nextArgument();
2459         if (group == null) {
2460             ci.println("frmGroupFlows <group> [verbose]");
2461             return;
2462         }
2463         boolean verbose = false;
2464         String verboseCheck = ci.nextArgument();
2465         if (verboseCheck != null) {
2466             verbose = verboseCheck.equalsIgnoreCase("true");
2467         }
2468
2469         if (!groupFlows.containsKey(group)) {
2470             return;
2471         }
2472         // Dump per node database
2473         ci.println("Group " + group + ":\n");
2474         for (FlowEntryInstall flowEntry : groupFlows.get(group)) {
2475             if (!verbose) {
2476                 ci.println(flowEntry.getNode() + " " + flowEntry.getFlowName());
2477             } else {
2478                 ci.println(flowEntry.getNode() + " " + flowEntry.toString());
2479             }
2480         }
2481     }
2482
2483     @Override
2484     public void flowRemoved(Node node, Flow flow) {
2485         log.trace("Received flow removed notification on {} for {}", node, flow);
2486
2487         // For flow entry identification, only node, match and priority matter
2488         FlowEntryInstall test = new FlowEntryInstall(new FlowEntry("","",flow, node), null);
2489         FlowEntryInstall installedEntry = this.installedSwView.get(test);
2490         if (installedEntry == null) {
2491             log.trace("Entry is not known to us");
2492             return;
2493         }
2494
2495         // Update Static flow status
2496         Integer key = 0;
2497         FlowConfig target = null;
2498         for (Map.Entry<Integer, FlowConfig> entry : staticFlows.entrySet()) {
2499             FlowConfig conf = entry.getValue();
2500             if (conf.isByNameAndNodeIdEqual(installedEntry.getFlowName(), node)) {
2501                 key = entry.getKey();
2502                 target = conf;
2503                 break;
2504             }
2505         }
2506         if (target != null) {
2507             // Update Configuration database
2508             target.toggleInstallation();
2509             target.setStatus(SUCCESS);
2510             staticFlows.put(key, target);
2511         }
2512
2513         // Update software views
2514         this.updateLocalDatabase(installedEntry, false);
2515     }
2516
2517     @Override
2518     public void flowErrorReported(Node node, long rid, Object err) {
2519         log.trace("Got error {} for message rid {} from node {}", new Object[] { err, rid, node });
2520         pendingEvents.offer(new ErrorReportedEvent(rid, node, err));
2521     }
2522
2523     private void processErrorEvent(ErrorReportedEvent event) {
2524         Node node = event.getNode();
2525         long rid = event.getRequestId();
2526         Object error = event.getError();
2527         String errorString = (error == null) ? "Not provided" : error.toString();
2528         /*
2529          * If this was for a flow install, remove the corresponding entry from
2530          * the software view. If it was a Looking for the rid going through the
2531          * software database. TODO: A more efficient rid <-> FlowEntryInstall
2532          * mapping will have to be added in future
2533          */
2534         FlowEntryInstall target = null;
2535         for (FlowEntryInstall index : nodeFlows.get(node)) {
2536             FlowEntryInstall entry = installedSwView.get(index);
2537             if (entry.getRequestId() == rid) {
2538                 target = entry;
2539                 break;
2540             }
2541         }
2542         if (target != null) {
2543             // This was a flow install, update database
2544             this.updateLocalDatabase(target, false);
2545         }
2546
2547         // Notify listeners
2548         if (frmAware != null) {
2549             synchronized (frmAware) {
2550                 for (IForwardingRulesManagerAware frma : frmAware) {
2551                     try {
2552                         frma.requestFailed(rid, errorString);
2553                     } catch (Exception e) {
2554                         log.warn("Failed to notify {}", frma);
2555                     }
2556                 }
2557             }
2558         }
2559     }
2560
2561     @Override
2562     public Status solicitStatusResponse(Node node, boolean blocking) {
2563         Status rv = new Status(StatusCode.INTERNALERROR);
2564
2565         if (this.programmer != null) {
2566             if (blocking) {
2567                 rv = programmer.syncSendBarrierMessage(node);
2568             } else {
2569                 rv = programmer.asyncSendBarrierMessage(node);
2570             }
2571         }
2572
2573         return rv;
2574     }
2575 }