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