3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
10 package org.opendaylight.controller.containermanager.internal;
13 import java.io.FileNotFoundException;
14 import java.io.IOException;
15 import java.io.ObjectInputStream;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.EnumSet;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Locale;
25 import java.util.Map.Entry;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.CopyOnWriteArrayList;
31 import org.eclipse.osgi.framework.console.CommandInterpreter;
32 import org.eclipse.osgi.framework.console.CommandProvider;
33 import org.opendaylight.controller.appauth.authorization.Authorization;
34 import org.opendaylight.controller.clustering.services.CacheConfigException;
35 import org.opendaylight.controller.clustering.services.CacheExistException;
36 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
37 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
38 import org.opendaylight.controller.clustering.services.IClusterServices;
39 import org.opendaylight.controller.configuration.IConfigurationAware;
40 import org.opendaylight.controller.configuration.IConfigurationService;
41 import org.opendaylight.controller.containermanager.ContainerChangeEvent;
42 import org.opendaylight.controller.containermanager.ContainerConfig;
43 import org.opendaylight.controller.containermanager.ContainerData;
44 import org.opendaylight.controller.containermanager.ContainerFlowChangeEvent;
45 import org.opendaylight.controller.containermanager.ContainerFlowConfig;
46 import org.opendaylight.controller.containermanager.IContainerAuthorization;
47 import org.opendaylight.controller.containermanager.IContainerManager;
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.ObjectReader;
67 import org.opendaylight.controller.sal.utils.ObjectWriter;
68 import org.opendaylight.controller.sal.utils.ServiceHelper;
69 import org.opendaylight.controller.sal.utils.Status;
70 import org.opendaylight.controller.sal.utils.StatusCode;
71 import org.opendaylight.controller.topologymanager.ITopologyManager;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
75 public class ContainerManager extends Authorization<String> implements IContainerManager, IObjectReader,
76 CommandProvider, ICacheUpdateAware<String, Object>, IContainerInternal, IContainerAuthorization,
78 private static final Logger logger = LoggerFactory.getLogger(ContainerManager.class);
79 private static String ROOT = GlobalConstants.STARTUPHOME.toString();
80 private static String containersFileName = ROOT + "containers.conf";
81 private static final String allContainersGroup = "allContainers";
82 private IClusterGlobalServices clusterServices;
84 * Collection containing the configuration objects. This is configuration
85 * world: container names (also the map key) are maintained as they were
86 * configured by user, same case
88 private ConcurrentMap<String, ContainerConfig> containerConfigs;
89 private ConcurrentMap<String, ContainerData> containerData;
90 private ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>> nodeConnectorToContainers;
91 private ConcurrentMap<Node, Set<String>> nodeToContainers;
92 private ConcurrentMap<String, Object> containerChangeEvents;
93 private final Set<IContainerAware> iContainerAware = Collections.synchronizedSet(new HashSet<IContainerAware>());
94 private final Set<IContainerListener> iContainerListener = Collections
95 .synchronizedSet(new HashSet<IContainerListener>());
96 private final Set<IContainerLocalListener> iContainerLocalListener = Collections
97 .synchronizedSet(new HashSet<IContainerLocalListener>());
99 void setIContainerListener(IContainerListener s) {
100 if (this.iContainerListener != null) {
101 this.iContainerListener.add(s);
103 * At boot with startup, containers are created before listeners have
104 * joined. Replaying here the first container creation notification for
105 * the joining listener when containers are already present. Also
106 * replaying all the node connectors and container flows additions
107 * to the existing containers.
109 if (!this.containerData.isEmpty()) {
110 s.containerModeUpdated(UpdateType.ADDED);
112 for (ConcurrentMap.Entry<NodeConnector, CopyOnWriteArrayList<String>> entry : nodeConnectorToContainers
114 NodeConnector port = entry.getKey();
115 for (String container : entry.getValue()) {
116 s.nodeConnectorUpdated(container, port, UpdateType.ADDED);
119 for (Map.Entry<String, ContainerData> container : containerData.entrySet()) {
120 for (ContainerFlow cFlow : container.getValue().getContainerFlowSpecs()) {
121 s.containerFlowUpdated(container.getKey(), cFlow, cFlow, UpdateType.ADDED);
127 void unsetIContainerListener(IContainerListener s) {
128 if (this.iContainerListener != null) {
129 this.iContainerListener.remove(s);
133 void setIContainerLocalListener(IContainerLocalListener s) {
134 if (this.iContainerLocalListener != null) {
135 this.iContainerLocalListener.add(s);
139 void unsetIContainerLocalListener(IContainerLocalListener s) {
140 if (this.iContainerLocalListener != null) {
141 this.iContainerLocalListener.remove(s);
145 public void setIContainerAware(IContainerAware iContainerAware) {
146 if (!this.iContainerAware.contains(iContainerAware)) {
147 this.iContainerAware.add(iContainerAware);
148 // Now call the container creation for all the known containers so far
149 for (String container : getContainerNameList()) {
150 iContainerAware.containerCreate(container.toLowerCase(Locale.ENGLISH));
155 public void unsetIContainerAware(IContainerAware iContainerAware) {
156 this.iContainerAware.remove(iContainerAware);
157 // There is no need to do cleanup of the component when
158 // unregister because it will be taken care by the Container
162 public void setClusterServices(IClusterGlobalServices i) {
163 this.clusterServices = i;
164 logger.debug("IClusterServices set");
167 public void unsetClusterServices(IClusterGlobalServices i) {
168 if (this.clusterServices == i) {
169 this.clusterServices = null;
170 logger.debug("IClusterServices Unset");
174 private void allocateCaches() {
175 logger.debug("Container Manager allocating caches");
177 if (clusterServices == null) {
178 logger.warn("un-initialized Cluster Services, can't allocate caches");
182 clusterServices.createCache("containermgr.containerConfigs", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
184 clusterServices.createCache("containermgr.event.containerChange",
185 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
187 clusterServices.createCache("containermgr.containerData", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
189 clusterServices.createCache("containermgr.nodeConnectorToContainers",
190 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
192 clusterServices.createCache("containermgr.nodeToContainers", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
194 clusterServices.createCache("containermgr.containerGroups", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
196 clusterServices.createCache("containermgr.containerAuthorizations",
197 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
199 clusterServices.createCache("containermgr.roles", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
200 } catch (CacheConfigException cce) {
201 logger.error("Cache configuration invalid - check cache mode");
202 } catch (CacheExistException ce) {
203 logger.error("Cache already exits - destroy and recreate if needed");
207 @SuppressWarnings({ "unchecked" })
208 private void retrieveCaches() {
209 logger.debug("Container Manager retrieving caches");
211 if (clusterServices == null) {
212 logger.warn("un-initialized Cluster Services, can't retrieve caches");
216 containerConfigs = (ConcurrentMap<String, ContainerConfig>) clusterServices.getCache("containermgr.containerConfigs");
218 containerChangeEvents = (ConcurrentMap<String, Object>) clusterServices.getCache("containermgr.event.containerChange");
220 containerData = (ConcurrentMap<String, ContainerData>) clusterServices.getCache("containermgr.containerData");
222 nodeConnectorToContainers = (ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>>) clusterServices
223 .getCache("containermgr.nodeConnectorToContainers");
225 nodeToContainers = (ConcurrentMap<Node, Set<String>>) clusterServices.getCache("containermgr.nodeToContainers");
227 resourceGroups = (ConcurrentMap<String, Set<String>>) clusterServices.getCache("containermgr.containerGroups");
229 groupsAuthorizations = (ConcurrentMap<String, Set<ResourceGroup>>) clusterServices
230 .getCache("containermgr.containerAuthorizations");
232 roles = (ConcurrentMap<String, AppRoleLevel>) clusterServices.getCache("containermgr.roles");
234 if (inContainerMode()) {
235 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
236 // Notify global and local listeners about the mode change
237 notifyContainerChangeInternal(entry.getValue(), UpdateType.ADDED, true);
243 public void entryCreated(String containerName, String cacheName, boolean originLocal) {
248 public void entryUpdated(String key, Object value, String cacheName, boolean originLocal) {
250 * This is were container manager replays a configuration event that was
251 * notified by its peer from a cluster node where the configuration
252 * happened. Only the global listeners, the cluster unaware classes,
253 * (mainly the shim classes in the sdn protocol plugins) need to receive
254 * these notifications on this cluster node. The cluster aware classes,
255 * like the functional modules which reacts on these events, must _not_
256 * be notified to avoid parallel computation in the cluster.
259 if (value instanceof NodeConnectorsChangeEvent) {
260 NodeConnectorsChangeEvent event = (NodeConnectorsChangeEvent) value;
261 List<NodeConnector> ncList = event.getNodeConnectors();
262 notifyContainerEntryChangeInternal(key, ncList, event.getUpdateType(), false);
263 } else if (value instanceof ContainerFlowChangeEvent) {
264 ContainerFlowChangeEvent event = (ContainerFlowChangeEvent) value;
265 notifyCFlowChangeInternal(key, event.getConfigList(), event.getUpdateType(), false);
266 } else if (value instanceof ContainerChangeEvent) {
267 ContainerChangeEvent event = (ContainerChangeEvent) value;
268 notifyContainerChangeInternal(event.getConfig(), event.getUpdateType(), false);
274 public void entryDeleted(String containerName, String cacheName, boolean originLocal) {
277 public ContainerManager() {
284 public void start() {
285 // Get caches from cluster manager
289 // Allocates default groups and association to default roles
290 createDefaultAuthorizationGroups();
292 // Read startup configuration and create local database
293 loadConfigurations();
296 public void destroy() {
297 // Clear local states
298 this.iContainerAware.clear();
299 this.iContainerListener.clear();
300 this.iContainerLocalListener.clear();
304 * Adds/Remove the list of flow specs to/from the specified container. This
305 * function is supposed to be called after all the validation checks have
306 * already been run on the proposed configuration.
308 private Status updateContainerFlow(String containerName, List<ContainerFlowConfig> confList, boolean delete) {
309 ContainerData container = getContainerByName(containerName);
310 if (container == null) {
311 return new Status(StatusCode.GONE, "Container not present");
314 for (ContainerFlowConfig conf : confList) {
315 // Validation was fine. Modify the database now.
316 for (Match match : conf.getMatches()) {
317 ContainerFlow cFlow = new ContainerFlow(match);
319 logger.trace("Removing Flow Spec {} from Container {}", conf.getName(), containerName);
320 container.deleteFlowSpec(cFlow);
322 logger.trace("Adding Flow Spec {} to Container {}", conf.getName(), containerName);
323 container.addFlowSpec(cFlow);
327 putContainerDataByName(containerName, container);
330 return new Status(StatusCode.SUCCESS);
334 * Adds/Remove this container to/from the Container database, no updates are going
335 * to be generated here other that the destroying and creation of the container.
336 * This function is supposed to be called after all the validation checks
337 * have already been run on the configuration object
339 private Status updateContainerDatabase(ContainerConfig containerConf, boolean delete) {
341 * Back-end world here, container names are all stored in lower case
343 String containerName = containerConf.getContainerName();
344 ContainerData container = getContainerByName(containerName);
345 if (delete && container == null) {
346 return new Status(StatusCode.NOTFOUND, "Container is not present");
348 if (!delete && container != null) {
349 // A container with the same (lower case) name already exists
350 return new Status(StatusCode.CONFLICT, "A container with the same name already exists");
353 logger.debug("Removing container {}", containerName);
354 removeNodeToContainersMapping(container);
355 removeNodeConnectorToContainersMapping(container);
356 removeContainerDataByName(containerName);
358 logger.debug("Adding container {}", containerName);
359 container = new ContainerData(containerConf);
360 putContainerDataByName(containerName, container);
362 // If flow specs are specified, add them
363 if (containerConf.hasFlowSpecs()) {
364 updateContainerFlow(containerName, containerConf.getContainerFlowConfigs(), delete);
367 // If ports are specified, add them
368 if (!containerConf.getPortList().isEmpty()) {
369 updateContainerEntryDatabase(containerName, containerConf.getPortList(), delete);
372 return new Status(StatusCode.SUCCESS);
375 private void removeNodeConnectorToContainersMapping(ContainerData container) {
376 Iterator<Entry<NodeConnector, CopyOnWriteArrayList<String>>> it = nodeConnectorToContainers.entrySet().iterator();
377 String containerName = container.getContainerName();
378 for (; it.hasNext();) {
379 Entry<NodeConnector, CopyOnWriteArrayList<String>> entry = it.next();
380 final NodeConnector nc = entry.getKey();
381 final CopyOnWriteArrayList<String> slist = entry.getValue();
382 for (final String sdata : slist) {
383 if (sdata.equalsIgnoreCase(containerName)) {
384 logger.debug("Removing NodeConnector->Containers mapping, nodeConnector: {}", nc);
385 slist.remove(containerName);
386 if (slist.isEmpty()) {
387 nodeConnectorToContainers.remove(nc);
389 nodeConnectorToContainers.put(nc, slist);
397 private void removeNodeToContainersMapping(ContainerData container) {
398 for (Entry<Node, Set<String>> entry : nodeToContainers.entrySet()) {
399 Node node = entry.getKey();
400 for (String sdata : entry.getValue()) {
401 if (sdata.equals(container.getContainerName())) {
402 logger.debug("Removing Node->Containers mapping, node {} container {}", node, sdata);
403 Set<String> value = nodeToContainers.get(node);
405 nodeToContainers.put(node, value);
413 * Adds/Remove container data to/from the container. This function is supposed to be
414 * called after all the validation checks have already been run on the
415 * configuration object
417 private Status updateContainerEntryDatabase(String containerName, List<NodeConnector> nodeConnectors, boolean delete) {
418 ContainerData container = getContainerByName(containerName);
420 if (container == null) {
421 return new Status(StatusCode.NOTFOUND, "Container Not Found");
424 // Check changes in the portlist
425 for (NodeConnector port : nodeConnectors) {
426 Node node = port.getNode();
428 container.removePortFromSwitch(port);
429 putContainerDataByName(containerName, container);
431 /* remove <sp> - container mapping */
432 if (nodeConnectorToContainers.containsKey(port)) {
433 nodeConnectorToContainers.remove(port);
436 * If no more ports in the switch, remove switch from container
437 * Generate switchRemoved Event
439 if (container.portListEmpty(node)) {
440 logger.debug("Port List empty for switch {}", node);
441 putContainerDataByName(containerName, container);
442 // remove node->containers mapping
443 Set<String> slist = nodeToContainers.get(node);
445 logger.debug("Removing container from switch-container list. node{}, container{}", node, containerName);
446 slist.remove(container.getContainerName());
447 nodeToContainers.put(node, slist);
448 if (slist.isEmpty()) {
449 logger.debug("Container list empty for switch {}. removing switch-container mapping", node);
450 nodeToContainers.remove(node);
455 if (container.isSwitchInContainer(node) == false) {
456 Set<String> value = nodeToContainers.get(node);
457 // Add node->containers mapping
459 value = new HashSet<String>();
460 logger.debug("Creating new Container Set for switch {}", node);
462 value.add(container.getContainerName());
463 nodeToContainers.put(node, value);
465 container.addPortToSwitch(port);
466 putContainerDataByName(containerName, container);
468 // added nc->containers mapping
469 CopyOnWriteArrayList<String> slist = nodeConnectorToContainers.get(port);
471 slist = new CopyOnWriteArrayList<String>();
473 slist.add(container.getContainerName());
474 nodeConnectorToContainers.put(port, slist);
477 return new Status(StatusCode.SUCCESS);
480 private Status validateContainerFlowAddRemoval(String containerName, ContainerFlow cFlow, boolean delete) {
482 * It used to be the comment below: ~~~~~~~~~~~~~~~~~~~~ If Link Sharing
483 * at Host facing interfaces, then disallow last ContainerFlow removal
484 * ~~~~~~~~~~~~~~~~~~~~ But the interface being host facing is a
485 * condition that can change at runtime and so the final effect will be
486 * unreliable. So now we will always allow the container flow removal,
487 * if this is a link host facing and is shared by many that will cause
488 * issues but that validation should be done not at the configuration
489 * but in the UI/northbound side.
491 ContainerData container = this.getContainerByName(containerName);
492 if (container == null) {
493 String error = String.format("Cannot validate flow specs for container %s: (Container does not exist)", containerName);
495 return new Status(StatusCode.BADREQUEST, error);
499 Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
500 // Go through all the installed containers
501 for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
502 if (containerName.equalsIgnoreCase(entry.getKey())) {
505 // Derive the common ports
506 Set<NodeConnector> commonPorts = entry.getValue().getNodeConnectors();
507 commonPorts.retainAll(thisContainerPorts);
508 if (commonPorts.isEmpty()) {
512 // Check if this operation would remove the only flow spec
513 // assigned to this container
514 if (container.getFlowSpecCount() == 1) {
515 if (!container.hasStaticVlanAssigned()) {
516 // Ports are shared and static vlan is not present: this
518 // regardless the shared ports are host facing or
520 return new Status(StatusCode.BADREQUEST, "Container shares port with another container: "
521 + "The only one flow spec assigned to this container cannot be removed,"
522 + "because this container is not assigned any static vlan");
525 // Check on host facing port
526 ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(
527 ITopologyManager.class, GlobalConstants.DEFAULT.toString(), this);
528 if (topologyManager == null) {
529 return new Status(StatusCode.NOSERVICE,
530 "Cannot validate the request: Required service is not available");
532 for (NodeConnector nc : commonPorts) {
534 * Shared link case : For internal port check if it has
535 * a vlan configured. If vlan is configured, allow the
536 * flowspec to be deleted If the port is host-facing, do
537 * not allow the flowspec to be deleted
539 if (!topologyManager.isInternal(nc)) {
540 return new Status(StatusCode.BADREQUEST, String.format(
541 "Port %s is shared and is host facing port: "
542 + "The only one flow spec assigned to this container cannot be removed", nc));
548 // Adding a new flow spec: need to check if other containers with common
549 // ports do not have same flow spec
550 Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
551 List<ContainerFlow> proposed = new ArrayList<ContainerFlow>(container.getContainerFlowSpecs());
553 for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
554 if (containerName.equalsIgnoreCase(entry.getKey())) {
557 ContainerData otherContainer = entry.getValue();
558 Set<NodeConnector> commonPorts = otherContainer.getNodeConnectors();
559 commonPorts.retainAll(thisContainerPorts);
561 if (!commonPorts.isEmpty()) {
562 Status status = checkCommonContainerFlow(otherContainer.getContainerFlowSpecs(), proposed);
563 if (!status.isSuccess()) {
564 return new Status(StatusCode.BADREQUEST, String.format(
565 "Container %s which shares ports with this container has overlapping flow spec: %s",
566 entry.getKey(), status.getDescription()));
572 return new Status(StatusCode.SUCCESS);
576 * Checks if the passed list of node connectors can be safely applied to the
577 * specified existing container in terms of port sharing with other containers.
579 * @param containerName
580 * the name of the existing container
582 * the list of node connectors to be added to the container
583 * @return the status object representing the result of the check
585 private Status validatePortSharing(String containerName, List<NodeConnector> portList) {
586 ContainerData container = this.getContainerByName(containerName);
587 if (container == null) {
588 String error = String
589 .format("Cannot validate port sharing for container %s: (container does not exist)", containerName);
591 return new Status(StatusCode.BADREQUEST, error);
593 return validatePortSharingInternal(portList, container.getContainerFlowSpecs());
597 * Checks if the proposed container configuration is valid to be applied in
598 * terms of port sharing with other containers.
600 * @param containerConf
601 * the container configuration object containing the list of node
603 * @return the status object representing the result of the check
605 private Status validatePortSharing(ContainerConfig containerConf) {
606 return validatePortSharingInternal(containerConf.getPortList(), containerConf.getContainerFlowSpecs());
610 * If any port is shared with an existing container, need flowSpec to be
611 * configured. If no flowSpec for this or other container, or if containers have any
612 * overlapping flowspec in common, then let the caller know this
613 * configuration has to be rejected.
615 private Status validatePortSharingInternal(List<NodeConnector> portList, List<ContainerFlow> flowSpecList) {
616 for (NodeConnector port : portList) {
617 List<String> slist = nodeConnectorToContainers.get(port);
618 if (slist != null && !slist.isEmpty()) {
619 for (String otherContainerName : slist) {
621 ContainerData other = containerData.get(otherContainerName);
622 if (flowSpecList.isEmpty()) {
623 msg = String.format("Port %s is shared and flow spec is empty for this container", port);
624 } else if (other.isFlowSpecEmpty()) {
625 msg = String.format("Port %s is shared and flow spec is empty for the other container", port);
626 } else if (!checkCommonContainerFlow(flowSpecList, other.getContainerFlowSpecs()).isSuccess()) {
627 msg = String.format("Port %s is shared and other container has common flow spec", port);
631 return new Status(StatusCode.BADREQUEST, msg);
636 return new Status(StatusCode.SUCCESS);
640 * Utility function to check if two lists of container flows share any same
641 * or overlapping container flows.
644 * One of the two lists of container flows to test
646 * One of the two lists of container flows to test
647 * @return The status of the check. Either SUCCESS or CONFLICT. In case of
648 * conflict, the Status will contain the description for the failed
651 private Status checkCommonContainerFlow(List<ContainerFlow> oneFlowList, List<ContainerFlow> twoFlowList) {
652 for (ContainerFlow oneFlow : oneFlowList) {
653 for (ContainerFlow twoFlow : twoFlowList) {
654 if (oneFlow.getMatch().intersetcs(twoFlow.getMatch())) {
655 return new Status(StatusCode.CONFLICT, String.format("Flow Specs overlap: %s %s",
656 oneFlow.getMatch(), twoFlow.getMatch()));
660 return new Status(StatusCode.SUCCESS);
664 * Return the ContainerData object for the passed container name. Given this is a
665 * backend database, the lower case version of the passed name is used while
666 * searching for the corresponding ContainerData object.
669 * The container name in any case
670 * @return The corresponding ContainerData object
672 private ContainerData getContainerByName(String name) {
673 return containerData.get(name.toLowerCase(Locale.ENGLISH));
677 * Add a ContainerData object for the given container name.
680 * The container name in any case
682 * The container data object
684 private void putContainerDataByName(String name, ContainerData sData) {
685 containerData.put(name.toLowerCase(Locale.ENGLISH), sData);
689 * Removes the ContainerData object for the given container name.
692 * The container name in any case
694 private void removeContainerDataByName(String name) {
695 containerData.remove(name.toLowerCase(Locale.ENGLISH));
699 public List<ContainerConfig> getContainerConfigList() {
700 return new ArrayList<ContainerConfig>(containerConfigs.values());
704 public ContainerConfig getContainerConfig(String containerName) {
705 ContainerConfig target = containerConfigs.get(containerName);
706 return (target == null) ? null : new ContainerConfig(target);
710 public List<String> getContainerNameList() {
712 * Return container names as they were configured by user (case sensitive)
713 * along with the default container
715 List<String> containerNameList = new ArrayList<String>();
716 containerNameList.add(GlobalConstants.DEFAULT.toString());
717 containerNameList.addAll(containerConfigs.keySet());
718 return containerNameList;
722 public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
723 Map<String, List<ContainerFlowConfig>> flowSpecConfig = new HashMap<String, List<ContainerFlowConfig>>();
724 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
725 List<ContainerFlowConfig> set = entry.getValue().getContainerFlowConfigs();
726 flowSpecConfig.put(entry.getKey(), set);
728 return flowSpecConfig;
731 private void loadConfigurations() {
733 * Read containers, container flows and finally containers' entries from file
734 * and program the database accordingly
736 if ((clusterServices != null) && (clusterServices.amICoordinator())) {
737 loadContainerConfig();
741 private Status saveContainerConfig() {
742 return saveContainerConfigLocal();
745 public Status saveContainerConfigLocal() {
746 ObjectWriter objWriter = new ObjectWriter();
748 Status status = objWriter.write(new ConcurrentHashMap<String, ContainerConfig>(containerConfigs), containersFileName);
749 if (!status.isSuccess()) {
750 return new Status(StatusCode.INTERNALERROR, "Failed to save container configurations: "
751 + status.getDescription());
753 return new Status(StatusCode.SUCCESS);
756 private void removeComponentsStartUpfiles(String containerName) {
757 String startupLocation = String.format("./%s", GlobalConstants.STARTUPHOME.toString());
758 String containerPrint = String.format("_%s.", containerName.toLowerCase(Locale.ENGLISH));
760 File directory = new File(startupLocation);
761 String[] fileList = directory.list();
763 logger.trace("Deleting startup configuration files for container {}", containerName);
764 if (fileList != null) {
765 for (String fileName : fileList) {
766 if (fileName.contains(containerPrint)) {
767 String fullPath = String.format("%s/%s", startupLocation, fileName);
768 File file = new File(fullPath);
769 boolean done = file.delete();
770 logger.trace("{} {}", (done ? "Deleted: " : "Failed to delete: "), fileName);
777 * Create and initialize default all resource group and create association
778 * with default well known users and profiles, if not already learnt from
779 * another cluster node
781 private void createDefaultAuthorizationGroups() {
782 allResourcesGroupName = ContainerManager.allContainersGroup;
784 // Add the default container to the all containers group if needed
785 String defaultContainer = GlobalConstants.DEFAULT.toString();
786 Set<String> allContainers = (resourceGroups.containsKey(allResourcesGroupName)) ? resourceGroups
787 .get(allResourcesGroupName) : new HashSet<String>();
788 if (!allContainers.contains(defaultContainer)) {
789 // Add Default container
790 allContainers.add(defaultContainer);
792 resourceGroups.put(allResourcesGroupName, allContainers);
795 // Add the controller well known roles, if not known already
796 if (!roles.containsKey(UserLevel.SYSTEMADMIN.toString())) {
797 roles.put(UserLevel.SYSTEMADMIN.toString(), AppRoleLevel.APPADMIN);
799 if (!roles.containsKey(UserLevel.NETWORKADMIN.toString())) {
800 roles.put(UserLevel.NETWORKADMIN.toString(), AppRoleLevel.APPADMIN);
802 if (!roles.containsKey(UserLevel.NETWORKOPERATOR.toString())) {
803 roles.put(UserLevel.NETWORKOPERATOR.toString(), AppRoleLevel.APPOPERATOR);
807 * Create and add the all containers user groups and associate them to the
808 * default well known user roles, if not present already
810 if (!groupsAuthorizations.containsKey(UserLevel.NETWORKADMIN.toString())) {
811 Set<ResourceGroup> writeProfile = new HashSet<ResourceGroup>(1);
812 Set<ResourceGroup> readProfile = new HashSet<ResourceGroup>(1);
813 writeProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.WRITE));
814 readProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.READ));
815 groupsAuthorizations.put(UserLevel.SYSTEMADMIN.toString(), writeProfile);
816 groupsAuthorizations.put(UserLevel.NETWORKADMIN.toString(), writeProfile);
817 groupsAuthorizations.put(UserLevel.NETWORKOPERATOR.toString(), readProfile);
822 * Until manual configuration is not available, automatically maintain the
823 * well known resource groups
825 * @param containerName
828 private void updateResourceGroups(ContainerConfig containerConf, boolean delete) {
829 // Container Roles and Container Resource Group
830 String containerName = containerConf.getContainer();
831 String groupName = containerConf.getContainerGroupName();
832 String containerAdminRole = containerConf.getContainerAdminRole();
833 String containerOperatorRole = containerConf.getContainerOperatorRole();
834 Set<String> allContainerSet = resourceGroups.get(allResourcesGroupName);
836 resourceGroups.remove(groupName);
837 groupsAuthorizations.remove(containerAdminRole);
838 groupsAuthorizations.remove(containerOperatorRole);
839 roles.remove(containerAdminRole);
840 roles.remove(containerOperatorRole);
841 // Update the all container group
842 allContainerSet.remove(containerName);
844 Set<String> resources = new HashSet<String>(1);
845 resources.add(containerName);
846 resourceGroups.put(groupName, resources);
847 Set<ResourceGroup> adminGroups = new HashSet<ResourceGroup>(1);
848 Set<ResourceGroup> operatorGroups = new HashSet<ResourceGroup>(1);
849 adminGroups.add(new ResourceGroup(groupName, Privilege.WRITE));
850 operatorGroups.add(new ResourceGroup(groupName, Privilege.READ));
851 groupsAuthorizations.put(containerAdminRole, adminGroups);
852 groupsAuthorizations.put(containerOperatorRole, operatorGroups);
853 roles.put(containerAdminRole, AppRoleLevel.APPADMIN);
854 roles.put(containerOperatorRole, AppRoleLevel.APPOPERATOR);
855 // Update the all containers resource group
856 allContainerSet.add(containerName);
858 // Update resource groups in cluster
859 resourceGroups.put(allResourcesGroupName, allContainerSet);
863 * Notify ContainerAware listeners of the creation/deletion of the container
865 * @param containerName
867 * true is container was removed, false otherwise
869 private void notifyContainerAwareListeners(String containerName, boolean delete) {
870 // Back-end World: container name forced to lower case
871 String name = containerName.toLowerCase(Locale.ENGLISH);
873 synchronized (this.iContainerAware) {
874 for (IContainerAware i : this.iContainerAware) {
876 i.containerDestroy(name);
878 i.containerCreate(name);
885 * Notify the ContainerListener listeners in case the container mode has
886 * changed following a container configuration operation Note: this call
887 * must happen after the configuration db has been updated
889 * @param lastActionDelete
890 * true if the last container configuration operation was a
891 * container delete operation
893 * if true, the notification is also sent to the
894 * IContainerLocalListener classes besides the IContainerListener
897 private void notifyContainerModeChange(boolean lastActionDelete, boolean notifyLocal) {
898 if (lastActionDelete == false && containerConfigs.size() == 1) {
899 logger.info("First container Creation. Inform listeners");
900 synchronized (this.iContainerListener) {
901 for (IContainerListener i : this.iContainerListener) {
902 i.containerModeUpdated(UpdateType.ADDED);
906 synchronized (this.iContainerLocalListener) {
907 for (IContainerLocalListener i : this.iContainerLocalListener) {
908 i.containerModeUpdated(UpdateType.ADDED);
912 } else if (lastActionDelete == true && containerConfigs.isEmpty()) {
913 logger.info("Last container Deletion. Inform listeners");
914 synchronized (this.iContainerListener) {
915 for (IContainerListener i : this.iContainerListener) {
916 i.containerModeUpdated(UpdateType.REMOVED);
920 synchronized (this.iContainerLocalListener) {
921 for (IContainerLocalListener i : this.iContainerLocalListener) {
922 i.containerModeUpdated(UpdateType.REMOVED);
929 private Status addRemoveContainerEntries(String containerName, List<String> nodeConnectorsString, boolean delete) {
930 // Construct action message
931 String action = String.format("Node connector(s) %s container %s: %s", delete ? "removal from" : "addition to",
932 containerName, nodeConnectorsString);
935 if (nodeConnectorsString == null || nodeConnectorsString.isEmpty()) {
936 return new Status(StatusCode.BADREQUEST, "Node connector list is null or empty");
940 ContainerConfig entryConf = containerConfigs.get(containerName);
941 if (entryConf == null) {
942 String msg = String.format("Container not found: %s", containerName);
943 String error = String.format("Failed to apply %s: (%s)", action, msg);
945 return new Status(StatusCode.NOTFOUND, msg);
949 Status status = ContainerConfig.validateNodeConnectors(nodeConnectorsString);
950 if (!status.isSuccess()) {
951 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
956 List<NodeConnector> nodeConnectors = ContainerConfig.nodeConnectorsFromString(nodeConnectorsString);
958 // Port sharing check
961 * Check if the ports being added to this container already belong to
962 * other containers. If so check whether the the appropriate flow specs
963 * are configured on this container
965 status = validatePortSharing(containerName, nodeConnectors);
966 if (!status.isSuccess()) {
967 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
974 status = updateContainerEntryDatabase(containerName, nodeConnectors, delete);
975 if (!status.isSuccess()) {
976 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
981 // Update Configuration
982 status = (delete) ? entryConf.removeNodeConnectors(nodeConnectorsString) : entryConf
983 .addNodeConnectors(nodeConnectorsString);
984 if (!status.isSuccess()) {
985 String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
987 // Revert backend changes
988 Status statusRevert = updateContainerEntryDatabase(containerName, nodeConnectors, !delete);
989 if (!statusRevert.isSuccess()) {
991 logger.error("Failed to revert changes in database (CRITICAL)");
996 // Update cluster Configuration cache
997 containerConfigs.put(containerName, entryConf);
999 // Notify global and local listeners
1000 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1001 notifyContainerEntryChangeInternal(containerName, nodeConnectors, update, true);
1002 // Trigger cluster notification
1003 containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(nodeConnectors, update));
1008 private void notifyContainerChangeInternal(ContainerConfig conf, UpdateType update, boolean notifyLocal) {
1009 String containerName = conf.getContainerName();
1010 logger.trace("Notifying listeners on {} for container {}", update, containerName);
1011 // Back-end World: container name forced to lower case
1012 String container = containerName.toLowerCase(Locale.ENGLISH);
1013 boolean delete = (update == UpdateType.REMOVED);
1014 // Check if a container mode change notification is needed
1015 notifyContainerModeChange(delete, notifyLocal);
1017 notifyContainerAwareListeners(container, delete);
1020 * This is a quick fix until configuration service becomes the
1021 * centralized configuration management place. Here container manager
1022 * will remove the startup files for all the bundles that are present in
1023 * the container being deleted. Do the cleanup here in Container manger
1024 * as do not want to put this temporary code in Configuration manager
1028 // TODO: remove when Config Mgr takes over
1029 removeComponentsStartUpfiles(containerName);
1033 private void notifyContainerEntryChangeInternal(String containerName, List<NodeConnector> ncList, UpdateType update, boolean notifyLocal) {
1034 logger.trace("Notifying listeners on {} for ports {} in container {}", update, ncList, containerName);
1035 // Back-end World: container name forced to lower case
1036 String container = containerName.toLowerCase(Locale.ENGLISH);
1037 for (NodeConnector nodeConnector : ncList) {
1038 // Now signal that the port has been added/removed
1039 synchronized (this.iContainerListener) {
1040 for (IContainerListener i : this.iContainerListener) {
1041 i.nodeConnectorUpdated(container, nodeConnector, update);
1044 // Check if the Functional Modules need to be notified as well
1046 synchronized (this.iContainerLocalListener) {
1047 for (IContainerLocalListener i : this.iContainerLocalListener) {
1048 i.nodeConnectorUpdated(container, nodeConnector, update);
1055 private void notifyCFlowChangeInternal(String containerName, List<ContainerFlowConfig> confList, UpdateType update,
1056 boolean notifyLocal) {
1057 logger.trace("Notifying listeners on {} for flow specs {} in container {}", update, confList, containerName);
1058 // Back-end World: container name forced to lower case
1059 String container = containerName.toLowerCase(Locale.ENGLISH);
1061 for (ContainerFlowConfig conf : confList) {
1062 for (Match match : conf.getMatches()) {
1063 ContainerFlow cFlow = new ContainerFlow(match);
1064 synchronized (this.iContainerListener) {
1065 for (IContainerListener i : this.iContainerListener) {
1066 i.containerFlowUpdated(container, cFlow, cFlow, update);
1069 // Check if the Functional Modules need to be notified as well
1071 synchronized (this.iContainerLocalListener) {
1072 for (IContainerLocalListener i : this.iContainerLocalListener) {
1073 i.containerFlowUpdated(container, cFlow, cFlow, update);
1081 private Status addRemoveContainerFlow(String containerName, List<ContainerFlowConfig> cFlowConfList, boolean delete) {
1082 // Construct action message
1083 String action = String.format("Flow spec(s) %s container %s: %s", delete ? "removal from" : "addition to",
1084 containerName, cFlowConfList);
1087 ContainerConfig containerConfig = this.containerConfigs.get(containerName);
1088 if (containerConfig == null) {
1089 String msg = String.format("Container not found: %s", containerName);
1090 String error = String.format("Failed to apply %s: (%s)", action, msg);
1092 return new Status(StatusCode.NOTFOUND, "Container not present");
1095 // Validity check, check for overlaps on current container configuration
1096 Status status = containerConfig.validateContainerFlowModify(cFlowConfList, delete);
1097 if (!status.isSuccess()) {
1098 String msg = status.getDescription();
1099 String error = String.format("Failed to apply %s: (%s)", action, msg);
1101 return new Status(StatusCode.BADREQUEST, msg);
1104 // Validate the operation in terms to the port sharing with other containers
1105 for (ContainerFlowConfig conf : cFlowConfList) {
1106 for (Match match : conf.getMatches()) {
1107 ContainerFlow cFlow = new ContainerFlow(match);
1108 status = validateContainerFlowAddRemoval(containerName, cFlow, delete);
1109 if (!status.isSuccess()) {
1110 String msg = "Validation failed: " + status.getDescription();
1111 String error = String.format("Failed to apply %s: (%s)", action, msg);
1113 return new Status(StatusCode.BADREQUEST, msg);
1119 status = updateContainerFlow(containerName, cFlowConfList, delete);
1120 if (!status.isSuccess()) {
1121 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
1122 logger.error(error);
1126 // Update Configuration
1127 status = (delete) ? containerConfig.removeContainerFlows(cFlowConfList) : containerConfig
1128 .addContainerFlows(cFlowConfList);
1129 if (!status.isSuccess()) {
1130 String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
1131 logger.error(error);
1132 // Revert backend changes
1133 Status statusRevert = updateContainerFlow(containerName, cFlowConfList, !delete);
1134 if (!statusRevert.isSuccess()) {
1136 logger.error("Failed to revert changes in database (CRITICAL)");
1140 // Update cluster cache
1141 this.containerConfigs.put(containerName, containerConfig);
1143 // Notify global and local listeners
1144 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1145 notifyCFlowChangeInternal(containerName, cFlowConfList, update, true);
1146 // Trigger cluster notification
1147 containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(cFlowConfList, update));
1152 private Status addRemoveContainer(ContainerConfig containerConf, boolean delete) {
1153 // Construct action message
1154 String action = String.format("Container %s", delete ? "removal" : "creation");
1156 // Valid configuration check
1157 Status status = null;
1158 String error = (containerConfigs == null) ? String.format("Invalid %s configuration: (null config object)", action)
1159 : (!(status = containerConf.validate()).isSuccess()) ? String.format("Invalid %s configuration: (%s)",
1160 action, status.getDescription()) : null;
1161 if (error != null) {
1163 return new Status(StatusCode.BADREQUEST, error);
1166 // Configuration presence check
1167 String containerName = containerConf.getContainerName();
1169 if (!containerConfigs.containsKey(containerName)) {
1170 String msg = String.format("%s Failed: (Container does not exist: %s)", action, containerName);
1172 return new Status(StatusCode.NOTFOUND, msg);
1175 if (containerConfigs.containsKey(containerName)) {
1176 String msg = String.format("%s Failed: (Container already exist: %s)", action, containerName);
1178 return new Status(StatusCode.CONFLICT, msg);
1183 * The proposed container configuration could be a complex one containing
1184 * both ports and flow spec. If so, check if it has shared ports with
1185 * other existing containers. If that is the case verify flow spec isolation
1186 * is in place. No need to check on flow spec validation first. This
1187 * would take care of both
1190 status = validatePortSharing(containerConf);
1191 if (!status.isSuccess()) {
1192 error = String.format("%s Failed: (%s)", action, status.getDescription());
1193 logger.error(error);
1199 status = updateContainerDatabase(containerConf, delete);
1201 // Abort and exit here if back-end database update failed
1202 if (!status.isSuccess()) {
1207 * Update Configuration: This will trigger the notifications on cache
1208 * update callback locally and on the other cluster nodes
1211 this.containerConfigs.remove(containerName);
1213 this.containerConfigs.put(containerName, containerConf);
1216 // Automatically create and populate user and resource groups
1217 updateResourceGroups(containerConf, delete);
1219 // Notify global and local listeners
1220 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1221 notifyContainerChangeInternal(containerConf, update, true);
1223 // Trigger cluster notification
1224 containerChangeEvents.put(containerName, new ContainerChangeEvent(containerConf, update));
1226 if (update == UpdateType.ADDED) {
1227 if (containerConf.hasFlowSpecs()) {
1228 List<ContainerFlowConfig> specList = containerConf.getContainerFlowConfigs();
1229 // Notify global and local listeners about flow spec addition
1230 notifyCFlowChangeInternal(containerName, specList, update, true);
1232 // Trigger cluster notification
1233 containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(specList, update));
1236 if (containerConf.hasNodeConnectors()) {
1237 List<NodeConnector> ncList = containerConf.getPortList();
1238 // Notify global and local listeners about port(s) addition
1239 notifyContainerEntryChangeInternal(containerName, ncList, update, true);
1240 // Trigger cluster notification
1241 containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(ncList, update));
1246 clusterServices.removeContainerCaches(containerName);
1252 public Status addContainer(ContainerConfig containerConf) {
1253 return addRemoveContainer(containerConf, false);
1257 public Status removeContainer(ContainerConfig containerConf) {
1258 return addRemoveContainer(containerConf, true);
1262 public Status removeContainer(String containerName) {
1263 // Construct action message
1264 String action = String.format("Container removal: %s", containerName);
1266 ContainerConfig containerConf = containerConfigs.get(containerName);
1267 if (containerConf == null) {
1268 String msg = String.format("Container not found");
1269 String error = String.format("Failed to apply %s: (%s)", action, msg);
1271 return new Status(StatusCode.NOTFOUND, msg);
1273 return addRemoveContainer(containerConf, true);
1277 public Status addContainerEntry(String containerName, List<String> nodeConnectors) {
1278 return addRemoveContainerEntries(containerName, nodeConnectors, false);
1282 public Status removeContainerEntry(String containerName, List<String> nodeConnectors) {
1283 return addRemoveContainerEntries(containerName, nodeConnectors, true);
1287 public Status addContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1288 return addRemoveContainerFlow(containerName, fSpecConf, false);
1292 public Status removeContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1293 return addRemoveContainerFlow(containerName, fSpecConf, true);
1297 public Status removeContainerFlows(String containerName, Set<String> names) {
1298 // Construct action message
1299 String action = String.format("Flow spec(s) removal from container %s: %s", containerName, names);
1302 ContainerConfig sc = containerConfigs.get(containerName);
1304 String msg = String.format("Container not found: %s", containerName);
1305 String error = String.format("Failed to apply %s: (%s)", action, msg);
1307 return new Status(StatusCode.NOTFOUND, msg);
1309 List<ContainerFlowConfig> list = sc.getContainerFlowConfigs(names);
1310 if (list.isEmpty() || list.size() != names.size()) {
1311 String msg = String.format("Cannot find all the specified flow specs");
1312 String error = String.format("Failed to apply %s: (%s)", action, msg);
1314 return new Status(StatusCode.BADREQUEST, msg);
1316 return addRemoveContainerFlow(containerName, list, true);
1320 public List<ContainerFlowConfig> getContainerFlows(String containerName) {
1321 ContainerConfig sc = containerConfigs.get(containerName);
1322 return (sc == null) ? new ArrayList<ContainerFlowConfig>(0) : sc.getContainerFlowConfigs();
1326 public List<String> getContainerFlowNameList(String containerName) {
1327 ContainerConfig sc = containerConfigs.get(containerName);
1328 return (sc == null) ? new ArrayList<String>(0) : sc.getContainerFlowConfigsNames();
1332 public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
1333 // Perform the class deserialization locally, from inside the package
1334 // where the class is defined
1335 return ois.readObject();
1338 @SuppressWarnings("unchecked")
1339 private void loadContainerConfig() {
1340 ObjectReader objReader = new ObjectReader();
1341 ConcurrentMap<String, ContainerConfig> configMap = (ConcurrentMap<String, ContainerConfig>) objReader.read(this,
1342 containersFileName);
1344 if (configMap == null) {
1348 for (Map.Entry<String, ContainerConfig> configEntry : configMap.entrySet()) {
1349 addContainer(configEntry.getValue());
1353 public void _psc(CommandInterpreter ci) {
1354 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1355 ContainerConfig sc = entry.getValue();
1356 ci.println(String.format("%s: %s", sc.getContainerName(), sc.toString()));
1358 ci.println("Total number of containers: " + containerConfigs.entrySet().size());
1361 public void _pfc(CommandInterpreter ci) {
1362 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1363 ContainerConfig sc = entry.getValue();
1364 ci.println(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
1368 public void _psd(CommandInterpreter ci) {
1369 for (String containerName : containerData.keySet()) {
1370 ContainerData sd = containerData.get(containerName);
1371 for (Node sid : sd.getSwPorts().keySet()) {
1372 Set<NodeConnector> s = sd.getSwPorts().get(sid);
1373 ci.println("\t" + sid + " : " + s);
1376 for (ContainerFlow s : sd.getContainerFlowSpecs()) {
1377 ci.println("\t" + s.toString());
1382 public void _psp(CommandInterpreter ci) {
1383 for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
1384 ci.println(nodeConnectorToContainers.get(sp));
1388 public void _psm(CommandInterpreter ci) {
1389 for (Node sp : nodeToContainers.keySet()) {
1390 ci.println(nodeToContainers.get(sp));
1394 public void _addContainer(CommandInterpreter ci) {
1395 String containerName = ci.nextArgument();
1396 if (containerName == null) {
1397 ci.print("Container Name not specified");
1400 String staticVlan = ci.nextArgument();
1401 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
1402 ci.println(this.addRemoveContainer(containerConfig, false));
1405 public void _createContainer(CommandInterpreter ci) {
1406 String containerName = ci.nextArgument();
1407 if (containerName == null) {
1408 ci.print("Container Name not specified");
1411 String staticVlan = ci.nextArgument();
1412 if (staticVlan == null) {
1413 ci.print("Static Vlan not specified");
1416 List<String> ports = new ArrayList<String>();
1417 for (long l = 1L; l < 10L; l++) {
1418 ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
1420 List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
1421 cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
1422 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
1423 ci.println(this.addRemoveContainer(containerConfig, false));
1426 public void _removeContainer(CommandInterpreter ci) {
1427 String containerName = ci.nextArgument();
1428 if (containerName == null) {
1429 ci.print("Container Name not specified");
1432 ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
1433 ci.println(this.addRemoveContainer(containerConfig, true));
1436 public void _addContainerEntry(CommandInterpreter ci) {
1437 String containerName = ci.nextArgument();
1438 if (containerName == null) {
1439 ci.print("Container Name not specified");
1442 String nodeId = ci.nextArgument();
1443 if (nodeId == null) {
1444 ci.print("Node Id not specified");
1447 String portId = ci.nextArgument();
1448 if (portId == null) {
1449 ci.print("Port not specified");
1452 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1453 Short port = Short.valueOf(portId);
1454 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1455 List<String> portList = new ArrayList<String>(1);
1456 portList.add(nc.toString());
1457 ci.println(this.addRemoveContainerEntries(containerName, portList, false));
1460 public void _removeContainerEntry(CommandInterpreter ci) {
1461 String containerName = ci.nextArgument();
1462 if (containerName == null) {
1463 ci.print("Container Name not specified");
1466 String nodeId = ci.nextArgument();
1467 if (nodeId == null) {
1468 ci.print("Node Id not specified");
1471 String portId = ci.nextArgument();
1472 if (portId == null) {
1473 ci.print("Port not specified");
1476 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1477 Short port = Short.valueOf(portId);
1478 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1479 List<String> portList = new ArrayList<String>(1);
1480 portList.add(nc.toString());
1481 ci.println(this.addRemoveContainerEntries(containerName, portList, true));
1484 private ContainerFlowConfig createSampleContainerFlowConfig(String cflowName, boolean boolUnidirectional) {
1485 ContainerFlowConfig cfg = new ContainerFlowConfig(cflowName, "9.9.1.0/24", "19.9.1.2", "TCP", "1234", "25");
1489 public void _addContainerFlow(CommandInterpreter ci) {
1490 String containerName = ci.nextArgument();
1491 if (containerName == null) {
1492 ci.print("Container Name not specified");
1495 String cflowName = ci.nextArgument();
1496 if (cflowName == null) {
1497 ci.print("cflowName not specified");
1500 String unidirectional = ci.nextArgument();
1501 boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
1502 List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
1503 list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
1504 ci.println(this.addRemoveContainerFlow(containerName, list, false));
1507 public void _removeContainerFlow(CommandInterpreter ci) {
1508 String containerName = ci.nextArgument();
1509 if (containerName == null) {
1510 ci.print("Container Name not specified");
1513 String cflowName = ci.nextArgument();
1514 if (cflowName == null) {
1515 ci.print("cflowName not specified");
1518 Set<String> set = new HashSet<String>(1);
1520 ci.println(this.removeContainerFlows(containerName, set));
1524 public String getHelp() {
1525 StringBuffer help = new StringBuffer();
1526 help.append("---ContainerManager Testing---\n");
1527 help.append("\tpsc - Print ContainerConfigs\n");
1528 help.append("\tpfc - Print FlowSpecConfigs\n");
1529 help.append("\tpsd - Print ContainerData\n");
1530 help.append("\tpsp - Print nodeConnectorToContainers\n");
1531 help.append("\tpsm - Print nodeToContainers\n");
1532 help.append("\t addContainer <containerName> <staticVlan> \n");
1533 help.append("\t removeContainer <containerName> \n");
1534 help.append("\t addContainerEntry <containerName> <nodeId> <port> \n");
1535 help.append("\t removeContainerEntry <containerName> <nodeId> <port> \n");
1536 help.append("\t addContainerFlow <containerName> <cflowName> <unidirectional true/false>\n");
1537 help.append("\t removeContainerFlow <containerName> <cflowName> \n");
1538 return help.toString();
1542 public boolean doesContainerExist(String containerName) {
1543 // Test for default container
1544 if (GlobalConstants.DEFAULT.toString().equalsIgnoreCase(containerName)) {
1547 // Test for non-default one
1548 return (getContainerByName(containerName) != null);
1552 public ContainerData getContainerData(String containerName) {
1553 return (getContainerByName(containerName));
1557 public Status saveConfiguration() {
1558 return saveContainerConfig();
1561 public void _containermgrGetRoles(CommandInterpreter ci) {
1562 ci.println("Configured roles for Container Mgr:");
1563 List<String> list = this.getRoles();
1564 for (String role : list) {
1565 ci.println(role + "\t" + roles.get(role));
1569 public void _containermgrGetAuthorizedGroups(CommandInterpreter ci) {
1570 String roleName = ci.nextArgument();
1571 if (roleName == null || roleName.trim().isEmpty()) {
1572 ci.println("Invalid argument");
1573 ci.println("mmGetAuthorizedGroups <role_name>");
1576 ci.println("Resource Groups associated to role " + roleName + ":");
1577 List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
1578 for (ResourceGroup group : list) {
1579 ci.println(group.toString());
1583 public void _containermgrGetAuthorizedResources(CommandInterpreter ci) {
1584 String roleName = ci.nextArgument();
1585 if (roleName == null || roleName.trim().isEmpty()) {
1586 ci.println("Invalid argument");
1587 ci.println("mmGetAuthorizedResources <role_name>");
1590 ci.println("Resource associated to role " + roleName + ":");
1591 List<Resource> list = this.getAuthorizedResources(roleName);
1592 for (Resource resource : list) {
1593 ci.println(resource.toString());
1597 public void _containermgrGetResourcesForGroup(CommandInterpreter ci) {
1598 String groupName = ci.nextArgument();
1599 if (groupName == null || groupName.trim().isEmpty()) {
1600 ci.println("Invalid argument");
1601 ci.println("containermgrResourcesForGroup <group_name>");
1604 ci.println("Group " + groupName + " contains the following resources:");
1605 List<Object> resources = this.getResources(groupName);
1606 for (Object resource : resources) {
1607 ci.println(resource.toString());
1611 public void _containermgrGetUserLevel(CommandInterpreter ci) {
1612 String userName = ci.nextArgument();
1613 if (userName == null || userName.trim().isEmpty()) {
1614 ci.println("Invalid argument");
1615 ci.println("containermgrGetUserLevel <user_name>");
1618 ci.println("User " + userName + " has level: " + this.getUserLevel(userName));
1621 public void _containermgrGetUserResources(CommandInterpreter ci) {
1622 String userName = ci.nextArgument();
1623 if (userName == null || userName.trim().isEmpty()) {
1624 ci.println("Invalid argument");
1625 ci.println("containermgrGetUserResources <user_name>");
1628 ci.println("User " + userName + " owns the following resources: ");
1629 Set<Resource> resources = this.getAllResourcesforUser(userName);
1630 for (Resource resource : resources) {
1631 ci.println(resource.toString());
1636 * For scalability testing where as of now controller gui is unresponsive
1637 * providing here an osgi hook to trigger the save config so that DT do not
1638 * have to reaply the scalable configuration each time they restart the
1641 // TODO: remove when no longer needed
1642 public void _saveConfig(CommandInterpreter ci) {
1643 Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
1645 IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
1646 IConfigurationService.class, this);
1647 if (configService != null) {
1648 status = configService.saveConfigurations();
1650 ci.println(status.toString());
1654 public List<String> getContainerNames() {
1655 return getContainerNameList();
1659 public boolean hasNonDefaultContainer() {
1660 return !containerConfigs.keySet().isEmpty();
1664 public boolean inContainerMode() {
1665 return this.containerConfigs.size() > 0;