Merge "BUG-2329 Add test for anyxmls inside rpc resonse for netcfon-connector"
[controller.git] / opendaylight / containermanager / implementation / src / main / java / org / opendaylight / controller / containermanager / internal / ContainerManager.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.controller.containermanager.internal;
11
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.io.ObjectInputStream;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.EnumSet;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.Set;
26 import java.util.concurrent.ConcurrentMap;
27 import java.util.concurrent.CopyOnWriteArrayList;
28
29 import org.eclipse.osgi.framework.console.CommandInterpreter;
30 import org.eclipse.osgi.framework.console.CommandProvider;
31 import org.opendaylight.controller.appauth.authorization.Authorization;
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.IClusterGlobalServices;
36 import org.opendaylight.controller.clustering.services.IClusterServices;
37 import org.opendaylight.controller.configuration.ConfigurationObject;
38 import org.opendaylight.controller.configuration.IConfigurationAware;
39 import org.opendaylight.controller.configuration.IConfigurationService;
40 import org.opendaylight.controller.containermanager.ContainerChangeEvent;
41 import org.opendaylight.controller.containermanager.ContainerConfig;
42 import org.opendaylight.controller.containermanager.ContainerData;
43 import org.opendaylight.controller.containermanager.ContainerFlowChangeEvent;
44 import org.opendaylight.controller.containermanager.ContainerFlowConfig;
45 import org.opendaylight.controller.containermanager.IContainerAuthorization;
46 import org.opendaylight.controller.containermanager.IContainerManager;
47 import org.opendaylight.controller.containermanager.IContainerManagerShell;
48 import org.opendaylight.controller.containermanager.NodeConnectorsChangeEvent;
49 import org.opendaylight.controller.sal.authorization.AppRoleLevel;
50 import org.opendaylight.controller.sal.authorization.Privilege;
51 import org.opendaylight.controller.sal.authorization.Resource;
52 import org.opendaylight.controller.sal.authorization.ResourceGroup;
53 import org.opendaylight.controller.sal.authorization.UserLevel;
54 import org.opendaylight.controller.sal.core.ContainerFlow;
55 import org.opendaylight.controller.sal.core.IContainerAware;
56 import org.opendaylight.controller.sal.core.IContainerListener;
57 import org.opendaylight.controller.sal.core.IContainerLocalListener;
58 import org.opendaylight.controller.sal.core.Node;
59 import org.opendaylight.controller.sal.core.NodeConnector;
60 import org.opendaylight.controller.sal.core.UpdateType;
61 import org.opendaylight.controller.sal.match.Match;
62 import org.opendaylight.controller.sal.utils.GlobalConstants;
63 import org.opendaylight.controller.sal.utils.IObjectReader;
64 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
65 import org.opendaylight.controller.sal.utils.NodeCreator;
66 import org.opendaylight.controller.sal.utils.ServiceHelper;
67 import org.opendaylight.controller.sal.utils.Status;
68 import org.opendaylight.controller.sal.utils.StatusCode;
69 import org.opendaylight.controller.topologymanager.ITopologyManager;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73 public class ContainerManager extends Authorization<String> implements IContainerManager, IObjectReader,
74         CommandProvider, ICacheUpdateAware<String, Object>, IContainerInternal, IContainerAuthorization,
75         IConfigurationAware, IContainerManagerShell {
76     private static final Logger logger = LoggerFactory.getLogger(ContainerManager.class);
77     private static String CONTAINERS_FILE_NAME = "containers.conf";
78     private static final String allContainersGroup = "allContainers";
79     private IClusterGlobalServices clusterServices;
80     private IConfigurationService configurationService;
81     /*
82      * Collection containing the configuration objects. This is configuration
83      * world: container names (also the map key) are maintained as they were
84      * configured by user, same case
85      */
86     private ConcurrentMap<String, ContainerConfig> containerConfigs;
87     private ConcurrentMap<String, ContainerData> containerData;
88     private ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>> nodeConnectorToContainers;
89     private ConcurrentMap<Node, Set<String>> nodeToContainers;
90     private ConcurrentMap<String, Object> containerChangeEvents;
91     private final Set<IContainerAware> iContainerAware = Collections.synchronizedSet(new HashSet<IContainerAware>());
92     private final Set<IContainerListener> iContainerListener = Collections
93             .synchronizedSet(new HashSet<IContainerListener>());
94     private final Set<IContainerLocalListener> iContainerLocalListener = Collections
95             .synchronizedSet(new HashSet<IContainerLocalListener>());
96
97     void setIContainerListener(IContainerListener s) {
98         if (this.iContainerListener != null) {
99             this.iContainerListener.add(s);
100             /*
101              * At boot with startup, containers are created before listeners have
102              * joined. Replaying here the first container creation notification for
103              * the joining listener when containers are already present. Also
104              * replaying all the node connectors and container flows additions
105              * to the existing containers.
106              */
107             if (!this.containerData.isEmpty()) {
108                 s.containerModeUpdated(UpdateType.ADDED);
109             }
110             for (ConcurrentMap.Entry<NodeConnector, CopyOnWriteArrayList<String>> entry : nodeConnectorToContainers
111                     .entrySet()) {
112                 NodeConnector port = entry.getKey();
113                 for (String container : entry.getValue()) {
114                     s.nodeConnectorUpdated(container, port, UpdateType.ADDED);
115                 }
116             }
117             for (Map.Entry<String, ContainerData> container : containerData.entrySet()) {
118                 for (ContainerFlow cFlow : container.getValue().getContainerFlowSpecs()) {
119                     s.containerFlowUpdated(container.getKey(), cFlow, cFlow, UpdateType.ADDED);
120                 }
121             }
122         }
123     }
124
125     void unsetIContainerListener(IContainerListener s) {
126         if (this.iContainerListener != null) {
127             this.iContainerListener.remove(s);
128         }
129     }
130
131     void setIContainerLocalListener(IContainerLocalListener s) {
132         if (this.iContainerLocalListener != null) {
133             this.iContainerLocalListener.add(s);
134         }
135     }
136
137     void unsetIContainerLocalListener(IContainerLocalListener s) {
138         if (this.iContainerLocalListener != null) {
139             this.iContainerLocalListener.remove(s);
140         }
141     }
142
143     public void setIContainerAware(IContainerAware iContainerAware) {
144         if (!this.iContainerAware.contains(iContainerAware)) {
145             this.iContainerAware.add(iContainerAware);
146             // Now call the container creation for all the known containers so far
147             for (String container : getContainerNameList()) {
148                 iContainerAware.containerCreate(container.toLowerCase(Locale.ENGLISH));
149             }
150         }
151     }
152
153     public void unsetIContainerAware(IContainerAware iContainerAware) {
154         this.iContainerAware.remove(iContainerAware);
155         // There is no need to do cleanup of the component when
156         // unregister because it will be taken care by the Container
157         // component itself
158     }
159
160     public void setClusterServices(IClusterGlobalServices i) {
161         this.clusterServices = i;
162         logger.debug("IClusterServices set");
163     }
164
165     public void unsetClusterServices(IClusterGlobalServices i) {
166         if (this.clusterServices == i) {
167             this.clusterServices = null;
168             logger.debug("IClusterServices Unset");
169         }
170     }
171
172     public void setConfigurationService(IConfigurationService service) {
173         logger.trace("Got configuration service set request {}", service);
174         this.configurationService = service;
175     }
176
177     public void unsetConfigurationService(IConfigurationService service) {
178         logger.trace("Got configuration service UNset request");
179         this.configurationService = null;
180     }
181
182     private void allocateCaches() {
183         logger.debug("Container Manager allocating caches");
184
185         if (clusterServices == null) {
186             logger.warn("un-initialized Cluster Services, can't allocate caches");
187             return;
188         }
189         try {
190             clusterServices.createCache("containermgr.containerConfigs", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
191
192             clusterServices.createCache("containermgr.event.containerChange",
193                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
194
195             clusterServices.createCache("containermgr.containerData", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
196
197             clusterServices.createCache("containermgr.nodeConnectorToContainers",
198                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
199
200             clusterServices.createCache("containermgr.nodeToContainers", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
201
202             clusterServices.createCache("containermgr.containerGroups", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
203
204             clusterServices.createCache("containermgr.containerAuthorizations",
205                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
206
207             clusterServices.createCache("containermgr.roles", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
208         } catch (CacheConfigException cce) {
209             logger.error("Cache configuration invalid - check cache mode");
210         } catch (CacheExistException ce) {
211             logger.error("Cache already exits - destroy and recreate if needed");
212         }
213     }
214
215     @SuppressWarnings({ "unchecked" })
216     private void retrieveCaches() {
217         logger.debug("Container Manager retrieving caches");
218
219         if (clusterServices == null) {
220             logger.warn("un-initialized Cluster Services, can't retrieve caches");
221             return;
222         }
223
224         containerConfigs = (ConcurrentMap<String, ContainerConfig>) clusterServices.getCache("containermgr.containerConfigs");
225
226         containerChangeEvents = (ConcurrentMap<String, Object>) clusterServices.getCache("containermgr.event.containerChange");
227
228         containerData = (ConcurrentMap<String, ContainerData>) clusterServices.getCache("containermgr.containerData");
229
230         nodeConnectorToContainers = (ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>>) clusterServices
231                 .getCache("containermgr.nodeConnectorToContainers");
232
233         nodeToContainers = (ConcurrentMap<Node, Set<String>>) clusterServices.getCache("containermgr.nodeToContainers");
234
235         resourceGroups = (ConcurrentMap<String, Set<String>>) clusterServices.getCache("containermgr.containerGroups");
236
237         groupsAuthorizations = (ConcurrentMap<String, Set<ResourceGroup>>) clusterServices
238                 .getCache("containermgr.containerAuthorizations");
239
240         roles = (ConcurrentMap<String, AppRoleLevel>) clusterServices.getCache("containermgr.roles");
241
242         if (inContainerMode()) {
243             for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
244                 // Notify global and local listeners about the mode change
245                 notifyContainerChangeInternal(entry.getValue(), UpdateType.ADDED, true);
246             }
247         }
248     }
249
250     @Override
251     public void entryCreated(String containerName, String cacheName, boolean originLocal) {
252
253     }
254
255     @Override
256     public void entryUpdated(String key, Object value, String cacheName, boolean originLocal) {
257         /*
258          * This is were container manager replays a configuration event that was
259          * notified by its peer from a cluster node where the configuration
260          * happened. Only the global listeners, the cluster unaware classes,
261          * (mainly the shim classes in the sdn protocol plugins) need to receive
262          * these notifications on this cluster node. The cluster aware classes,
263          * like the functional modules which reacts on these events, must _not_
264          * be notified to avoid parallel computation in the cluster.
265          */
266         if (!originLocal) {
267             if (value instanceof NodeConnectorsChangeEvent) {
268                 NodeConnectorsChangeEvent event = (NodeConnectorsChangeEvent) value;
269                 List<NodeConnector> ncList = event.getNodeConnectors();
270                 notifyContainerEntryChangeInternal(key, ncList, event.getUpdateType(), false);
271             } else if (value instanceof ContainerFlowChangeEvent) {
272                 ContainerFlowChangeEvent event = (ContainerFlowChangeEvent) value;
273                 notifyCFlowChangeInternal(key, event.getConfigList(), event.getUpdateType(), false);
274             } else if (value instanceof ContainerChangeEvent) {
275                 ContainerChangeEvent event = (ContainerChangeEvent) value;
276                 notifyContainerChangeInternal(event.getConfig(), event.getUpdateType(), false);
277             }
278         }
279     }
280
281     @Override
282     public void entryDeleted(String containerName, String cacheName, boolean originLocal) {
283     }
284
285     public ContainerManager() {
286     }
287
288     public void init() {
289
290     }
291
292     public void start() {
293         // Get caches from cluster manager
294         allocateCaches();
295         retrieveCaches();
296
297         // Allocates default groups and association to default roles
298         createDefaultAuthorizationGroups();
299
300         // Read startup configuration and create local database
301         loadContainerConfig();
302     }
303
304     public void destroy() {
305         // Clear local states
306         this.iContainerAware.clear();
307         this.iContainerListener.clear();
308         this.iContainerLocalListener.clear();
309     }
310
311     /**
312      * Adds/Remove the list of flow specs to/from the specified container. This
313      * function is supposed to be called after all the validation checks have
314      * already been run on the proposed configuration.
315      */
316     private Status updateContainerFlow(String containerName, List<ContainerFlowConfig> confList, boolean delete) {
317         ContainerData container = getContainerByName(containerName);
318         if (container == null) {
319             return new Status(StatusCode.GONE, "Container not present");
320         }
321
322         for (ContainerFlowConfig conf : confList) {
323             // Validation was fine. Modify the database now.
324             for (Match match : conf.getMatches()) {
325                 ContainerFlow cFlow = new ContainerFlow(match);
326                 if (delete) {
327                     logger.trace("Removing Flow Spec {} from Container {}", conf.getName(), containerName);
328                     container.deleteFlowSpec(cFlow);
329                 } else {
330                     logger.trace("Adding Flow Spec {} to Container {}", conf.getName(), containerName);
331                     container.addFlowSpec(cFlow);
332
333                 }
334                 // Update Database
335                 putContainerDataByName(containerName, container);
336             }
337         }
338         return new Status(StatusCode.SUCCESS);
339     }
340
341     /**
342      * Adds/Remove this container to/from the Container database, no updates are going
343      * to be generated here other that the destroying and creation of the container.
344      * This function is supposed to be called after all the validation checks
345      * have already been run on the configuration object
346      */
347     private Status updateContainerDatabase(ContainerConfig containerConf, boolean delete) {
348         /*
349          * Back-end world here, container names are all stored in lower case
350          */
351         String containerName = containerConf.getContainerName();
352         ContainerData container = getContainerByName(containerName);
353         if (delete && container == null) {
354             return new Status(StatusCode.NOTFOUND, "Container is not present");
355         }
356         if (!delete && container != null) {
357             // A container with the same (lower case) name already exists
358             return new Status(StatusCode.CONFLICT, "A container with the same name already exists");
359         }
360         if (delete) {
361             logger.debug("Removing container {}", containerName);
362             removeNodeToContainersMapping(container);
363             removeNodeConnectorToContainersMapping(container);
364             removeContainerDataByName(containerName);
365         } else {
366             logger.debug("Adding container {}", containerName);
367             container = new ContainerData(containerConf);
368             putContainerDataByName(containerName, container);
369
370             // If flow specs are specified, add them
371             if (containerConf.hasFlowSpecs()) {
372                 updateContainerFlow(containerName, containerConf.getContainerFlowConfigs(), delete);
373             }
374
375             // If ports are specified, add them
376             if (!containerConf.getPortList().isEmpty()) {
377                 updateContainerEntryDatabase(containerName, containerConf.getPortList(), delete);
378             }
379         }
380         return new Status(StatusCode.SUCCESS);
381     }
382
383     private void removeNodeConnectorToContainersMapping(ContainerData container) {
384         Iterator<Entry<NodeConnector, CopyOnWriteArrayList<String>>> it = nodeConnectorToContainers.entrySet().iterator();
385         String containerName = container.getContainerName();
386         for (; it.hasNext();) {
387             Entry<NodeConnector, CopyOnWriteArrayList<String>> entry = it.next();
388             final NodeConnector nc = entry.getKey();
389             final CopyOnWriteArrayList<String> slist = entry.getValue();
390             for (final String sdata : slist) {
391                 if (sdata.equalsIgnoreCase(containerName)) {
392                     logger.debug("Removing NodeConnector->Containers mapping, nodeConnector: {}", nc);
393                     slist.remove(containerName);
394                     if (slist.isEmpty()) {
395                         nodeConnectorToContainers.remove(nc);
396                     } else {
397                         nodeConnectorToContainers.put(nc, slist);
398                     }
399                     break;
400                 }
401             }
402         }
403     }
404
405     private void removeNodeToContainersMapping(ContainerData container) {
406         for (Entry<Node, Set<String>> entry : nodeToContainers.entrySet()) {
407             Node node = entry.getKey();
408             for (String sdata : entry.getValue()) {
409                 if (sdata.equals(container.getContainerName())) {
410                     logger.debug("Removing Node->Containers mapping, node {} container {}", node, sdata);
411                     Set<String> value = nodeToContainers.get(node);
412                     value.remove(sdata);
413                     nodeToContainers.put(node, value);
414                     break;
415                 }
416             }
417         }
418     }
419
420     /**
421      * Adds/Remove container data to/from the container. This function is supposed to be
422      * called after all the validation checks have already been run on the
423      * configuration object
424      */
425     private Status updateContainerEntryDatabase(String containerName, List<NodeConnector> nodeConnectors, boolean delete) {
426         ContainerData container = getContainerByName(containerName);
427         // Presence check
428         if (container == null) {
429             return new Status(StatusCode.NOTFOUND, "Container Not Found");
430         }
431
432         // Check changes in the portlist
433         for (NodeConnector port : nodeConnectors) {
434             Node node = port.getNode();
435             if (delete) {
436                 container.removePortFromSwitch(port);
437                 putContainerDataByName(containerName, container);
438
439                 /* remove <sp> - container mapping */
440                 if (nodeConnectorToContainers.containsKey(port)) {
441                     nodeConnectorToContainers.remove(port);
442                 }
443                 /*
444                  * If no more ports in the switch, remove switch from container
445                  * Generate switchRemoved Event
446                  */
447                 if (container.portListEmpty(node)) {
448                     logger.debug("Port List empty for switch {}", node);
449                     putContainerDataByName(containerName, container);
450                     // remove node->containers mapping
451                     Set<String> slist = nodeToContainers.get(node);
452                     if (slist != null) {
453                         logger.debug("Removing container from switch-container list. node{}, container{}", node, containerName);
454                         slist.remove(container.getContainerName());
455                         nodeToContainers.put(node, slist);
456                         if (slist.isEmpty()) {
457                             logger.debug("Container list empty for switch {}. removing switch-container mapping", node);
458                             nodeToContainers.remove(node);
459                         }
460                     }
461                 }
462             } else {
463                 if (container.isSwitchInContainer(node) == false) {
464                     Set<String> value = nodeToContainers.get(node);
465                     // Add node->containers mapping
466                     if (value == null) {
467                         value = new HashSet<String>();
468                         logger.debug("Creating new Container Set for switch {}", node);
469                     }
470                     value.add(container.getContainerName());
471                     nodeToContainers.put(node, value);
472                 }
473                 container.addPortToSwitch(port);
474                 putContainerDataByName(containerName, container);
475
476                 // added nc->containers mapping
477                 CopyOnWriteArrayList<String> slist = nodeConnectorToContainers.get(port);
478                 if (slist == null) {
479                     slist = new CopyOnWriteArrayList<String>();
480                 }
481                 slist.add(container.getContainerName());
482                 nodeConnectorToContainers.put(port, slist);
483             }
484         }
485         return new Status(StatusCode.SUCCESS);
486     }
487
488     private Status validateContainerFlowAddRemoval(String containerName, ContainerFlow cFlow, boolean delete) {
489         /*
490          * It used to be the comment below: ~~~~~~~~~~~~~~~~~~~~ If Link Sharing
491          * at Host facing interfaces, then disallow last ContainerFlow removal
492          * ~~~~~~~~~~~~~~~~~~~~ But the interface being host facing is a
493          * condition that can change at runtime and so the final effect will be
494          * unreliable. So now we will always allow the container flow removal,
495          * if this is a link host facing and is shared by many that will cause
496          * issues but that validation should be done not at the configuration
497          * but in the UI/northbound side.
498          */
499         ContainerData container = this.getContainerByName(containerName);
500         if (container == null) {
501             String error = String.format("Cannot validate flow specs for container %s: (Container does not exist)", containerName);
502             logger.warn(error);
503             return new Status(StatusCode.BADREQUEST, error);
504         }
505
506         if (delete) {
507             Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
508             // Go through all the installed containers
509             for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
510                 if (containerName.equalsIgnoreCase(entry.getKey())) {
511                     continue;
512                 }
513                 // Derive the common ports
514                 Set<NodeConnector> commonPorts = entry.getValue().getNodeConnectors();
515                 commonPorts.retainAll(thisContainerPorts);
516                 if (commonPorts.isEmpty()) {
517                     continue;
518                 }
519
520                 // Check if this operation would remove the only flow spec
521                 // assigned to this container
522                 if (container.getFlowSpecCount() == 1) {
523                     if (!container.hasStaticVlanAssigned()) {
524                         // Ports are shared and static vlan is not present: this
525                         // is a failure
526                         // regardless the shared ports are host facing or
527                         // interswitch ports
528                         return new Status(StatusCode.BADREQUEST, "Container shares port with another container: "
529                                 + "The only one flow spec assigned to this container cannot be removed,"
530                                 + "because this container is not assigned any static vlan");
531                     }
532
533                     // Check on host facing port
534                     ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(
535                             ITopologyManager.class, GlobalConstants.DEFAULT.toString(), this);
536                     if (topologyManager == null) {
537                         return new Status(StatusCode.NOSERVICE,
538                                 "Cannot validate the request: Required service is not available");
539                     }
540                     for (NodeConnector nc : commonPorts) {
541                         /*
542                          * Shared link case : For internal port check if it has
543                          * a vlan configured. If vlan is configured, allow the
544                          * flowspec to be deleted If the port is host-facing, do
545                          * not allow the flowspec to be deleted
546                          */
547                         if (!topologyManager.isInternal(nc)) {
548                             return new Status(StatusCode.BADREQUEST, String.format(
549                                     "Port %s is shared and is host facing port: "
550                                             + "The only one flow spec assigned to this container cannot be removed", nc));
551                         }
552                     }
553                 }
554             }
555         } else {
556             // Adding a new flow spec: need to check if other containers with common
557             // ports do not have same flow spec
558             Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
559             List<ContainerFlow> proposed = new ArrayList<ContainerFlow>(container.getContainerFlowSpecs());
560             proposed.add(cFlow);
561             for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
562                 if (containerName.equalsIgnoreCase(entry.getKey())) {
563                     continue;
564                 }
565                 ContainerData otherContainer = entry.getValue();
566                 Set<NodeConnector> commonPorts = otherContainer.getNodeConnectors();
567                 commonPorts.retainAll(thisContainerPorts);
568
569                 if (!commonPorts.isEmpty()) {
570                     Status status = checkCommonContainerFlow(otherContainer.getContainerFlowSpecs(), proposed);
571                     if (!status.isSuccess()) {
572                         return new Status(StatusCode.BADREQUEST, String.format(
573                                 "Container %s which shares ports with this container has overlapping flow spec: %s",
574                                 entry.getKey(), status.getDescription()));
575                     }
576                 }
577             }
578         }
579
580         return new Status(StatusCode.SUCCESS);
581     }
582
583     /**
584      * Checks if the passed list of node connectors can be safely applied to the
585      * specified existing container in terms of port sharing with other containers.
586      *
587      * @param containerName
588      *            the name of the existing container
589      * @param portList
590      *            the list of node connectors to be added to the container
591      * @return the status object representing the result of the check
592      */
593     private Status validatePortSharing(String containerName, List<NodeConnector> portList) {
594         ContainerData container = this.getContainerByName(containerName);
595         if (container == null) {
596             String error = String
597                     .format("Cannot validate port sharing for container %s: (container does not exist)", containerName);
598             logger.error(error);
599             return new Status(StatusCode.BADREQUEST, error);
600         }
601         return validatePortSharingInternal(portList, container.getContainerFlowSpecs());
602     }
603
604     /**
605      * Checks if the proposed container configuration is valid to be applied in
606      * terms of port sharing with other containers.
607      *
608      * @param containerConf
609      *            the container configuration object containing the list of node
610      *            connectors
611      * @return the status object representing the result of the check
612      */
613     private Status validatePortSharing(ContainerConfig containerConf) {
614         return validatePortSharingInternal(containerConf.getPortList(), containerConf.getContainerFlowSpecs());
615     }
616
617     /*
618      * If any port is shared with an existing container, need flowSpec to be
619      * configured. If no flowSpec for this or other container, or if containers have any
620      * overlapping flowspec in common, then let the caller know this
621      * configuration has to be rejected.
622      */
623     private Status validatePortSharingInternal(List<NodeConnector> portList, List<ContainerFlow> flowSpecList) {
624         for (NodeConnector port : portList) {
625             List<String> slist = nodeConnectorToContainers.get(port);
626             if (slist != null && !slist.isEmpty()) {
627                 for (String otherContainerName : slist) {
628                     String msg = null;
629                     ContainerData other = containerData.get(otherContainerName);
630                     if (flowSpecList.isEmpty()) {
631                         msg = String.format("Port %s is shared and flow spec is empty for this container", port);
632                     } else if (other.isFlowSpecEmpty()) {
633                         msg = String.format("Port %s is shared and flow spec is empty for the other container", port);
634                     } else if (!checkCommonContainerFlow(flowSpecList, other.getContainerFlowSpecs()).isSuccess()) {
635                         msg = String.format("Port %s is shared and other container has common flow spec", port);
636                     }
637                     if (msg != null) {
638                         logger.debug(msg);
639                         return new Status(StatusCode.BADREQUEST, msg);
640                     }
641                 }
642             }
643         }
644         return new Status(StatusCode.SUCCESS);
645     }
646
647     /**
648      * Utility function to check if two lists of container flows share any same
649      * or overlapping container flows.
650      *
651      * @param oneFlowList
652      *            One of the two lists of container flows to test
653      * @param twoFlowList
654      *            One of the two lists of container flows to test
655      * @return The status of the check. Either SUCCESS or CONFLICT. In case of
656      *         conflict, the Status will contain the description for the failed
657      *         check.
658      */
659     private Status checkCommonContainerFlow(List<ContainerFlow> oneFlowList, List<ContainerFlow> twoFlowList) {
660         for (ContainerFlow oneFlow : oneFlowList) {
661             for (ContainerFlow twoFlow : twoFlowList) {
662                 if (oneFlow.getMatch().intersetcs(twoFlow.getMatch())) {
663                     return new Status(StatusCode.CONFLICT, String.format("Flow Specs overlap: %s %s",
664                             oneFlow.getMatch(), twoFlow.getMatch()));
665                 }
666             }
667         }
668         return new Status(StatusCode.SUCCESS);
669     }
670
671     /**
672      * Return the ContainerData object for the passed container name. Given this is a
673      * backend database, the lower case version of the passed name is used while
674      * searching for the corresponding ContainerData object.
675      *
676      * @param name
677      *            The container name in any case
678      * @return The corresponding ContainerData object
679      */
680     private ContainerData getContainerByName(String name) {
681         return containerData.get(name.toLowerCase(Locale.ENGLISH));
682     }
683
684     /**
685      * Add a ContainerData object for the given container name.
686      *
687      * @param name
688      *            The container name in any case
689      * @param sData
690      *            The container data object
691      */
692     private void putContainerDataByName(String name, ContainerData sData) {
693         containerData.put(name.toLowerCase(Locale.ENGLISH), sData);
694     }
695
696     /**
697      * Removes the ContainerData object for the given container name.
698      *
699      * @param name
700      *            The container name in any case
701      */
702     private void removeContainerDataByName(String name) {
703         containerData.remove(name.toLowerCase(Locale.ENGLISH));
704     }
705
706     @Override
707     public List<ContainerConfig> getContainerConfigList() {
708         return new ArrayList<ContainerConfig>(containerConfigs.values());
709     }
710
711     @Override
712     public ContainerConfig getContainerConfig(String containerName) {
713         ContainerConfig target = containerConfigs.get(containerName);
714         return (target == null) ? null : new ContainerConfig(target);
715     }
716
717     @Override
718     public List<String> getContainerNameList() {
719         /*
720          * Return container names as they were configured by user (case sensitive)
721          * along with the default container
722          */
723         List<String> containerNameList = new ArrayList<String>();
724         containerNameList.add(GlobalConstants.DEFAULT.toString());
725         containerNameList.addAll(containerConfigs.keySet());
726         return containerNameList;
727     }
728
729     @Override
730     public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
731         Map<String, List<ContainerFlowConfig>> flowSpecConfig = new HashMap<String, List<ContainerFlowConfig>>();
732         for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
733             List<ContainerFlowConfig> set = entry.getValue().getContainerFlowConfigs();
734             flowSpecConfig.put(entry.getKey(), set);
735         }
736         return flowSpecConfig;
737     }
738
739     private Status saveContainerConfig() {
740         return saveContainerConfigLocal();
741     }
742
743     public Status saveContainerConfigLocal() {
744         Status status = configurationService.persistConfiguration(
745                 new ArrayList<ConfigurationObject>(containerConfigs.values()), CONTAINERS_FILE_NAME);
746
747         if (!status.isSuccess()) {
748             return new Status(StatusCode.INTERNALERROR, "Failed to save container configurations: "
749                     + status.getDescription());
750         }
751         return status;
752     }
753
754     /**
755      * Create and initialize default all resource group and create association
756      * with default well known users and profiles, if not already learnt from
757      * another cluster node
758      */
759     private void createDefaultAuthorizationGroups() {
760         allResourcesGroupName = ContainerManager.allContainersGroup;
761
762         // Add the default container to the all containers group if needed
763         String defaultContainer = GlobalConstants.DEFAULT.toString();
764         Set<String> allContainers = (resourceGroups.containsKey(allResourcesGroupName)) ? resourceGroups
765                 .get(allResourcesGroupName) : new HashSet<String>();
766         if (!allContainers.contains(defaultContainer)) {
767             // Add Default container
768             allContainers.add(defaultContainer);
769             // Update cluster
770             resourceGroups.put(allResourcesGroupName, allContainers);
771         }
772
773         // Add the controller well known roles, if not known already
774         if (!roles.containsKey(UserLevel.SYSTEMADMIN.toString())) {
775             roles.put(UserLevel.SYSTEMADMIN.toString(), AppRoleLevel.APPADMIN);
776         }
777         if (!roles.containsKey(UserLevel.NETWORKADMIN.toString())) {
778             roles.put(UserLevel.NETWORKADMIN.toString(), AppRoleLevel.APPADMIN);
779         }
780         if (!roles.containsKey(UserLevel.NETWORKOPERATOR.toString())) {
781             roles.put(UserLevel.NETWORKOPERATOR.toString(), AppRoleLevel.APPOPERATOR);
782         }
783
784         /*
785          * Create and add the all containers user groups and associate them to the
786          * default well known user roles, if not present already
787          */
788         if (!groupsAuthorizations.containsKey(UserLevel.NETWORKADMIN.toString())) {
789             Set<ResourceGroup> writeProfile = new HashSet<ResourceGroup>(1);
790             Set<ResourceGroup> readProfile = new HashSet<ResourceGroup>(1);
791             writeProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.WRITE));
792             readProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.READ));
793             groupsAuthorizations.put(UserLevel.SYSTEMADMIN.toString(), writeProfile);
794             groupsAuthorizations.put(UserLevel.NETWORKADMIN.toString(), writeProfile);
795             groupsAuthorizations.put(UserLevel.NETWORKOPERATOR.toString(), readProfile);
796         }
797     }
798
799     /**
800      * Until manual configuration is not available, automatically maintain the
801      * well known resource groups
802      *
803      * @param containerName
804      * @param delete
805      */
806     private void updateResourceGroups(ContainerConfig containerConf, boolean delete) {
807         // Container Roles and Container Resource Group
808         String containerName = containerConf.getContainer();
809         String groupName = containerConf.getContainerGroupName();
810         String containerAdminRole = containerConf.getContainerAdminRole();
811         String containerOperatorRole = containerConf.getContainerOperatorRole();
812         Set<String> allContainerSet = resourceGroups.get(allResourcesGroupName);
813         if (delete) {
814             resourceGroups.remove(groupName);
815             groupsAuthorizations.remove(containerAdminRole);
816             groupsAuthorizations.remove(containerOperatorRole);
817             roles.remove(containerAdminRole);
818             roles.remove(containerOperatorRole);
819             // Update the all container group
820             allContainerSet.remove(containerName);
821         } else {
822             Set<String> resources = new HashSet<String>(1);
823             resources.add(containerName);
824             resourceGroups.put(groupName, resources);
825             Set<ResourceGroup> adminGroups = new HashSet<ResourceGroup>(1);
826             Set<ResourceGroup> operatorGroups = new HashSet<ResourceGroup>(1);
827             adminGroups.add(new ResourceGroup(groupName, Privilege.WRITE));
828             operatorGroups.add(new ResourceGroup(groupName, Privilege.READ));
829             groupsAuthorizations.put(containerAdminRole, adminGroups);
830             groupsAuthorizations.put(containerOperatorRole, operatorGroups);
831             roles.put(containerAdminRole, AppRoleLevel.APPADMIN);
832             roles.put(containerOperatorRole, AppRoleLevel.APPOPERATOR);
833             // Update the all containers resource group
834             allContainerSet.add(containerName);
835         }
836         // Update resource groups in cluster
837         resourceGroups.put(allResourcesGroupName, allContainerSet);
838     }
839
840     /**
841      * Notify ContainerAware listeners of the creation/deletion of the container
842      *
843      * @param containerName
844      * @param delete
845      *            true is container was removed, false otherwise
846      */
847     private void notifyContainerAwareListeners(String containerName, boolean delete) {
848         // Back-end World: container name forced to lower case
849         String name = containerName.toLowerCase(Locale.ENGLISH);
850
851         synchronized (this.iContainerAware) {
852             for (IContainerAware i : this.iContainerAware) {
853                 if (delete) {
854                     i.containerDestroy(name);
855                 } else {
856                     i.containerCreate(name);
857                 }
858             }
859         }
860     }
861
862     /**
863      * Notify the ContainerListener listeners in case the container mode has
864      * changed following a container configuration operation Note: this call
865      * must happen after the configuration db has been updated
866      *
867      * @param lastActionDelete
868      *            true if the last container configuration operation was a
869      *            container delete operation
870      * @param notifyLocal
871      *            if true, the notification is also sent to the
872      *            IContainerLocalListener classes besides the IContainerListener
873      *            classes
874      */
875     private void notifyContainerModeChange(boolean lastActionDelete, boolean notifyLocal) {
876         if (lastActionDelete == false && containerConfigs.size() == 1) {
877             logger.trace("First container Creation. Inform listeners");
878             synchronized (this.iContainerListener) {
879                 for (IContainerListener i : this.iContainerListener) {
880                     i.containerModeUpdated(UpdateType.ADDED);
881                 }
882             }
883             if (notifyLocal) {
884                 synchronized (this.iContainerLocalListener) {
885                     for (IContainerLocalListener i : this.iContainerLocalListener) {
886                         i.containerModeUpdated(UpdateType.ADDED);
887                     }
888                 }
889             }
890         } else if (lastActionDelete == true && containerConfigs.isEmpty()) {
891             logger.trace("Last container Deletion. Inform listeners");
892             synchronized (this.iContainerListener) {
893                 for (IContainerListener i : this.iContainerListener) {
894                     i.containerModeUpdated(UpdateType.REMOVED);
895                 }
896             }
897             if (notifyLocal) {
898                 synchronized (this.iContainerLocalListener) {
899                     for (IContainerLocalListener i : this.iContainerLocalListener) {
900                         i.containerModeUpdated(UpdateType.REMOVED);
901                     }
902                 }
903             }
904         }
905     }
906
907     private Status addRemoveContainerEntries(String containerName, List<String> nodeConnectorsString, boolean delete) {
908         // Construct action message
909         String action = String.format("Node connector(s) %s container %s: %s", delete ? "removal from" : "addition to",
910                 containerName, nodeConnectorsString);
911
912         // Validity Check
913         if (nodeConnectorsString == null || nodeConnectorsString.isEmpty()) {
914             return new Status(StatusCode.BADREQUEST, "Node connector list is null or empty");
915         }
916
917         // Presence check
918         ContainerConfig entryConf = containerConfigs.get(containerName);
919         if (entryConf == null) {
920             String msg = String.format("Container not found: %s", containerName);
921             String error = String.format("Failed to apply %s: (%s)", action, msg);
922             logger.warn(error);
923             return new Status(StatusCode.NOTFOUND, msg);
924         }
925
926         // Validation check
927         Status status = ContainerConfig.validateNodeConnectors(nodeConnectorsString);
928         if (!status.isSuccess()) {
929             String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
930             logger.warn(error);
931             return status;
932         }
933
934         List<NodeConnector> nodeConnectors = ContainerConfig.nodeConnectorsFromString(nodeConnectorsString);
935
936         // Port sharing check
937         if (!delete) {
938             /*
939              * Check if the ports being added to this container already belong to
940              * other containers. If so check whether the the appropriate flow specs
941              * are configured on this container
942              */
943             status = validatePortSharing(containerName, nodeConnectors);
944             if (!status.isSuccess()) {
945                 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
946                 logger.warn(error);
947                 return status;
948             }
949         }
950
951         // Update Database
952         status = updateContainerEntryDatabase(containerName, nodeConnectors, delete);
953         if (!status.isSuccess()) {
954             String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
955             logger.warn(error);
956             return status;
957         }
958
959         // Update Configuration
960         status = (delete) ? entryConf.removeNodeConnectors(nodeConnectorsString) : entryConf
961                 .addNodeConnectors(nodeConnectorsString);
962         if (!status.isSuccess()) {
963             String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
964             logger.warn(error);
965             // Revert backend changes
966             Status statusRevert = updateContainerEntryDatabase(containerName, nodeConnectors, !delete);
967             if (!statusRevert.isSuccess()) {
968                 // Unlikely
969                 logger.error("Failed to revert changes in database (CRITICAL)");
970             }
971             return status;
972         }
973
974         // Update cluster Configuration cache
975         containerConfigs.put(containerName, entryConf);
976
977         // Notify global and local listeners
978         UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
979         notifyContainerEntryChangeInternal(containerName, nodeConnectors, update, true);
980         // Trigger cluster notification
981         containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(nodeConnectors, update));
982
983         return status;
984     }
985
986     private void notifyContainerChangeInternal(ContainerConfig conf, UpdateType update, boolean notifyLocal) {
987         String containerName = conf.getContainerName();
988         logger.trace("Notifying listeners on {} for container {}", update, containerName);
989         // Back-end World: container name forced to lower case
990         String container = containerName.toLowerCase(Locale.ENGLISH);
991         boolean delete = (update == UpdateType.REMOVED);
992         // Check if a container mode change notification is needed
993         notifyContainerModeChange(delete, notifyLocal);
994         // Notify listeners
995         notifyContainerAwareListeners(container, delete);
996     }
997
998     private void notifyContainerEntryChangeInternal(String containerName, List<NodeConnector> ncList, UpdateType update, boolean notifyLocal) {
999         logger.trace("Notifying listeners on {} for ports {} in container {}", update, ncList, containerName);
1000         // Back-end World: container name forced to lower case
1001         String container = containerName.toLowerCase(Locale.ENGLISH);
1002         for (NodeConnector nodeConnector : ncList) {
1003             // Now signal that the port has been added/removed
1004             synchronized (this.iContainerListener) {
1005                 for (IContainerListener i : this.iContainerListener) {
1006                     i.nodeConnectorUpdated(container, nodeConnector, update);
1007                 }
1008             }
1009             // Check if the Functional Modules need to be notified as well
1010             if (notifyLocal) {
1011                 synchronized (this.iContainerLocalListener) {
1012                     for (IContainerLocalListener i : this.iContainerLocalListener) {
1013                         i.nodeConnectorUpdated(container, nodeConnector, update);
1014                     }
1015                 }
1016             }
1017         }
1018     }
1019
1020     private void notifyCFlowChangeInternal(String containerName, List<ContainerFlowConfig> confList, UpdateType update,
1021             boolean notifyLocal) {
1022         logger.trace("Notifying listeners on {} for flow specs {} in container {}", update, confList, containerName);
1023         // Back-end World: container name forced to lower case
1024         String container = containerName.toLowerCase(Locale.ENGLISH);
1025
1026         for (ContainerFlowConfig conf : confList) {
1027             for (Match match : conf.getMatches()) {
1028                 ContainerFlow cFlow = new ContainerFlow(match);
1029                 synchronized (this.iContainerListener) {
1030                     for (IContainerListener i : this.iContainerListener) {
1031                         i.containerFlowUpdated(container, cFlow, cFlow, update);
1032                     }
1033                 }
1034                 // Check if the Functional Modules need to be notified as well
1035                 if (notifyLocal) {
1036                     synchronized (this.iContainerLocalListener) {
1037                         for (IContainerLocalListener i : this.iContainerLocalListener) {
1038                             i.containerFlowUpdated(container, cFlow, cFlow, update);
1039                         }
1040                     }
1041                 }
1042             }
1043         }
1044     }
1045
1046     private Status addRemoveContainerFlow(String containerName, List<ContainerFlowConfig> cFlowConfList, boolean delete) {
1047         // Construct action message
1048         String action = String.format("Flow spec(s) %s container %s: %s", delete ? "removal from" : "addition to",
1049                 containerName, cFlowConfList);
1050
1051         // Presence check
1052         ContainerConfig containerConfig = this.containerConfigs.get(containerName);
1053         if (containerConfig == null) {
1054             String msg = String.format("Container not found: %s", containerName);
1055             String error = String.format("Failed to apply %s: (%s)", action, msg);
1056             logger.warn(error);
1057             return new Status(StatusCode.NOTFOUND, "Container not present");
1058         }
1059
1060         // Validity check, check for overlaps on current container configuration
1061         Status status = containerConfig.validateContainerFlowModify(cFlowConfList, delete);
1062         if (!status.isSuccess()) {
1063             String msg = status.getDescription();
1064             String error = String.format("Failed to apply %s: (%s)", action, msg);
1065             logger.warn(error);
1066             return new Status(StatusCode.BADREQUEST, msg);
1067         }
1068
1069         // Validate the operation in terms to the port sharing with other containers
1070         for (ContainerFlowConfig conf : cFlowConfList) {
1071             for (Match match : conf.getMatches()) {
1072                 ContainerFlow cFlow = new ContainerFlow(match);
1073                 status = validateContainerFlowAddRemoval(containerName, cFlow, delete);
1074                 if (!status.isSuccess()) {
1075                     String msg = "Validation failed: " + status.getDescription();
1076                     String error = String.format("Failed to apply %s: (%s)", action, msg);
1077                     logger.warn(error);
1078                     return new Status(StatusCode.BADREQUEST, msg);
1079                 }
1080             }
1081         }
1082
1083         // Update Database
1084         status = updateContainerFlow(containerName, cFlowConfList, delete);
1085         if (!status.isSuccess()) {
1086             String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
1087             logger.error(error);
1088             return status;
1089         }
1090
1091         // Update Configuration
1092         status = (delete) ? containerConfig.removeContainerFlows(cFlowConfList) : containerConfig
1093                 .addContainerFlows(cFlowConfList);
1094         if (!status.isSuccess()) {
1095             String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
1096             logger.error(error);
1097             // Revert backend changes
1098             Status statusRevert = updateContainerFlow(containerName, cFlowConfList, !delete);
1099             if (!statusRevert.isSuccess()) {
1100                 // Unlikely
1101                 logger.error("Failed to revert changes in database (CRITICAL)");
1102             }
1103             return status;
1104         }
1105         // Update cluster cache
1106         this.containerConfigs.put(containerName, containerConfig);
1107
1108         // Notify global and local listeners
1109         UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1110         notifyCFlowChangeInternal(containerName, cFlowConfList, update, true);
1111         // Trigger cluster notification
1112         containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(cFlowConfList, update));
1113
1114         return status;
1115     }
1116
1117     private Status addRemoveContainer(ContainerConfig containerConf, boolean delete) {
1118         // Construct action message
1119         String action = String.format("Container %s", delete ? "removal" : "creation");
1120
1121         // Valid configuration check
1122         Status status = null;
1123         String error = (containerConfigs == null) ? String.format("Invalid %s configuration: (null config object)", action)
1124                 : (!(status = containerConf.validate()).isSuccess()) ? String.format("Invalid %s configuration: (%s)",
1125                         action, status.getDescription()) : null;
1126         if (error != null) {
1127             logger.warn(error);
1128             return new Status(StatusCode.BADREQUEST, error);
1129         }
1130
1131         // Configuration presence check
1132         String containerName = containerConf.getContainerName();
1133         if (delete) {
1134             if (!containerConfigs.containsKey(containerName)) {
1135                 String msg = String.format("%s Failed: (Container does not exist: %s)", action, containerName);
1136                 logger.warn(msg);
1137                 return new Status(StatusCode.NOTFOUND, msg);
1138             }
1139         } else {
1140             if (containerConfigs.containsKey(containerName)) {
1141                 String msg = String.format("%s Failed: (Container already exist: %s)", action, containerName);
1142                 logger.warn(msg);
1143                 return new Status(StatusCode.CONFLICT, msg);
1144             }
1145         }
1146
1147         /*
1148          * The proposed container configuration could be a complex one containing
1149          * both ports and flow spec. If so, check if it has shared ports with
1150          * other existing containers. If that is the case verify flow spec isolation
1151          * is in place. No need to check on flow spec validation first. This
1152          * would take care of both
1153          */
1154         if (!delete) {
1155             status = validatePortSharing(containerConf);
1156             if (!status.isSuccess()) {
1157                 error = String.format("%s Failed: (%s)", action, status.getDescription());
1158                 logger.error(error);
1159                 return status;
1160             }
1161         }
1162
1163         // Update Database
1164         status = updateContainerDatabase(containerConf, delete);
1165
1166         // Abort and exit here if back-end database update failed
1167         if (!status.isSuccess()) {
1168             return status;
1169         }
1170
1171         /*
1172          * Update Configuration: This will trigger the notifications on cache
1173          * update callback locally and on the other cluster nodes
1174          */
1175         if (delete) {
1176             this.containerConfigs.remove(containerName);
1177         } else {
1178             this.containerConfigs.put(containerName, containerConf);
1179         }
1180
1181         // Automatically create and populate user and resource groups
1182         updateResourceGroups(containerConf, delete);
1183
1184         // Notify global and local listeners
1185         UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1186         notifyContainerChangeInternal(containerConf, update, true);
1187
1188         // Trigger cluster notification
1189         containerChangeEvents.put(containerName, new ContainerChangeEvent(containerConf, update));
1190
1191         if (update == UpdateType.ADDED) {
1192             if (containerConf.hasFlowSpecs()) {
1193                 List<ContainerFlowConfig> specList = containerConf.getContainerFlowConfigs();
1194                 // Notify global and local listeners about flow spec addition
1195                 notifyCFlowChangeInternal(containerName, specList, update, true);
1196
1197                 // Trigger cluster notification
1198                 containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(specList, update));
1199             }
1200
1201             if (containerConf.hasNodeConnectors()) {
1202                 List<NodeConnector> ncList = containerConf.getPortList();
1203                 // Notify global and local listeners about port(s) addition
1204                 notifyContainerEntryChangeInternal(containerName, ncList, update, true);
1205                 // Trigger cluster notification
1206                 containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(ncList, update));
1207             }
1208         }
1209
1210         if (delete) {
1211             clusterServices.removeContainerCaches(containerName);
1212         }
1213         return status;
1214     }
1215
1216     @Override
1217     public Status addContainer(ContainerConfig containerConf) {
1218         return addRemoveContainer(containerConf, false);
1219     }
1220
1221     @Override
1222     public Status removeContainer(ContainerConfig containerConf) {
1223         return addRemoveContainer(containerConf, true);
1224     }
1225
1226     @Override
1227     public Status removeContainer(String containerName) {
1228         // Construct action message
1229         String action = String.format("Container removal: %s", containerName);
1230
1231         ContainerConfig containerConf = containerConfigs.get(containerName);
1232         if (containerConf == null) {
1233             String msg = String.format("Container not found");
1234             String error = String.format("Failed to apply %s: (%s)", action, msg);
1235             logger.warn(error);
1236             return new Status(StatusCode.NOTFOUND, msg);
1237         }
1238         return addRemoveContainer(containerConf, true);
1239     }
1240
1241     @Override
1242     public Status addContainerEntry(String containerName, List<String> nodeConnectors) {
1243         return addRemoveContainerEntries(containerName, nodeConnectors, false);
1244     }
1245
1246     @Override
1247     public Status removeContainerEntry(String containerName, List<String> nodeConnectors) {
1248         return addRemoveContainerEntries(containerName, nodeConnectors, true);
1249     }
1250
1251     @Override
1252     public Status addContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1253         return addRemoveContainerFlow(containerName, fSpecConf, false);
1254     }
1255
1256     @Override
1257     public Status removeContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1258         return addRemoveContainerFlow(containerName, fSpecConf, true);
1259     }
1260
1261     @Override
1262     public Status removeContainerFlows(String containerName, Set<String> names) {
1263         // Construct action message
1264         String action = String.format("Flow spec(s) removal from container %s: %s", containerName, names);
1265
1266         // Presence check
1267         ContainerConfig sc = containerConfigs.get(containerName);
1268         if (sc == null) {
1269             String msg = String.format("Container not found: %s", containerName);
1270             String error = String.format("Failed to apply %s: (%s)", action, msg);
1271             logger.warn(error);
1272             return new Status(StatusCode.NOTFOUND, msg);
1273         }
1274         List<ContainerFlowConfig> list = sc.getContainerFlowConfigs(names);
1275         if (list.isEmpty() || list.size() != names.size()) {
1276             String msg = String.format("Cannot find all the specified flow specs");
1277             String error = String.format("Failed to apply %s: (%s)", action, msg);
1278             logger.warn(error);
1279             return new Status(StatusCode.BADREQUEST, msg);
1280         }
1281         return addRemoveContainerFlow(containerName, list, true);
1282     }
1283
1284     @Override
1285     public List<ContainerFlowConfig> getContainerFlows(String containerName) {
1286         ContainerConfig sc = containerConfigs.get(containerName);
1287         return (sc == null) ? new ArrayList<ContainerFlowConfig>(0) : sc.getContainerFlowConfigs();
1288     }
1289
1290     @Override
1291     public List<String> getContainerFlowNameList(String containerName) {
1292         ContainerConfig sc = containerConfigs.get(containerName);
1293         return (sc == null) ? new ArrayList<String>(0) : sc.getContainerFlowConfigsNames();
1294     }
1295
1296     @Override
1297     public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
1298         // Perform the class deserialization locally, from inside the package
1299         // where the class is defined
1300         return ois.readObject();
1301     }
1302
1303     private void loadContainerConfig() {
1304         for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, CONTAINERS_FILE_NAME)) {
1305             addContainer((ContainerConfig) conf);
1306         }
1307     }
1308
1309     public void _psc(CommandInterpreter ci) {
1310         for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1311             ContainerConfig sc = entry.getValue();
1312             ci.println(String.format("%s: %s", sc.getContainerName(), sc.toString()));
1313         }
1314         ci.println("Total number of containers: " + containerConfigs.entrySet().size());
1315     }
1316
1317     public void _pfc(CommandInterpreter ci) {
1318         for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1319             ContainerConfig sc = entry.getValue();
1320             ci.println(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
1321         }
1322     }
1323
1324     public void _psd(CommandInterpreter ci) {
1325         for (String containerName : containerData.keySet()) {
1326             ContainerData sd = containerData.get(containerName);
1327             for (Node sid : sd.getSwPorts().keySet()) {
1328                 Set<NodeConnector> s = sd.getSwPorts().get(sid);
1329                 ci.println("\t" + sid + " : " + s);
1330             }
1331
1332             for (ContainerFlow s : sd.getContainerFlowSpecs()) {
1333                 ci.println("\t" + s.toString());
1334             }
1335         }
1336     }
1337
1338     public void _psp(CommandInterpreter ci) {
1339         for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
1340             ci.println(nodeConnectorToContainers.get(sp));
1341         }
1342     }
1343
1344     public void _psm(CommandInterpreter ci) {
1345         for (Node sp : nodeToContainers.keySet()) {
1346             ci.println(nodeToContainers.get(sp));
1347         }
1348     }
1349
1350     public void _addContainer(CommandInterpreter ci) {
1351         String containerName = ci.nextArgument();
1352         if (containerName == null) {
1353             ci.print("Container Name not specified");
1354             return;
1355         }
1356         String staticVlan = ci.nextArgument();
1357         ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
1358         ci.println(this.addRemoveContainer(containerConfig, false));
1359     }
1360
1361     public void _createContainer(CommandInterpreter ci) {
1362         String containerName = ci.nextArgument();
1363         if (containerName == null) {
1364             ci.print("Container Name not specified");
1365             return;
1366         }
1367         String staticVlan = ci.nextArgument();
1368         if (staticVlan == null) {
1369             ci.print("Static Vlan not specified");
1370             return;
1371         }
1372         List<String> ports = new ArrayList<String>();
1373         for (long l = 1L; l < 10L; l++) {
1374             ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
1375         }
1376         List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
1377         cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
1378         ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
1379         ci.println(this.addRemoveContainer(containerConfig, false));
1380     }
1381
1382     public void _removeContainer(CommandInterpreter ci) {
1383         String containerName = ci.nextArgument();
1384         if (containerName == null) {
1385             ci.print("Container Name not specified");
1386             return;
1387         }
1388         ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
1389         ci.println(this.addRemoveContainer(containerConfig, true));
1390     }
1391
1392     public void _addContainerEntry(CommandInterpreter ci) {
1393         String containerName = ci.nextArgument();
1394         if (containerName == null) {
1395             ci.print("Container Name not specified");
1396             return;
1397         }
1398         String nodeId = ci.nextArgument();
1399         if (nodeId == null) {
1400             ci.print("Node Id not specified");
1401             return;
1402         }
1403         String portId = ci.nextArgument();
1404         if (portId == null) {
1405             ci.print("Port not specified");
1406             return;
1407         }
1408         Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1409         Short port = Short.valueOf(portId);
1410         NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1411         List<String> portList = new ArrayList<String>(1);
1412         portList.add(nc.toString());
1413         ci.println(this.addRemoveContainerEntries(containerName, portList, false));
1414     }
1415
1416     public void _removeContainerEntry(CommandInterpreter ci) {
1417         String containerName = ci.nextArgument();
1418         if (containerName == null) {
1419             ci.print("Container Name not specified");
1420             return;
1421         }
1422         String nodeId = ci.nextArgument();
1423         if (nodeId == null) {
1424             ci.print("Node Id not specified");
1425             return;
1426         }
1427         String portId = ci.nextArgument();
1428         if (portId == null) {
1429             ci.print("Port not specified");
1430             return;
1431         }
1432         Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1433         Short port = Short.valueOf(portId);
1434         NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1435         List<String> portList = new ArrayList<String>(1);
1436         portList.add(nc.toString());
1437         ci.println(this.addRemoveContainerEntries(containerName, portList, true));
1438     }
1439
1440     private ContainerFlowConfig createSampleContainerFlowConfig(String cflowName, boolean boolUnidirectional) {
1441         ContainerFlowConfig cfg = new ContainerFlowConfig(cflowName, "9.9.1.0/24", "19.9.1.2", "TCP", "1234", "25");
1442         return cfg;
1443     }
1444
1445     public void _addContainerFlow(CommandInterpreter ci) {
1446         String containerName = ci.nextArgument();
1447         if (containerName == null) {
1448             ci.print("Container Name not specified");
1449             return;
1450         }
1451         String cflowName = ci.nextArgument();
1452         if (cflowName == null) {
1453             ci.print("cflowName not specified");
1454             return;
1455         }
1456         String unidirectional = ci.nextArgument();
1457         boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
1458         List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
1459         list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
1460         ci.println(this.addRemoveContainerFlow(containerName, list, false));
1461     }
1462
1463     public void _removeContainerFlow(CommandInterpreter ci) {
1464         String containerName = ci.nextArgument();
1465         if (containerName == null) {
1466             ci.print("Container Name not specified");
1467             return;
1468         }
1469         String cflowName = ci.nextArgument();
1470         if (cflowName == null) {
1471             ci.print("cflowName not specified");
1472             return;
1473         }
1474         Set<String> set = new HashSet<String>(1);
1475         set.add(cflowName);
1476         ci.println(this.removeContainerFlows(containerName, set));
1477     }
1478
1479     @Override
1480     public String getHelp() {
1481         StringBuffer help = new StringBuffer();
1482         help.append("---ContainerManager Testing---\n");
1483         help.append("\tpsc        - Print ContainerConfigs\n");
1484         help.append("\tpfc        - Print FlowSpecConfigs\n");
1485         help.append("\tpsd        - Print ContainerData\n");
1486         help.append("\tpsp        - Print nodeConnectorToContainers\n");
1487         help.append("\tpsm        - Print nodeToContainers\n");
1488         help.append("\t addContainer <containerName> <staticVlan> \n");
1489         help.append("\t removeContainer <containerName> \n");
1490         help.append("\t addContainerEntry <containerName> <nodeId> <port> \n");
1491         help.append("\t removeContainerEntry <containerName> <nodeId> <port> \n");
1492         help.append("\t addContainerFlow <containerName> <cflowName> <unidirectional true/false>\n");
1493         help.append("\t removeContainerFlow <containerName> <cflowName> \n");
1494         return help.toString();
1495     }
1496
1497     @Override
1498     public boolean doesContainerExist(String containerName) {
1499         // Test for default container
1500         if (GlobalConstants.DEFAULT.toString().equalsIgnoreCase(containerName)) {
1501             return true;
1502         }
1503         // Test for non-default one
1504         return (getContainerByName(containerName) != null);
1505     }
1506
1507     @Override
1508     public ContainerData getContainerData(String containerName) {
1509         return (getContainerByName(containerName));
1510     }
1511
1512     @Override
1513     public Status saveConfiguration() {
1514         return saveContainerConfig();
1515     }
1516
1517     public void _containermgrGetRoles(CommandInterpreter ci) {
1518         ci.println("Configured roles for Container Mgr:");
1519         List<String> list = this.getRoles();
1520         for (String role : list) {
1521             ci.println(role + "\t" + roles.get(role));
1522         }
1523     }
1524
1525     public void _containermgrGetAuthorizedGroups(CommandInterpreter ci) {
1526         String roleName = ci.nextArgument();
1527         if (roleName == null || roleName.trim().isEmpty()) {
1528             ci.println("Invalid argument");
1529             ci.println("mmGetAuthorizedGroups <role_name>");
1530             return;
1531         }
1532         ci.println("Resource Groups associated to role " + roleName + ":");
1533         List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
1534         for (ResourceGroup group : list) {
1535             ci.println(group.toString());
1536         }
1537     }
1538
1539     public void _containermgrGetAuthorizedResources(CommandInterpreter ci) {
1540         String roleName = ci.nextArgument();
1541         if (roleName == null || roleName.trim().isEmpty()) {
1542             ci.println("Invalid argument");
1543             ci.println("mmGetAuthorizedResources <role_name>");
1544             return;
1545         }
1546         ci.println("Resource associated to role " + roleName + ":");
1547         List<Resource> list = this.getAuthorizedResources(roleName);
1548         for (Resource resource : list) {
1549             ci.println(resource.toString());
1550         }
1551     }
1552
1553     public void _containermgrGetResourcesForGroup(CommandInterpreter ci) {
1554         String groupName = ci.nextArgument();
1555         if (groupName == null || groupName.trim().isEmpty()) {
1556             ci.println("Invalid argument");
1557             ci.println("containermgrResourcesForGroup <group_name>");
1558             return;
1559         }
1560         ci.println("Group " + groupName + " contains the following resources:");
1561         List<Object> resources = this.getResources(groupName);
1562         for (Object resource : resources) {
1563             ci.println(resource.toString());
1564         }
1565     }
1566
1567     public void _containermgrGetUserLevel(CommandInterpreter ci) {
1568         String userName = ci.nextArgument();
1569         if (userName == null || userName.trim().isEmpty()) {
1570             ci.println("Invalid argument");
1571             ci.println("containermgrGetUserLevel <user_name>");
1572             return;
1573         }
1574         ci.println("User " + userName + " has level: " + this.getUserLevel(userName));
1575     }
1576
1577     public void _containermgrGetUserResources(CommandInterpreter ci) {
1578         String userName = ci.nextArgument();
1579         if (userName == null || userName.trim().isEmpty()) {
1580             ci.println("Invalid argument");
1581             ci.println("containermgrGetUserResources <user_name>");
1582             return;
1583         }
1584         ci.println("User " + userName + " owns the following resources: ");
1585         Set<Resource> resources = this.getAllResourcesforUser(userName);
1586         for (Resource resource : resources) {
1587             ci.println(resource.toString());
1588         }
1589     }
1590
1591     /*
1592      * For scalability testing where as of now controller gui is unresponsive
1593      * providing here an osgi hook to trigger the save config so that DT do not
1594      * have to reaply the scalable configuration each time they restart the
1595      * controller
1596      */
1597     // TODO: remove when no longer needed
1598     public void _saveConfig(CommandInterpreter ci) {
1599         Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
1600
1601         IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
1602                 IConfigurationService.class, this);
1603         if (configService != null) {
1604             status = configService.saveConfigurations();
1605         }
1606         ci.println(status.toString());
1607     }
1608
1609     @Override
1610     public List<String> getContainerNames() {
1611         return getContainerNameList();
1612     }
1613
1614     @Override
1615     public boolean hasNonDefaultContainer() {
1616         return !containerConfigs.keySet().isEmpty();
1617     }
1618
1619     @Override
1620     public boolean inContainerMode() {
1621         return this.containerConfigs.size() > 0;
1622     }
1623
1624     public List<String> psc() {
1625         List<String> result = new ArrayList<String>();
1626         for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1627             ContainerConfig sc = entry.getValue();
1628             result.add(String.format("%s: %s", sc.getContainerName(), sc.toString()));
1629         }
1630         result.add("Total number of containers: " + containerConfigs.entrySet().size());
1631         return result;
1632     }
1633
1634     public List<String> pfc() {
1635         List<String> result = new ArrayList<String>();
1636         for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1637             ContainerConfig sc = entry.getValue();
1638             result.add(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
1639         }
1640         return result;
1641     }
1642
1643     public List<String> psd() {
1644         List<String> result = new ArrayList<String>();
1645         for (String containerName : containerData.keySet()) {
1646             ContainerData sd = containerData.get(containerName);
1647             for (Node sid : sd.getSwPorts().keySet()) {
1648                 Set<NodeConnector> s = sd.getSwPorts().get(sid);
1649                 result.add("\t" + sid + " : " + s);
1650             }
1651
1652             for (ContainerFlow s : sd.getContainerFlowSpecs()) {
1653                 result.add("\t" + s.toString());
1654             }
1655         }
1656         return result;
1657     }
1658
1659     public List<String> psp() {
1660         List<String> result = new ArrayList<String>();
1661         for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
1662             result.add(nodeConnectorToContainers.get(sp).toString());
1663         }
1664         return result;
1665     }
1666
1667     public List<String> psm() {
1668         List<String> result = new ArrayList<String>();
1669         for (Node sp : nodeToContainers.keySet()) {
1670             result.add(nodeToContainers.get(sp).toString());
1671         }
1672         return result;
1673     }
1674
1675     public List<String> addContainer(String arg1, String arg2) {
1676         List<String> result = new ArrayList<String>();
1677         String containerName = arg1;
1678         if (containerName == null) {
1679             result.add("Container Name not specified");
1680             return result;
1681         }
1682         String staticVlan = arg2;
1683         ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
1684         result.add((this.addRemoveContainer(containerConfig, false)).toString());
1685         return result;
1686     }
1687
1688     public List<String> createContainer(String arg1, String arg2) {
1689         List<String> result = new ArrayList<String>();
1690         String containerName = arg1;
1691         if (containerName == null) {
1692             result.add("Container Name not specified");
1693             return result;
1694         }
1695         String staticVlan = arg2;
1696         if (staticVlan == null) {
1697             result.add("Static Vlan not specified");
1698             return result;
1699         }
1700         List<String> ports = new ArrayList<String>();
1701         for (long l = 1L; l < 10L; l++) {
1702             ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
1703         }
1704         List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
1705         cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
1706         ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
1707         result.add((this.addRemoveContainer(containerConfig, false)).toString());
1708         return result;
1709     }
1710
1711     public List<String> removeContainerShell(String arg1) {
1712         List<String> result = new ArrayList<String>();
1713         String containerName = arg1;
1714         if (containerName == null) {
1715             result.add("Container Name not specified");
1716             return result;
1717         }
1718         ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
1719         result.add((this.addRemoveContainer(containerConfig, true)).toString());
1720         return result;
1721     }
1722
1723     public List<String> addContainerEntry(String arg1, String arg2, String arg3) {
1724         List<String> result = new ArrayList<String>();
1725         String containerName = arg1;
1726         if (containerName == null) {
1727             result.add("Container Name not specified");
1728             return result;
1729         }
1730         String nodeId = arg2;
1731         if (nodeId == null) {
1732             result.add("Node Id not specified");
1733             return result;
1734         }
1735         String portId = arg3;
1736         if (portId == null) {
1737             result.add("Port not specified");
1738             return result;
1739         }
1740         Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1741         Short port = Short.valueOf(portId);
1742         NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1743         List<String> portList = new ArrayList<String>(1);
1744         portList.add(nc.toString());
1745         result.add((this.addRemoveContainerEntries(containerName, portList, false)).toString());
1746         return result;
1747     }
1748
1749     public List<String> removeContainerEntry(String arg1, String arg2, String arg3) {
1750         List<String> result = new ArrayList<String>();
1751         String containerName = arg1;
1752         if (containerName == null) {
1753             result.add("Container Name not specified");
1754             return result;
1755         }
1756         String nodeId = arg2;
1757         if (nodeId == null) {
1758             result.add("Node Id not specified");
1759             return result;
1760         }
1761         String portId = arg3;
1762         if (portId == null) {
1763             result.add("Port not specified");
1764             return result;
1765         }
1766         Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1767         Short port = Short.valueOf(portId);
1768         NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1769         List<String> portList = new ArrayList<String>(1);
1770         portList.add(nc.toString());
1771         result.add((this.addRemoveContainerEntries(containerName, portList, true)).toString());
1772         return result;
1773     }
1774     public List<String> addContainerFlow(String arg1, String arg2, String arg3) {
1775         List<String> result = new ArrayList<String>();
1776         String containerName = arg1;
1777         if (containerName == null) {
1778             result.add("Container Name not specified");
1779             return result;
1780         }
1781         String cflowName = arg2;
1782         if (cflowName == null) {
1783             result.add("cflowName not specified");
1784             return result;
1785         }
1786         String unidirectional = arg3;
1787         boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
1788         List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
1789         list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
1790         result.add((this.addRemoveContainerFlow(containerName, list, false)).toString());
1791         return result;
1792     }
1793
1794     public List<String> removeContainerFlow(String arg1, String arg2) {
1795         List<String> result = new ArrayList<String>();
1796         String containerName = arg1;
1797         if (containerName == null) {
1798             result.add("Container Name not specified");
1799             return result;
1800         }
1801         String cflowName = arg2;
1802         if (cflowName == null) {
1803             result.add("cflowName not specified");
1804             return result;
1805         }
1806         Set<String> set = new HashSet<String>(1);
1807         set.add(cflowName);
1808         result.add((this.removeContainerFlows(containerName, set)).toString());
1809         return result;
1810     }
1811
1812     public List<String> containermgrGetRoles() {
1813         List<String> result = new ArrayList<String>();
1814         result.add("Configured roles for Container Mgr:");
1815         List<String> list = this.getRoles();
1816         for (String role : list) {
1817             result.add(role + "\t" + roles.get(role));
1818         }
1819         return result;
1820     }
1821
1822     public List<String> containermgrGetAuthorizedGroups(String arg1) {
1823         List<String> result = new ArrayList<String>();
1824         String roleName = arg1;
1825         if (roleName == null || roleName.trim().isEmpty()) {
1826             result.add("Invalid argument");
1827             result.add("mmGetAuthorizedGroups <role_name>");
1828             return result;
1829         }
1830         result.add("Resource Groups associated to role " + roleName + ":");
1831         List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
1832         for (ResourceGroup group : list) {
1833             result.add(group.toString());
1834         }
1835         return result;
1836     }
1837     public List<String> containermgrGetAuthorizedResources(String arg1) {
1838         List<String> result = new ArrayList<String>();
1839         String roleName = arg1;
1840         if (roleName == null || roleName.trim().isEmpty()) {
1841             result.add("Invalid argument");
1842             result.add("mmGetAuthorizedResources <role_name>");
1843             return result;
1844         }
1845         result.add("Resource associated to role " + roleName + ":");
1846         List<Resource> list = this.getAuthorizedResources(roleName);
1847         for (Resource resource : list) {
1848             result.add(resource.toString());
1849         }
1850         return result;
1851     }
1852     public List<String> containermgrGetResourcesForGroup(String arg1) {
1853         List<String> result = new ArrayList<String>();
1854         String groupName = arg1;
1855         if (groupName == null || groupName.trim().isEmpty()) {
1856             result.add("Invalid argument");
1857             result.add("containermgrResourcesForGroup <group_name>");
1858             return result;
1859         }
1860         result.add("Group " + groupName + " contains the following resources:");
1861         List<Object> resources = this.getResources(groupName);
1862         for (Object resource : resources) {
1863             result.add(resource.toString());
1864         }
1865         return result;
1866     }
1867     public List<String> containermgrGetUserLevel(String arg1) {
1868         List<String> result = new ArrayList<String>();
1869         String userName = arg1;
1870         if (userName == null || userName.trim().isEmpty()) {
1871             result.add("Invalid argument");
1872             result.add("containermgrGetUserLevel <user_name>");
1873             return result;
1874         }
1875         result.add("User " + userName + " has level: " + this.getUserLevel(userName));
1876         return result;
1877     }
1878     public List<String> containermgrGetUserResources(String arg1) {
1879         List<String> result = new ArrayList<String>();
1880         String userName = arg1;
1881         if (userName == null || userName.trim().isEmpty()) {
1882             result.add("Invalid argument");
1883             result.add("containermgrGetUserResources <user_name>");
1884             return result;
1885         }
1886         result.add("User " + userName + " owns the following resources: ");
1887         Set<Resource> resources = this.getAllResourcesforUser(userName);
1888         for (Resource resource : resources) {
1889             result.add(resource.toString());
1890         }
1891         return result;
1892     }
1893     public List<String> saveConfig() {
1894         List<String> result = new ArrayList<String>();
1895         Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
1896
1897         IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
1898                 IConfigurationService.class, this);
1899         if (configService != null) {
1900             status = configService.saveConfigurations();
1901         }
1902         result.add(status.toString());
1903         return result;
1904     }
1905 }