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