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