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;
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;
24 import java.util.Map.Entry;
26 import java.util.concurrent.ConcurrentMap;
27 import java.util.concurrent.CopyOnWriteArrayList;
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.NodeConnectorsChangeEvent;
48 import org.opendaylight.controller.sal.authorization.AppRoleLevel;
49 import org.opendaylight.controller.sal.authorization.Privilege;
50 import org.opendaylight.controller.sal.authorization.Resource;
51 import org.opendaylight.controller.sal.authorization.ResourceGroup;
52 import org.opendaylight.controller.sal.authorization.UserLevel;
53 import org.opendaylight.controller.sal.core.ContainerFlow;
54 import org.opendaylight.controller.sal.core.IContainerAware;
55 import org.opendaylight.controller.sal.core.IContainerListener;
56 import org.opendaylight.controller.sal.core.IContainerLocalListener;
57 import org.opendaylight.controller.sal.core.Node;
58 import org.opendaylight.controller.sal.core.NodeConnector;
59 import org.opendaylight.controller.sal.core.UpdateType;
60 import org.opendaylight.controller.sal.match.Match;
61 import org.opendaylight.controller.sal.utils.GlobalConstants;
62 import org.opendaylight.controller.sal.utils.IObjectReader;
63 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
64 import org.opendaylight.controller.sal.utils.NodeCreator;
65 import org.opendaylight.controller.sal.utils.ServiceHelper;
66 import org.opendaylight.controller.sal.utils.Status;
67 import org.opendaylight.controller.sal.utils.StatusCode;
68 import org.opendaylight.controller.topologymanager.ITopologyManager;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
72 public class ContainerManager extends Authorization<String> implements IContainerManager, IObjectReader,
73 CommandProvider, ICacheUpdateAware<String, Object>, IContainerInternal, IContainerAuthorization,
75 private static final Logger logger = LoggerFactory.getLogger(ContainerManager.class);
76 private static String CONTAINERS_FILE_NAME = "containers.conf";
77 private static final String allContainersGroup = "allContainers";
78 private IClusterGlobalServices clusterServices;
79 private IConfigurationService configurationService;
81 * Collection containing the configuration objects. This is configuration
82 * world: container names (also the map key) are maintained as they were
83 * configured by user, same case
85 private ConcurrentMap<String, ContainerConfig> containerConfigs;
86 private ConcurrentMap<String, ContainerData> containerData;
87 private ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>> nodeConnectorToContainers;
88 private ConcurrentMap<Node, Set<String>> nodeToContainers;
89 private ConcurrentMap<String, Object> containerChangeEvents;
90 private final Set<IContainerAware> iContainerAware = Collections.synchronizedSet(new HashSet<IContainerAware>());
91 private final Set<IContainerListener> iContainerListener = Collections
92 .synchronizedSet(new HashSet<IContainerListener>());
93 private final Set<IContainerLocalListener> iContainerLocalListener = Collections
94 .synchronizedSet(new HashSet<IContainerLocalListener>());
96 void setIContainerListener(IContainerListener s) {
97 if (this.iContainerListener != null) {
98 this.iContainerListener.add(s);
100 * At boot with startup, containers are created before listeners have
101 * joined. Replaying here the first container creation notification for
102 * the joining listener when containers are already present. Also
103 * replaying all the node connectors and container flows additions
104 * to the existing containers.
106 if (!this.containerData.isEmpty()) {
107 s.containerModeUpdated(UpdateType.ADDED);
109 for (ConcurrentMap.Entry<NodeConnector, CopyOnWriteArrayList<String>> entry : nodeConnectorToContainers
111 NodeConnector port = entry.getKey();
112 for (String container : entry.getValue()) {
113 s.nodeConnectorUpdated(container, port, UpdateType.ADDED);
116 for (Map.Entry<String, ContainerData> container : containerData.entrySet()) {
117 for (ContainerFlow cFlow : container.getValue().getContainerFlowSpecs()) {
118 s.containerFlowUpdated(container.getKey(), cFlow, cFlow, UpdateType.ADDED);
124 void unsetIContainerListener(IContainerListener s) {
125 if (this.iContainerListener != null) {
126 this.iContainerListener.remove(s);
130 void setIContainerLocalListener(IContainerLocalListener s) {
131 if (this.iContainerLocalListener != null) {
132 this.iContainerLocalListener.add(s);
136 void unsetIContainerLocalListener(IContainerLocalListener s) {
137 if (this.iContainerLocalListener != null) {
138 this.iContainerLocalListener.remove(s);
142 public void setIContainerAware(IContainerAware iContainerAware) {
143 if (!this.iContainerAware.contains(iContainerAware)) {
144 this.iContainerAware.add(iContainerAware);
145 // Now call the container creation for all the known containers so far
146 for (String container : getContainerNameList()) {
147 iContainerAware.containerCreate(container.toLowerCase(Locale.ENGLISH));
152 public void unsetIContainerAware(IContainerAware iContainerAware) {
153 this.iContainerAware.remove(iContainerAware);
154 // There is no need to do cleanup of the component when
155 // unregister because it will be taken care by the Container
159 public void setClusterServices(IClusterGlobalServices i) {
160 this.clusterServices = i;
161 logger.debug("IClusterServices set");
164 public void unsetClusterServices(IClusterGlobalServices i) {
165 if (this.clusterServices == i) {
166 this.clusterServices = null;
167 logger.debug("IClusterServices Unset");
171 public void setConfigurationService(IConfigurationService service) {
172 logger.trace("Got configuration service set request {}", service);
173 this.configurationService = service;
176 public void unsetConfigurationService(IConfigurationService service) {
177 logger.trace("Got configuration service UNset request");
178 this.configurationService = null;
181 private void allocateCaches() {
182 logger.debug("Container Manager allocating caches");
184 if (clusterServices == null) {
185 logger.warn("un-initialized Cluster Services, can't allocate caches");
189 clusterServices.createCache("containermgr.containerConfigs", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
191 clusterServices.createCache("containermgr.event.containerChange",
192 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
194 clusterServices.createCache("containermgr.containerData", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
196 clusterServices.createCache("containermgr.nodeConnectorToContainers",
197 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
199 clusterServices.createCache("containermgr.nodeToContainers", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
201 clusterServices.createCache("containermgr.containerGroups", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
203 clusterServices.createCache("containermgr.containerAuthorizations",
204 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
206 clusterServices.createCache("containermgr.roles", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
207 } catch (CacheConfigException cce) {
208 logger.error("Cache configuration invalid - check cache mode");
209 } catch (CacheExistException ce) {
210 logger.error("Cache already exits - destroy and recreate if needed");
214 @SuppressWarnings({ "unchecked" })
215 private void retrieveCaches() {
216 logger.debug("Container Manager retrieving caches");
218 if (clusterServices == null) {
219 logger.warn("un-initialized Cluster Services, can't retrieve caches");
223 containerConfigs = (ConcurrentMap<String, ContainerConfig>) clusterServices.getCache("containermgr.containerConfigs");
225 containerChangeEvents = (ConcurrentMap<String, Object>) clusterServices.getCache("containermgr.event.containerChange");
227 containerData = (ConcurrentMap<String, ContainerData>) clusterServices.getCache("containermgr.containerData");
229 nodeConnectorToContainers = (ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>>) clusterServices
230 .getCache("containermgr.nodeConnectorToContainers");
232 nodeToContainers = (ConcurrentMap<Node, Set<String>>) clusterServices.getCache("containermgr.nodeToContainers");
234 resourceGroups = (ConcurrentMap<String, Set<String>>) clusterServices.getCache("containermgr.containerGroups");
236 groupsAuthorizations = (ConcurrentMap<String, Set<ResourceGroup>>) clusterServices
237 .getCache("containermgr.containerAuthorizations");
239 roles = (ConcurrentMap<String, AppRoleLevel>) clusterServices.getCache("containermgr.roles");
241 if (inContainerMode()) {
242 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
243 // Notify global and local listeners about the mode change
244 notifyContainerChangeInternal(entry.getValue(), UpdateType.ADDED, true);
250 public void entryCreated(String containerName, String cacheName, boolean originLocal) {
255 public void entryUpdated(String key, Object value, String cacheName, boolean originLocal) {
257 * This is were container manager replays a configuration event that was
258 * notified by its peer from a cluster node where the configuration
259 * happened. Only the global listeners, the cluster unaware classes,
260 * (mainly the shim classes in the sdn protocol plugins) need to receive
261 * these notifications on this cluster node. The cluster aware classes,
262 * like the functional modules which reacts on these events, must _not_
263 * be notified to avoid parallel computation in the cluster.
266 if (value instanceof NodeConnectorsChangeEvent) {
267 NodeConnectorsChangeEvent event = (NodeConnectorsChangeEvent) value;
268 List<NodeConnector> ncList = event.getNodeConnectors();
269 notifyContainerEntryChangeInternal(key, ncList, event.getUpdateType(), false);
270 } else if (value instanceof ContainerFlowChangeEvent) {
271 ContainerFlowChangeEvent event = (ContainerFlowChangeEvent) value;
272 notifyCFlowChangeInternal(key, event.getConfigList(), event.getUpdateType(), false);
273 } else if (value instanceof ContainerChangeEvent) {
274 ContainerChangeEvent event = (ContainerChangeEvent) value;
275 notifyContainerChangeInternal(event.getConfig(), event.getUpdateType(), false);
281 public void entryDeleted(String containerName, String cacheName, boolean originLocal) {
284 public ContainerManager() {
291 public void start() {
292 // Get caches from cluster manager
296 // Allocates default groups and association to default roles
297 createDefaultAuthorizationGroups();
299 // Read startup configuration and create local database
300 loadContainerConfig();
303 public void destroy() {
304 // Clear local states
305 this.iContainerAware.clear();
306 this.iContainerListener.clear();
307 this.iContainerLocalListener.clear();
311 * Adds/Remove the list of flow specs to/from the specified container. This
312 * function is supposed to be called after all the validation checks have
313 * already been run on the proposed configuration.
315 private Status updateContainerFlow(String containerName, List<ContainerFlowConfig> confList, boolean delete) {
316 ContainerData container = getContainerByName(containerName);
317 if (container == null) {
318 return new Status(StatusCode.GONE, "Container not present");
321 for (ContainerFlowConfig conf : confList) {
322 // Validation was fine. Modify the database now.
323 for (Match match : conf.getMatches()) {
324 ContainerFlow cFlow = new ContainerFlow(match);
326 logger.trace("Removing Flow Spec {} from Container {}", conf.getName(), containerName);
327 container.deleteFlowSpec(cFlow);
329 logger.trace("Adding Flow Spec {} to Container {}", conf.getName(), containerName);
330 container.addFlowSpec(cFlow);
334 putContainerDataByName(containerName, container);
337 return new Status(StatusCode.SUCCESS);
341 * Adds/Remove this container to/from the Container database, no updates are going
342 * to be generated here other that the destroying and creation of the container.
343 * This function is supposed to be called after all the validation checks
344 * have already been run on the configuration object
346 private Status updateContainerDatabase(ContainerConfig containerConf, boolean delete) {
348 * Back-end world here, container names are all stored in lower case
350 String containerName = containerConf.getContainerName();
351 ContainerData container = getContainerByName(containerName);
352 if (delete && container == null) {
353 return new Status(StatusCode.NOTFOUND, "Container is not present");
355 if (!delete && container != null) {
356 // A container with the same (lower case) name already exists
357 return new Status(StatusCode.CONFLICT, "A container with the same name already exists");
360 logger.debug("Removing container {}", containerName);
361 removeNodeToContainersMapping(container);
362 removeNodeConnectorToContainersMapping(container);
363 removeContainerDataByName(containerName);
365 logger.debug("Adding container {}", containerName);
366 container = new ContainerData(containerConf);
367 putContainerDataByName(containerName, container);
369 // If flow specs are specified, add them
370 if (containerConf.hasFlowSpecs()) {
371 updateContainerFlow(containerName, containerConf.getContainerFlowConfigs(), delete);
374 // If ports are specified, add them
375 if (!containerConf.getPortList().isEmpty()) {
376 updateContainerEntryDatabase(containerName, containerConf.getPortList(), delete);
379 return new Status(StatusCode.SUCCESS);
382 private void removeNodeConnectorToContainersMapping(ContainerData container) {
383 Iterator<Entry<NodeConnector, CopyOnWriteArrayList<String>>> it = nodeConnectorToContainers.entrySet().iterator();
384 String containerName = container.getContainerName();
385 for (; it.hasNext();) {
386 Entry<NodeConnector, CopyOnWriteArrayList<String>> entry = it.next();
387 final NodeConnector nc = entry.getKey();
388 final CopyOnWriteArrayList<String> slist = entry.getValue();
389 for (final String sdata : slist) {
390 if (sdata.equalsIgnoreCase(containerName)) {
391 logger.debug("Removing NodeConnector->Containers mapping, nodeConnector: {}", nc);
392 slist.remove(containerName);
393 if (slist.isEmpty()) {
394 nodeConnectorToContainers.remove(nc);
396 nodeConnectorToContainers.put(nc, slist);
404 private void removeNodeToContainersMapping(ContainerData container) {
405 for (Entry<Node, Set<String>> entry : nodeToContainers.entrySet()) {
406 Node node = entry.getKey();
407 for (String sdata : entry.getValue()) {
408 if (sdata.equals(container.getContainerName())) {
409 logger.debug("Removing Node->Containers mapping, node {} container {}", node, sdata);
410 Set<String> value = nodeToContainers.get(node);
412 nodeToContainers.put(node, value);
420 * Adds/Remove container data to/from the container. This function is supposed to be
421 * called after all the validation checks have already been run on the
422 * configuration object
424 private Status updateContainerEntryDatabase(String containerName, List<NodeConnector> nodeConnectors, boolean delete) {
425 ContainerData container = getContainerByName(containerName);
427 if (container == null) {
428 return new Status(StatusCode.NOTFOUND, "Container Not Found");
431 // Check changes in the portlist
432 for (NodeConnector port : nodeConnectors) {
433 Node node = port.getNode();
435 container.removePortFromSwitch(port);
436 putContainerDataByName(containerName, container);
438 /* remove <sp> - container mapping */
439 if (nodeConnectorToContainers.containsKey(port)) {
440 nodeConnectorToContainers.remove(port);
443 * If no more ports in the switch, remove switch from container
444 * Generate switchRemoved Event
446 if (container.portListEmpty(node)) {
447 logger.debug("Port List empty for switch {}", node);
448 putContainerDataByName(containerName, container);
449 // remove node->containers mapping
450 Set<String> slist = nodeToContainers.get(node);
452 logger.debug("Removing container from switch-container list. node{}, container{}", node, containerName);
453 slist.remove(container.getContainerName());
454 nodeToContainers.put(node, slist);
455 if (slist.isEmpty()) {
456 logger.debug("Container list empty for switch {}. removing switch-container mapping", node);
457 nodeToContainers.remove(node);
462 if (container.isSwitchInContainer(node) == false) {
463 Set<String> value = nodeToContainers.get(node);
464 // Add node->containers mapping
466 value = new HashSet<String>();
467 logger.debug("Creating new Container Set for switch {}", node);
469 value.add(container.getContainerName());
470 nodeToContainers.put(node, value);
472 container.addPortToSwitch(port);
473 putContainerDataByName(containerName, container);
475 // added nc->containers mapping
476 CopyOnWriteArrayList<String> slist = nodeConnectorToContainers.get(port);
478 slist = new CopyOnWriteArrayList<String>();
480 slist.add(container.getContainerName());
481 nodeConnectorToContainers.put(port, slist);
484 return new Status(StatusCode.SUCCESS);
487 private Status validateContainerFlowAddRemoval(String containerName, ContainerFlow cFlow, boolean delete) {
489 * It used to be the comment below: ~~~~~~~~~~~~~~~~~~~~ If Link Sharing
490 * at Host facing interfaces, then disallow last ContainerFlow removal
491 * ~~~~~~~~~~~~~~~~~~~~ But the interface being host facing is a
492 * condition that can change at runtime and so the final effect will be
493 * unreliable. So now we will always allow the container flow removal,
494 * if this is a link host facing and is shared by many that will cause
495 * issues but that validation should be done not at the configuration
496 * but in the UI/northbound side.
498 ContainerData container = this.getContainerByName(containerName);
499 if (container == null) {
500 String error = String.format("Cannot validate flow specs for container %s: (Container does not exist)", containerName);
502 return new Status(StatusCode.BADREQUEST, error);
506 Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
507 // Go through all the installed containers
508 for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
509 if (containerName.equalsIgnoreCase(entry.getKey())) {
512 // Derive the common ports
513 Set<NodeConnector> commonPorts = entry.getValue().getNodeConnectors();
514 commonPorts.retainAll(thisContainerPorts);
515 if (commonPorts.isEmpty()) {
519 // Check if this operation would remove the only flow spec
520 // assigned to this container
521 if (container.getFlowSpecCount() == 1) {
522 if (!container.hasStaticVlanAssigned()) {
523 // Ports are shared and static vlan is not present: this
525 // regardless the shared ports are host facing or
527 return new Status(StatusCode.BADREQUEST, "Container shares port with another container: "
528 + "The only one flow spec assigned to this container cannot be removed,"
529 + "because this container is not assigned any static vlan");
532 // Check on host facing port
533 ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(
534 ITopologyManager.class, GlobalConstants.DEFAULT.toString(), this);
535 if (topologyManager == null) {
536 return new Status(StatusCode.NOSERVICE,
537 "Cannot validate the request: Required service is not available");
539 for (NodeConnector nc : commonPorts) {
541 * Shared link case : For internal port check if it has
542 * a vlan configured. If vlan is configured, allow the
543 * flowspec to be deleted If the port is host-facing, do
544 * not allow the flowspec to be deleted
546 if (!topologyManager.isInternal(nc)) {
547 return new Status(StatusCode.BADREQUEST, String.format(
548 "Port %s is shared and is host facing port: "
549 + "The only one flow spec assigned to this container cannot be removed", nc));
555 // Adding a new flow spec: need to check if other containers with common
556 // ports do not have same flow spec
557 Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
558 List<ContainerFlow> proposed = new ArrayList<ContainerFlow>(container.getContainerFlowSpecs());
560 for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
561 if (containerName.equalsIgnoreCase(entry.getKey())) {
564 ContainerData otherContainer = entry.getValue();
565 Set<NodeConnector> commonPorts = otherContainer.getNodeConnectors();
566 commonPorts.retainAll(thisContainerPorts);
568 if (!commonPorts.isEmpty()) {
569 Status status = checkCommonContainerFlow(otherContainer.getContainerFlowSpecs(), proposed);
570 if (!status.isSuccess()) {
571 return new Status(StatusCode.BADREQUEST, String.format(
572 "Container %s which shares ports with this container has overlapping flow spec: %s",
573 entry.getKey(), status.getDescription()));
579 return new Status(StatusCode.SUCCESS);
583 * Checks if the passed list of node connectors can be safely applied to the
584 * specified existing container in terms of port sharing with other containers.
586 * @param containerName
587 * the name of the existing container
589 * the list of node connectors to be added to the container
590 * @return the status object representing the result of the check
592 private Status validatePortSharing(String containerName, List<NodeConnector> portList) {
593 ContainerData container = this.getContainerByName(containerName);
594 if (container == null) {
595 String error = String
596 .format("Cannot validate port sharing for container %s: (container does not exist)", containerName);
598 return new Status(StatusCode.BADREQUEST, error);
600 return validatePortSharingInternal(portList, container.getContainerFlowSpecs());
604 * Checks if the proposed container configuration is valid to be applied in
605 * terms of port sharing with other containers.
607 * @param containerConf
608 * the container configuration object containing the list of node
610 * @return the status object representing the result of the check
612 private Status validatePortSharing(ContainerConfig containerConf) {
613 return validatePortSharingInternal(containerConf.getPortList(), containerConf.getContainerFlowSpecs());
617 * If any port is shared with an existing container, need flowSpec to be
618 * configured. If no flowSpec for this or other container, or if containers have any
619 * overlapping flowspec in common, then let the caller know this
620 * configuration has to be rejected.
622 private Status validatePortSharingInternal(List<NodeConnector> portList, List<ContainerFlow> flowSpecList) {
623 for (NodeConnector port : portList) {
624 List<String> slist = nodeConnectorToContainers.get(port);
625 if (slist != null && !slist.isEmpty()) {
626 for (String otherContainerName : slist) {
628 ContainerData other = containerData.get(otherContainerName);
629 if (flowSpecList.isEmpty()) {
630 msg = String.format("Port %s is shared and flow spec is empty for this container", port);
631 } else if (other.isFlowSpecEmpty()) {
632 msg = String.format("Port %s is shared and flow spec is empty for the other container", port);
633 } else if (!checkCommonContainerFlow(flowSpecList, other.getContainerFlowSpecs()).isSuccess()) {
634 msg = String.format("Port %s is shared and other container has common flow spec", port);
638 return new Status(StatusCode.BADREQUEST, msg);
643 return new Status(StatusCode.SUCCESS);
647 * Utility function to check if two lists of container flows share any same
648 * or overlapping container flows.
651 * One of the two lists of container flows to test
653 * One of the two lists of container flows to test
654 * @return The status of the check. Either SUCCESS or CONFLICT. In case of
655 * conflict, the Status will contain the description for the failed
658 private Status checkCommonContainerFlow(List<ContainerFlow> oneFlowList, List<ContainerFlow> twoFlowList) {
659 for (ContainerFlow oneFlow : oneFlowList) {
660 for (ContainerFlow twoFlow : twoFlowList) {
661 if (oneFlow.getMatch().intersetcs(twoFlow.getMatch())) {
662 return new Status(StatusCode.CONFLICT, String.format("Flow Specs overlap: %s %s",
663 oneFlow.getMatch(), twoFlow.getMatch()));
667 return new Status(StatusCode.SUCCESS);
671 * Return the ContainerData object for the passed container name. Given this is a
672 * backend database, the lower case version of the passed name is used while
673 * searching for the corresponding ContainerData object.
676 * The container name in any case
677 * @return The corresponding ContainerData object
679 private ContainerData getContainerByName(String name) {
680 return containerData.get(name.toLowerCase(Locale.ENGLISH));
684 * Add a ContainerData object for the given container name.
687 * The container name in any case
689 * The container data object
691 private void putContainerDataByName(String name, ContainerData sData) {
692 containerData.put(name.toLowerCase(Locale.ENGLISH), sData);
696 * Removes the ContainerData object for the given container name.
699 * The container name in any case
701 private void removeContainerDataByName(String name) {
702 containerData.remove(name.toLowerCase(Locale.ENGLISH));
706 public List<ContainerConfig> getContainerConfigList() {
707 return new ArrayList<ContainerConfig>(containerConfigs.values());
711 public ContainerConfig getContainerConfig(String containerName) {
712 ContainerConfig target = containerConfigs.get(containerName);
713 return (target == null) ? null : new ContainerConfig(target);
717 public List<String> getContainerNameList() {
719 * Return container names as they were configured by user (case sensitive)
720 * along with the default container
722 List<String> containerNameList = new ArrayList<String>();
723 containerNameList.add(GlobalConstants.DEFAULT.toString());
724 containerNameList.addAll(containerConfigs.keySet());
725 return containerNameList;
729 public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
730 Map<String, List<ContainerFlowConfig>> flowSpecConfig = new HashMap<String, List<ContainerFlowConfig>>();
731 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
732 List<ContainerFlowConfig> set = entry.getValue().getContainerFlowConfigs();
733 flowSpecConfig.put(entry.getKey(), set);
735 return flowSpecConfig;
738 private Status saveContainerConfig() {
739 return saveContainerConfigLocal();
742 public Status saveContainerConfigLocal() {
743 Status status = configurationService.persistConfiguration(
744 new ArrayList<ConfigurationObject>(containerConfigs.values()), CONTAINERS_FILE_NAME);
746 if (!status.isSuccess()) {
747 return new Status(StatusCode.INTERNALERROR, "Failed to save container configurations: "
748 + status.getDescription());
754 * Create and initialize default all resource group and create association
755 * with default well known users and profiles, if not already learnt from
756 * another cluster node
758 private void createDefaultAuthorizationGroups() {
759 allResourcesGroupName = ContainerManager.allContainersGroup;
761 // Add the default container to the all containers group if needed
762 String defaultContainer = GlobalConstants.DEFAULT.toString();
763 Set<String> allContainers = (resourceGroups.containsKey(allResourcesGroupName)) ? resourceGroups
764 .get(allResourcesGroupName) : new HashSet<String>();
765 if (!allContainers.contains(defaultContainer)) {
766 // Add Default container
767 allContainers.add(defaultContainer);
769 resourceGroups.put(allResourcesGroupName, allContainers);
772 // Add the controller well known roles, if not known already
773 if (!roles.containsKey(UserLevel.SYSTEMADMIN.toString())) {
774 roles.put(UserLevel.SYSTEMADMIN.toString(), AppRoleLevel.APPADMIN);
776 if (!roles.containsKey(UserLevel.NETWORKADMIN.toString())) {
777 roles.put(UserLevel.NETWORKADMIN.toString(), AppRoleLevel.APPADMIN);
779 if (!roles.containsKey(UserLevel.NETWORKOPERATOR.toString())) {
780 roles.put(UserLevel.NETWORKOPERATOR.toString(), AppRoleLevel.APPOPERATOR);
784 * Create and add the all containers user groups and associate them to the
785 * default well known user roles, if not present already
787 if (!groupsAuthorizations.containsKey(UserLevel.NETWORKADMIN.toString())) {
788 Set<ResourceGroup> writeProfile = new HashSet<ResourceGroup>(1);
789 Set<ResourceGroup> readProfile = new HashSet<ResourceGroup>(1);
790 writeProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.WRITE));
791 readProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.READ));
792 groupsAuthorizations.put(UserLevel.SYSTEMADMIN.toString(), writeProfile);
793 groupsAuthorizations.put(UserLevel.NETWORKADMIN.toString(), writeProfile);
794 groupsAuthorizations.put(UserLevel.NETWORKOPERATOR.toString(), readProfile);
799 * Until manual configuration is not available, automatically maintain the
800 * well known resource groups
802 * @param containerName
805 private void updateResourceGroups(ContainerConfig containerConf, boolean delete) {
806 // Container Roles and Container Resource Group
807 String containerName = containerConf.getContainer();
808 String groupName = containerConf.getContainerGroupName();
809 String containerAdminRole = containerConf.getContainerAdminRole();
810 String containerOperatorRole = containerConf.getContainerOperatorRole();
811 Set<String> allContainerSet = resourceGroups.get(allResourcesGroupName);
813 resourceGroups.remove(groupName);
814 groupsAuthorizations.remove(containerAdminRole);
815 groupsAuthorizations.remove(containerOperatorRole);
816 roles.remove(containerAdminRole);
817 roles.remove(containerOperatorRole);
818 // Update the all container group
819 allContainerSet.remove(containerName);
821 Set<String> resources = new HashSet<String>(1);
822 resources.add(containerName);
823 resourceGroups.put(groupName, resources);
824 Set<ResourceGroup> adminGroups = new HashSet<ResourceGroup>(1);
825 Set<ResourceGroup> operatorGroups = new HashSet<ResourceGroup>(1);
826 adminGroups.add(new ResourceGroup(groupName, Privilege.WRITE));
827 operatorGroups.add(new ResourceGroup(groupName, Privilege.READ));
828 groupsAuthorizations.put(containerAdminRole, adminGroups);
829 groupsAuthorizations.put(containerOperatorRole, operatorGroups);
830 roles.put(containerAdminRole, AppRoleLevel.APPADMIN);
831 roles.put(containerOperatorRole, AppRoleLevel.APPOPERATOR);
832 // Update the all containers resource group
833 allContainerSet.add(containerName);
835 // Update resource groups in cluster
836 resourceGroups.put(allResourcesGroupName, allContainerSet);
840 * Notify ContainerAware listeners of the creation/deletion of the container
842 * @param containerName
844 * true is container was removed, false otherwise
846 private void notifyContainerAwareListeners(String containerName, boolean delete) {
847 // Back-end World: container name forced to lower case
848 String name = containerName.toLowerCase(Locale.ENGLISH);
850 synchronized (this.iContainerAware) {
851 for (IContainerAware i : this.iContainerAware) {
853 i.containerDestroy(name);
855 i.containerCreate(name);
862 * Notify the ContainerListener listeners in case the container mode has
863 * changed following a container configuration operation Note: this call
864 * must happen after the configuration db has been updated
866 * @param lastActionDelete
867 * true if the last container configuration operation was a
868 * container delete operation
870 * if true, the notification is also sent to the
871 * IContainerLocalListener classes besides the IContainerListener
874 private void notifyContainerModeChange(boolean lastActionDelete, boolean notifyLocal) {
875 if (lastActionDelete == false && containerConfigs.size() == 1) {
876 logger.trace("First container Creation. Inform listeners");
877 synchronized (this.iContainerListener) {
878 for (IContainerListener i : this.iContainerListener) {
879 i.containerModeUpdated(UpdateType.ADDED);
883 synchronized (this.iContainerLocalListener) {
884 for (IContainerLocalListener i : this.iContainerLocalListener) {
885 i.containerModeUpdated(UpdateType.ADDED);
889 } else if (lastActionDelete == true && containerConfigs.isEmpty()) {
890 logger.trace("Last container Deletion. Inform listeners");
891 synchronized (this.iContainerListener) {
892 for (IContainerListener i : this.iContainerListener) {
893 i.containerModeUpdated(UpdateType.REMOVED);
897 synchronized (this.iContainerLocalListener) {
898 for (IContainerLocalListener i : this.iContainerLocalListener) {
899 i.containerModeUpdated(UpdateType.REMOVED);
906 private Status addRemoveContainerEntries(String containerName, List<String> nodeConnectorsString, boolean delete) {
907 // Construct action message
908 String action = String.format("Node connector(s) %s container %s: %s", delete ? "removal from" : "addition to",
909 containerName, nodeConnectorsString);
912 if (nodeConnectorsString == null || nodeConnectorsString.isEmpty()) {
913 return new Status(StatusCode.BADREQUEST, "Node connector list is null or empty");
917 ContainerConfig entryConf = containerConfigs.get(containerName);
918 if (entryConf == null) {
919 String msg = String.format("Container not found: %s", containerName);
920 String error = String.format("Failed to apply %s: (%s)", action, msg);
922 return new Status(StatusCode.NOTFOUND, msg);
926 Status status = ContainerConfig.validateNodeConnectors(nodeConnectorsString);
927 if (!status.isSuccess()) {
928 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
933 List<NodeConnector> nodeConnectors = ContainerConfig.nodeConnectorsFromString(nodeConnectorsString);
935 // Port sharing check
938 * Check if the ports being added to this container already belong to
939 * other containers. If so check whether the the appropriate flow specs
940 * are configured on this container
942 status = validatePortSharing(containerName, nodeConnectors);
943 if (!status.isSuccess()) {
944 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
951 status = updateContainerEntryDatabase(containerName, nodeConnectors, delete);
952 if (!status.isSuccess()) {
953 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
958 // Update Configuration
959 status = (delete) ? entryConf.removeNodeConnectors(nodeConnectorsString) : entryConf
960 .addNodeConnectors(nodeConnectorsString);
961 if (!status.isSuccess()) {
962 String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
964 // Revert backend changes
965 Status statusRevert = updateContainerEntryDatabase(containerName, nodeConnectors, !delete);
966 if (!statusRevert.isSuccess()) {
968 logger.error("Failed to revert changes in database (CRITICAL)");
973 // Update cluster Configuration cache
974 containerConfigs.put(containerName, entryConf);
976 // Notify global and local listeners
977 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
978 notifyContainerEntryChangeInternal(containerName, nodeConnectors, update, true);
979 // Trigger cluster notification
980 containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(nodeConnectors, update));
985 private void notifyContainerChangeInternal(ContainerConfig conf, UpdateType update, boolean notifyLocal) {
986 String containerName = conf.getContainerName();
987 logger.trace("Notifying listeners on {} for container {}", update, containerName);
988 // Back-end World: container name forced to lower case
989 String container = containerName.toLowerCase(Locale.ENGLISH);
990 boolean delete = (update == UpdateType.REMOVED);
991 // Check if a container mode change notification is needed
992 notifyContainerModeChange(delete, notifyLocal);
994 notifyContainerAwareListeners(container, delete);
997 private void notifyContainerEntryChangeInternal(String containerName, List<NodeConnector> ncList, UpdateType update, boolean notifyLocal) {
998 logger.trace("Notifying listeners on {} for ports {} in container {}", update, ncList, containerName);
999 // Back-end World: container name forced to lower case
1000 String container = containerName.toLowerCase(Locale.ENGLISH);
1001 for (NodeConnector nodeConnector : ncList) {
1002 // Now signal that the port has been added/removed
1003 synchronized (this.iContainerListener) {
1004 for (IContainerListener i : this.iContainerListener) {
1005 i.nodeConnectorUpdated(container, nodeConnector, update);
1008 // Check if the Functional Modules need to be notified as well
1010 synchronized (this.iContainerLocalListener) {
1011 for (IContainerLocalListener i : this.iContainerLocalListener) {
1012 i.nodeConnectorUpdated(container, nodeConnector, update);
1019 private void notifyCFlowChangeInternal(String containerName, List<ContainerFlowConfig> confList, UpdateType update,
1020 boolean notifyLocal) {
1021 logger.trace("Notifying listeners on {} for flow specs {} in container {}", update, confList, containerName);
1022 // Back-end World: container name forced to lower case
1023 String container = containerName.toLowerCase(Locale.ENGLISH);
1025 for (ContainerFlowConfig conf : confList) {
1026 for (Match match : conf.getMatches()) {
1027 ContainerFlow cFlow = new ContainerFlow(match);
1028 synchronized (this.iContainerListener) {
1029 for (IContainerListener i : this.iContainerListener) {
1030 i.containerFlowUpdated(container, cFlow, cFlow, update);
1033 // Check if the Functional Modules need to be notified as well
1035 synchronized (this.iContainerLocalListener) {
1036 for (IContainerLocalListener i : this.iContainerLocalListener) {
1037 i.containerFlowUpdated(container, cFlow, cFlow, update);
1045 private Status addRemoveContainerFlow(String containerName, List<ContainerFlowConfig> cFlowConfList, boolean delete) {
1046 // Construct action message
1047 String action = String.format("Flow spec(s) %s container %s: %s", delete ? "removal from" : "addition to",
1048 containerName, cFlowConfList);
1051 ContainerConfig containerConfig = this.containerConfigs.get(containerName);
1052 if (containerConfig == null) {
1053 String msg = String.format("Container not found: %s", containerName);
1054 String error = String.format("Failed to apply %s: (%s)", action, msg);
1056 return new Status(StatusCode.NOTFOUND, "Container not present");
1059 // Validity check, check for overlaps on current container configuration
1060 Status status = containerConfig.validateContainerFlowModify(cFlowConfList, delete);
1061 if (!status.isSuccess()) {
1062 String msg = status.getDescription();
1063 String error = String.format("Failed to apply %s: (%s)", action, msg);
1065 return new Status(StatusCode.BADREQUEST, msg);
1068 // Validate the operation in terms to the port sharing with other containers
1069 for (ContainerFlowConfig conf : cFlowConfList) {
1070 for (Match match : conf.getMatches()) {
1071 ContainerFlow cFlow = new ContainerFlow(match);
1072 status = validateContainerFlowAddRemoval(containerName, cFlow, delete);
1073 if (!status.isSuccess()) {
1074 String msg = "Validation failed: " + status.getDescription();
1075 String error = String.format("Failed to apply %s: (%s)", action, msg);
1077 return new Status(StatusCode.BADREQUEST, msg);
1083 status = updateContainerFlow(containerName, cFlowConfList, delete);
1084 if (!status.isSuccess()) {
1085 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
1086 logger.error(error);
1090 // Update Configuration
1091 status = (delete) ? containerConfig.removeContainerFlows(cFlowConfList) : containerConfig
1092 .addContainerFlows(cFlowConfList);
1093 if (!status.isSuccess()) {
1094 String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
1095 logger.error(error);
1096 // Revert backend changes
1097 Status statusRevert = updateContainerFlow(containerName, cFlowConfList, !delete);
1098 if (!statusRevert.isSuccess()) {
1100 logger.error("Failed to revert changes in database (CRITICAL)");
1104 // Update cluster cache
1105 this.containerConfigs.put(containerName, containerConfig);
1107 // Notify global and local listeners
1108 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1109 notifyCFlowChangeInternal(containerName, cFlowConfList, update, true);
1110 // Trigger cluster notification
1111 containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(cFlowConfList, update));
1116 private Status addRemoveContainer(ContainerConfig containerConf, boolean delete) {
1117 // Construct action message
1118 String action = String.format("Container %s", delete ? "removal" : "creation");
1120 // Valid configuration check
1121 Status status = null;
1122 String error = (containerConfigs == null) ? String.format("Invalid %s configuration: (null config object)", action)
1123 : (!(status = containerConf.validate()).isSuccess()) ? String.format("Invalid %s configuration: (%s)",
1124 action, status.getDescription()) : null;
1125 if (error != null) {
1127 return new Status(StatusCode.BADREQUEST, error);
1130 // Configuration presence check
1131 String containerName = containerConf.getContainerName();
1133 if (!containerConfigs.containsKey(containerName)) {
1134 String msg = String.format("%s Failed: (Container does not exist: %s)", action, containerName);
1136 return new Status(StatusCode.NOTFOUND, msg);
1139 if (containerConfigs.containsKey(containerName)) {
1140 String msg = String.format("%s Failed: (Container already exist: %s)", action, containerName);
1142 return new Status(StatusCode.CONFLICT, msg);
1147 * The proposed container configuration could be a complex one containing
1148 * both ports and flow spec. If so, check if it has shared ports with
1149 * other existing containers. If that is the case verify flow spec isolation
1150 * is in place. No need to check on flow spec validation first. This
1151 * would take care of both
1154 status = validatePortSharing(containerConf);
1155 if (!status.isSuccess()) {
1156 error = String.format("%s Failed: (%s)", action, status.getDescription());
1157 logger.error(error);
1163 status = updateContainerDatabase(containerConf, delete);
1165 // Abort and exit here if back-end database update failed
1166 if (!status.isSuccess()) {
1171 * Update Configuration: This will trigger the notifications on cache
1172 * update callback locally and on the other cluster nodes
1175 this.containerConfigs.remove(containerName);
1177 this.containerConfigs.put(containerName, containerConf);
1180 // Automatically create and populate user and resource groups
1181 updateResourceGroups(containerConf, delete);
1183 // Notify global and local listeners
1184 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1185 notifyContainerChangeInternal(containerConf, update, true);
1187 // Trigger cluster notification
1188 containerChangeEvents.put(containerName, new ContainerChangeEvent(containerConf, update));
1190 if (update == UpdateType.ADDED) {
1191 if (containerConf.hasFlowSpecs()) {
1192 List<ContainerFlowConfig> specList = containerConf.getContainerFlowConfigs();
1193 // Notify global and local listeners about flow spec addition
1194 notifyCFlowChangeInternal(containerName, specList, update, true);
1196 // Trigger cluster notification
1197 containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(specList, update));
1200 if (containerConf.hasNodeConnectors()) {
1201 List<NodeConnector> ncList = containerConf.getPortList();
1202 // Notify global and local listeners about port(s) addition
1203 notifyContainerEntryChangeInternal(containerName, ncList, update, true);
1204 // Trigger cluster notification
1205 containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(ncList, update));
1210 clusterServices.removeContainerCaches(containerName);
1216 public Status addContainer(ContainerConfig containerConf) {
1217 return addRemoveContainer(containerConf, false);
1221 public Status removeContainer(ContainerConfig containerConf) {
1222 return addRemoveContainer(containerConf, true);
1226 public Status removeContainer(String containerName) {
1227 // Construct action message
1228 String action = String.format("Container removal: %s", containerName);
1230 ContainerConfig containerConf = containerConfigs.get(containerName);
1231 if (containerConf == null) {
1232 String msg = String.format("Container not found");
1233 String error = String.format("Failed to apply %s: (%s)", action, msg);
1235 return new Status(StatusCode.NOTFOUND, msg);
1237 return addRemoveContainer(containerConf, true);
1241 public Status addContainerEntry(String containerName, List<String> nodeConnectors) {
1242 return addRemoveContainerEntries(containerName, nodeConnectors, false);
1246 public Status removeContainerEntry(String containerName, List<String> nodeConnectors) {
1247 return addRemoveContainerEntries(containerName, nodeConnectors, true);
1251 public Status addContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1252 return addRemoveContainerFlow(containerName, fSpecConf, false);
1256 public Status removeContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1257 return addRemoveContainerFlow(containerName, fSpecConf, true);
1261 public Status removeContainerFlows(String containerName, Set<String> names) {
1262 // Construct action message
1263 String action = String.format("Flow spec(s) removal from container %s: %s", containerName, names);
1266 ContainerConfig sc = containerConfigs.get(containerName);
1268 String msg = String.format("Container not found: %s", containerName);
1269 String error = String.format("Failed to apply %s: (%s)", action, msg);
1271 return new Status(StatusCode.NOTFOUND, msg);
1273 List<ContainerFlowConfig> list = sc.getContainerFlowConfigs(names);
1274 if (list.isEmpty() || list.size() != names.size()) {
1275 String msg = String.format("Cannot find all the specified flow specs");
1276 String error = String.format("Failed to apply %s: (%s)", action, msg);
1278 return new Status(StatusCode.BADREQUEST, msg);
1280 return addRemoveContainerFlow(containerName, list, true);
1284 public List<ContainerFlowConfig> getContainerFlows(String containerName) {
1285 ContainerConfig sc = containerConfigs.get(containerName);
1286 return (sc == null) ? new ArrayList<ContainerFlowConfig>(0) : sc.getContainerFlowConfigs();
1290 public List<String> getContainerFlowNameList(String containerName) {
1291 ContainerConfig sc = containerConfigs.get(containerName);
1292 return (sc == null) ? new ArrayList<String>(0) : sc.getContainerFlowConfigsNames();
1296 public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
1297 // Perform the class deserialization locally, from inside the package
1298 // where the class is defined
1299 return ois.readObject();
1302 private void loadContainerConfig() {
1303 for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, CONTAINERS_FILE_NAME)) {
1304 addContainer((ContainerConfig) conf);
1308 public void _psc(CommandInterpreter ci) {
1309 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1310 ContainerConfig sc = entry.getValue();
1311 ci.println(String.format("%s: %s", sc.getContainerName(), sc.toString()));
1313 ci.println("Total number of containers: " + containerConfigs.entrySet().size());
1316 public void _pfc(CommandInterpreter ci) {
1317 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1318 ContainerConfig sc = entry.getValue();
1319 ci.println(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
1323 public void _psd(CommandInterpreter ci) {
1324 for (String containerName : containerData.keySet()) {
1325 ContainerData sd = containerData.get(containerName);
1326 for (Node sid : sd.getSwPorts().keySet()) {
1327 Set<NodeConnector> s = sd.getSwPorts().get(sid);
1328 ci.println("\t" + sid + " : " + s);
1331 for (ContainerFlow s : sd.getContainerFlowSpecs()) {
1332 ci.println("\t" + s.toString());
1337 public void _psp(CommandInterpreter ci) {
1338 for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
1339 ci.println(nodeConnectorToContainers.get(sp));
1343 public void _psm(CommandInterpreter ci) {
1344 for (Node sp : nodeToContainers.keySet()) {
1345 ci.println(nodeToContainers.get(sp));
1349 public void _addContainer(CommandInterpreter ci) {
1350 String containerName = ci.nextArgument();
1351 if (containerName == null) {
1352 ci.print("Container Name not specified");
1355 String staticVlan = ci.nextArgument();
1356 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
1357 ci.println(this.addRemoveContainer(containerConfig, false));
1360 public void _createContainer(CommandInterpreter ci) {
1361 String containerName = ci.nextArgument();
1362 if (containerName == null) {
1363 ci.print("Container Name not specified");
1366 String staticVlan = ci.nextArgument();
1367 if (staticVlan == null) {
1368 ci.print("Static Vlan not specified");
1371 List<String> ports = new ArrayList<String>();
1372 for (long l = 1L; l < 10L; l++) {
1373 ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
1375 List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
1376 cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
1377 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
1378 ci.println(this.addRemoveContainer(containerConfig, false));
1381 public void _removeContainer(CommandInterpreter ci) {
1382 String containerName = ci.nextArgument();
1383 if (containerName == null) {
1384 ci.print("Container Name not specified");
1387 ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
1388 ci.println(this.addRemoveContainer(containerConfig, true));
1391 public void _addContainerEntry(CommandInterpreter ci) {
1392 String containerName = ci.nextArgument();
1393 if (containerName == null) {
1394 ci.print("Container Name not specified");
1397 String nodeId = ci.nextArgument();
1398 if (nodeId == null) {
1399 ci.print("Node Id not specified");
1402 String portId = ci.nextArgument();
1403 if (portId == null) {
1404 ci.print("Port not specified");
1407 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1408 Short port = Short.valueOf(portId);
1409 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1410 List<String> portList = new ArrayList<String>(1);
1411 portList.add(nc.toString());
1412 ci.println(this.addRemoveContainerEntries(containerName, portList, false));
1415 public void _removeContainerEntry(CommandInterpreter ci) {
1416 String containerName = ci.nextArgument();
1417 if (containerName == null) {
1418 ci.print("Container Name not specified");
1421 String nodeId = ci.nextArgument();
1422 if (nodeId == null) {
1423 ci.print("Node Id not specified");
1426 String portId = ci.nextArgument();
1427 if (portId == null) {
1428 ci.print("Port not specified");
1431 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1432 Short port = Short.valueOf(portId);
1433 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1434 List<String> portList = new ArrayList<String>(1);
1435 portList.add(nc.toString());
1436 ci.println(this.addRemoveContainerEntries(containerName, portList, true));
1439 private ContainerFlowConfig createSampleContainerFlowConfig(String cflowName, boolean boolUnidirectional) {
1440 ContainerFlowConfig cfg = new ContainerFlowConfig(cflowName, "9.9.1.0/24", "19.9.1.2", "TCP", "1234", "25");
1444 public void _addContainerFlow(CommandInterpreter ci) {
1445 String containerName = ci.nextArgument();
1446 if (containerName == null) {
1447 ci.print("Container Name not specified");
1450 String cflowName = ci.nextArgument();
1451 if (cflowName == null) {
1452 ci.print("cflowName not specified");
1455 String unidirectional = ci.nextArgument();
1456 boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
1457 List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
1458 list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
1459 ci.println(this.addRemoveContainerFlow(containerName, list, false));
1462 public void _removeContainerFlow(CommandInterpreter ci) {
1463 String containerName = ci.nextArgument();
1464 if (containerName == null) {
1465 ci.print("Container Name not specified");
1468 String cflowName = ci.nextArgument();
1469 if (cflowName == null) {
1470 ci.print("cflowName not specified");
1473 Set<String> set = new HashSet<String>(1);
1475 ci.println(this.removeContainerFlows(containerName, set));
1479 public String getHelp() {
1480 StringBuffer help = new StringBuffer();
1481 help.append("---ContainerManager Testing---\n");
1482 help.append("\tpsc - Print ContainerConfigs\n");
1483 help.append("\tpfc - Print FlowSpecConfigs\n");
1484 help.append("\tpsd - Print ContainerData\n");
1485 help.append("\tpsp - Print nodeConnectorToContainers\n");
1486 help.append("\tpsm - Print nodeToContainers\n");
1487 help.append("\t addContainer <containerName> <staticVlan> \n");
1488 help.append("\t removeContainer <containerName> \n");
1489 help.append("\t addContainerEntry <containerName> <nodeId> <port> \n");
1490 help.append("\t removeContainerEntry <containerName> <nodeId> <port> \n");
1491 help.append("\t addContainerFlow <containerName> <cflowName> <unidirectional true/false>\n");
1492 help.append("\t removeContainerFlow <containerName> <cflowName> \n");
1493 return help.toString();
1497 public boolean doesContainerExist(String containerName) {
1498 // Test for default container
1499 if (GlobalConstants.DEFAULT.toString().equalsIgnoreCase(containerName)) {
1502 // Test for non-default one
1503 return (getContainerByName(containerName) != null);
1507 public ContainerData getContainerData(String containerName) {
1508 return (getContainerByName(containerName));
1512 public Status saveConfiguration() {
1513 return saveContainerConfig();
1516 public void _containermgrGetRoles(CommandInterpreter ci) {
1517 ci.println("Configured roles for Container Mgr:");
1518 List<String> list = this.getRoles();
1519 for (String role : list) {
1520 ci.println(role + "\t" + roles.get(role));
1524 public void _containermgrGetAuthorizedGroups(CommandInterpreter ci) {
1525 String roleName = ci.nextArgument();
1526 if (roleName == null || roleName.trim().isEmpty()) {
1527 ci.println("Invalid argument");
1528 ci.println("mmGetAuthorizedGroups <role_name>");
1531 ci.println("Resource Groups associated to role " + roleName + ":");
1532 List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
1533 for (ResourceGroup group : list) {
1534 ci.println(group.toString());
1538 public void _containermgrGetAuthorizedResources(CommandInterpreter ci) {
1539 String roleName = ci.nextArgument();
1540 if (roleName == null || roleName.trim().isEmpty()) {
1541 ci.println("Invalid argument");
1542 ci.println("mmGetAuthorizedResources <role_name>");
1545 ci.println("Resource associated to role " + roleName + ":");
1546 List<Resource> list = this.getAuthorizedResources(roleName);
1547 for (Resource resource : list) {
1548 ci.println(resource.toString());
1552 public void _containermgrGetResourcesForGroup(CommandInterpreter ci) {
1553 String groupName = ci.nextArgument();
1554 if (groupName == null || groupName.trim().isEmpty()) {
1555 ci.println("Invalid argument");
1556 ci.println("containermgrResourcesForGroup <group_name>");
1559 ci.println("Group " + groupName + " contains the following resources:");
1560 List<Object> resources = this.getResources(groupName);
1561 for (Object resource : resources) {
1562 ci.println(resource.toString());
1566 public void _containermgrGetUserLevel(CommandInterpreter ci) {
1567 String userName = ci.nextArgument();
1568 if (userName == null || userName.trim().isEmpty()) {
1569 ci.println("Invalid argument");
1570 ci.println("containermgrGetUserLevel <user_name>");
1573 ci.println("User " + userName + " has level: " + this.getUserLevel(userName));
1576 public void _containermgrGetUserResources(CommandInterpreter ci) {
1577 String userName = ci.nextArgument();
1578 if (userName == null || userName.trim().isEmpty()) {
1579 ci.println("Invalid argument");
1580 ci.println("containermgrGetUserResources <user_name>");
1583 ci.println("User " + userName + " owns the following resources: ");
1584 Set<Resource> resources = this.getAllResourcesforUser(userName);
1585 for (Resource resource : resources) {
1586 ci.println(resource.toString());
1591 * For scalability testing where as of now controller gui is unresponsive
1592 * providing here an osgi hook to trigger the save config so that DT do not
1593 * have to reaply the scalable configuration each time they restart the
1596 // TODO: remove when no longer needed
1597 public void _saveConfig(CommandInterpreter ci) {
1598 Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
1600 IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
1601 IConfigurationService.class, this);
1602 if (configService != null) {
1603 status = configService.saveConfigurations();
1605 ci.println(status.toString());
1609 public List<String> getContainerNames() {
1610 return getContainerNameList();
1614 public boolean hasNonDefaultContainer() {
1615 return !containerConfigs.keySet().isEmpty();
1619 public boolean inContainerMode() {
1620 return this.containerConfigs.size() > 0;