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