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