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.IContainerManagerShell;
48 import org.opendaylight.controller.containermanager.NodeConnectorsChangeEvent;
49 import org.opendaylight.controller.sal.authorization.AppRoleLevel;
50 import org.opendaylight.controller.sal.authorization.Privilege;
51 import org.opendaylight.controller.sal.authorization.Resource;
52 import org.opendaylight.controller.sal.authorization.ResourceGroup;
53 import org.opendaylight.controller.sal.authorization.UserLevel;
54 import org.opendaylight.controller.sal.core.ContainerFlow;
55 import org.opendaylight.controller.sal.core.IContainerAware;
56 import org.opendaylight.controller.sal.core.IContainerListener;
57 import org.opendaylight.controller.sal.core.IContainerLocalListener;
58 import org.opendaylight.controller.sal.core.Node;
59 import org.opendaylight.controller.sal.core.NodeConnector;
60 import org.opendaylight.controller.sal.core.UpdateType;
61 import org.opendaylight.controller.sal.match.Match;
62 import org.opendaylight.controller.sal.utils.GlobalConstants;
63 import org.opendaylight.controller.sal.utils.IObjectReader;
64 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
65 import org.opendaylight.controller.sal.utils.NodeCreator;
66 import org.opendaylight.controller.sal.utils.ServiceHelper;
67 import org.opendaylight.controller.sal.utils.Status;
68 import org.opendaylight.controller.sal.utils.StatusCode;
69 import org.opendaylight.controller.topologymanager.ITopologyManager;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
73 public class ContainerManager extends Authorization<String> implements IContainerManager, IObjectReader,
74 CommandProvider, ICacheUpdateAware<String, Object>, IContainerInternal, IContainerAuthorization,
75 IConfigurationAware, IContainerManagerShell {
76 private static final Logger logger = LoggerFactory.getLogger(ContainerManager.class);
77 private static String CONTAINERS_FILE_NAME = "containers.conf";
78 private static final String allContainersGroup = "allContainers";
79 private IClusterGlobalServices clusterServices;
80 private IConfigurationService configurationService;
82 * Collection containing the configuration objects. This is configuration
83 * world: container names (also the map key) are maintained as they were
84 * configured by user, same case
86 private ConcurrentMap<String, ContainerConfig> containerConfigs;
87 private ConcurrentMap<String, ContainerData> containerData;
88 private ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>> nodeConnectorToContainers;
89 private ConcurrentMap<Node, Set<String>> nodeToContainers;
90 private ConcurrentMap<String, Object> containerChangeEvents;
91 private final Set<IContainerAware> iContainerAware = Collections.synchronizedSet(new HashSet<IContainerAware>());
92 private final Set<IContainerListener> iContainerListener = Collections
93 .synchronizedSet(new HashSet<IContainerListener>());
94 private final Set<IContainerLocalListener> iContainerLocalListener = Collections
95 .synchronizedSet(new HashSet<IContainerLocalListener>());
97 void setIContainerListener(IContainerListener s) {
98 if (this.iContainerListener != null) {
99 this.iContainerListener.add(s);
101 * At boot with startup, containers are created before listeners have
102 * joined. Replaying here the first container creation notification for
103 * the joining listener when containers are already present. Also
104 * replaying all the node connectors and container flows additions
105 * to the existing containers.
107 if (!this.containerData.isEmpty()) {
108 s.containerModeUpdated(UpdateType.ADDED);
110 for (ConcurrentMap.Entry<NodeConnector, CopyOnWriteArrayList<String>> entry : nodeConnectorToContainers
112 NodeConnector port = entry.getKey();
113 for (String container : entry.getValue()) {
114 s.nodeConnectorUpdated(container, port, UpdateType.ADDED);
117 for (Map.Entry<String, ContainerData> container : containerData.entrySet()) {
118 for (ContainerFlow cFlow : container.getValue().getContainerFlowSpecs()) {
119 s.containerFlowUpdated(container.getKey(), cFlow, cFlow, UpdateType.ADDED);
125 void unsetIContainerListener(IContainerListener s) {
126 if (this.iContainerListener != null) {
127 this.iContainerListener.remove(s);
131 void setIContainerLocalListener(IContainerLocalListener s) {
132 if (this.iContainerLocalListener != null) {
133 this.iContainerLocalListener.add(s);
137 void unsetIContainerLocalListener(IContainerLocalListener s) {
138 if (this.iContainerLocalListener != null) {
139 this.iContainerLocalListener.remove(s);
143 public void setIContainerAware(IContainerAware iContainerAware) {
144 if (!this.iContainerAware.contains(iContainerAware)) {
145 this.iContainerAware.add(iContainerAware);
146 // Now call the container creation for all the known containers so far
147 for (String container : getContainerNameList()) {
148 iContainerAware.containerCreate(container.toLowerCase(Locale.ENGLISH));
153 public void unsetIContainerAware(IContainerAware iContainerAware) {
154 this.iContainerAware.remove(iContainerAware);
155 // There is no need to do cleanup of the component when
156 // unregister because it will be taken care by the Container
160 public void setClusterServices(IClusterGlobalServices i) {
161 this.clusterServices = i;
162 logger.debug("IClusterServices set");
165 public void unsetClusterServices(IClusterGlobalServices i) {
166 if (this.clusterServices == i) {
167 this.clusterServices = null;
168 logger.debug("IClusterServices Unset");
172 public void setConfigurationService(IConfigurationService service) {
173 logger.trace("Got configuration service set request {}", service);
174 this.configurationService = service;
177 public void unsetConfigurationService(IConfigurationService service) {
178 logger.trace("Got configuration service UNset request");
179 this.configurationService = null;
182 private void allocateCaches() {
183 logger.debug("Container Manager allocating caches");
185 if (clusterServices == null) {
186 logger.warn("un-initialized Cluster Services, can't allocate caches");
190 clusterServices.createCache("containermgr.containerConfigs", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
192 clusterServices.createCache("containermgr.event.containerChange",
193 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
195 clusterServices.createCache("containermgr.containerData", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
197 clusterServices.createCache("containermgr.nodeConnectorToContainers",
198 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
200 clusterServices.createCache("containermgr.nodeToContainers", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
202 clusterServices.createCache("containermgr.containerGroups", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
204 clusterServices.createCache("containermgr.containerAuthorizations",
205 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
207 clusterServices.createCache("containermgr.roles", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
208 } catch (CacheConfigException cce) {
209 logger.error("Cache configuration invalid - check cache mode");
210 } catch (CacheExistException ce) {
211 logger.error("Cache already exits - destroy and recreate if needed");
215 @SuppressWarnings({ "unchecked" })
216 private void retrieveCaches() {
217 logger.debug("Container Manager retrieving caches");
219 if (clusterServices == null) {
220 logger.warn("un-initialized Cluster Services, can't retrieve caches");
224 containerConfigs = (ConcurrentMap<String, ContainerConfig>) clusterServices.getCache("containermgr.containerConfigs");
226 containerChangeEvents = (ConcurrentMap<String, Object>) clusterServices.getCache("containermgr.event.containerChange");
228 containerData = (ConcurrentMap<String, ContainerData>) clusterServices.getCache("containermgr.containerData");
230 nodeConnectorToContainers = (ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>>) clusterServices
231 .getCache("containermgr.nodeConnectorToContainers");
233 nodeToContainers = (ConcurrentMap<Node, Set<String>>) clusterServices.getCache("containermgr.nodeToContainers");
235 resourceGroups = (ConcurrentMap<String, Set<String>>) clusterServices.getCache("containermgr.containerGroups");
237 groupsAuthorizations = (ConcurrentMap<String, Set<ResourceGroup>>) clusterServices
238 .getCache("containermgr.containerAuthorizations");
240 roles = (ConcurrentMap<String, AppRoleLevel>) clusterServices.getCache("containermgr.roles");
242 if (inContainerMode()) {
243 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
244 // Notify global and local listeners about the mode change
245 notifyContainerChangeInternal(entry.getValue(), UpdateType.ADDED, true);
251 public void entryCreated(String containerName, String cacheName, boolean originLocal) {
256 public void entryUpdated(String key, Object value, String cacheName, boolean originLocal) {
258 * This is were container manager replays a configuration event that was
259 * notified by its peer from a cluster node where the configuration
260 * happened. Only the global listeners, the cluster unaware classes,
261 * (mainly the shim classes in the sdn protocol plugins) need to receive
262 * these notifications on this cluster node. The cluster aware classes,
263 * like the functional modules which reacts on these events, must _not_
264 * be notified to avoid parallel computation in the cluster.
267 if (value instanceof NodeConnectorsChangeEvent) {
268 NodeConnectorsChangeEvent event = (NodeConnectorsChangeEvent) value;
269 List<NodeConnector> ncList = event.getNodeConnectors();
270 notifyContainerEntryChangeInternal(key, ncList, event.getUpdateType(), false);
271 } else if (value instanceof ContainerFlowChangeEvent) {
272 ContainerFlowChangeEvent event = (ContainerFlowChangeEvent) value;
273 notifyCFlowChangeInternal(key, event.getConfigList(), event.getUpdateType(), false);
274 } else if (value instanceof ContainerChangeEvent) {
275 ContainerChangeEvent event = (ContainerChangeEvent) value;
276 notifyContainerChangeInternal(event.getConfig(), event.getUpdateType(), false);
282 public void entryDeleted(String containerName, String cacheName, boolean originLocal) {
285 public ContainerManager() {
292 public void start() {
293 // Get caches from cluster manager
297 // Allocates default groups and association to default roles
298 createDefaultAuthorizationGroups();
300 // Read startup configuration and create local database
301 loadContainerConfig();
304 public void destroy() {
305 // Clear local states
306 this.iContainerAware.clear();
307 this.iContainerListener.clear();
308 this.iContainerLocalListener.clear();
312 * Adds/Remove the list of flow specs to/from the specified container. This
313 * function is supposed to be called after all the validation checks have
314 * already been run on the proposed configuration.
316 private Status updateContainerFlow(String containerName, List<ContainerFlowConfig> confList, boolean delete) {
317 ContainerData container = getContainerByName(containerName);
318 if (container == null) {
319 return new Status(StatusCode.GONE, "Container not present");
322 for (ContainerFlowConfig conf : confList) {
323 // Validation was fine. Modify the database now.
324 for (Match match : conf.getMatches()) {
325 ContainerFlow cFlow = new ContainerFlow(match);
327 logger.trace("Removing Flow Spec {} from Container {}", conf.getName(), containerName);
328 container.deleteFlowSpec(cFlow);
330 logger.trace("Adding Flow Spec {} to Container {}", conf.getName(), containerName);
331 container.addFlowSpec(cFlow);
335 putContainerDataByName(containerName, container);
338 return new Status(StatusCode.SUCCESS);
342 * Adds/Remove this container to/from the Container database, no updates are going
343 * to be generated here other that the destroying and creation of the container.
344 * This function is supposed to be called after all the validation checks
345 * have already been run on the configuration object
347 private Status updateContainerDatabase(ContainerConfig containerConf, boolean delete) {
349 * Back-end world here, container names are all stored in lower case
351 String containerName = containerConf.getContainerName();
352 ContainerData container = getContainerByName(containerName);
353 if (delete && container == null) {
354 return new Status(StatusCode.NOTFOUND, "Container is not present");
356 if (!delete && container != null) {
357 // A container with the same (lower case) name already exists
358 return new Status(StatusCode.CONFLICT, "A container with the same name already exists");
361 logger.debug("Removing container {}", containerName);
362 removeNodeToContainersMapping(container);
363 removeNodeConnectorToContainersMapping(container);
364 removeContainerDataByName(containerName);
366 logger.debug("Adding container {}", containerName);
367 container = new ContainerData(containerConf);
368 putContainerDataByName(containerName, container);
370 // If flow specs are specified, add them
371 if (containerConf.hasFlowSpecs()) {
372 updateContainerFlow(containerName, containerConf.getContainerFlowConfigs(), delete);
375 // If ports are specified, add them
376 if (!containerConf.getPortList().isEmpty()) {
377 updateContainerEntryDatabase(containerName, containerConf.getPortList(), delete);
380 return new Status(StatusCode.SUCCESS);
383 private void removeNodeConnectorToContainersMapping(ContainerData container) {
384 Iterator<Entry<NodeConnector, CopyOnWriteArrayList<String>>> it = nodeConnectorToContainers.entrySet().iterator();
385 String containerName = container.getContainerName();
386 for (; it.hasNext();) {
387 Entry<NodeConnector, CopyOnWriteArrayList<String>> entry = it.next();
388 final NodeConnector nc = entry.getKey();
389 final CopyOnWriteArrayList<String> slist = entry.getValue();
390 for (final String sdata : slist) {
391 if (sdata.equalsIgnoreCase(containerName)) {
392 logger.debug("Removing NodeConnector->Containers mapping, nodeConnector: {}", nc);
393 slist.remove(containerName);
394 if (slist.isEmpty()) {
395 nodeConnectorToContainers.remove(nc);
397 nodeConnectorToContainers.put(nc, slist);
405 private void removeNodeToContainersMapping(ContainerData container) {
406 for (Entry<Node, Set<String>> entry : nodeToContainers.entrySet()) {
407 Node node = entry.getKey();
408 for (String sdata : entry.getValue()) {
409 if (sdata.equals(container.getContainerName())) {
410 logger.debug("Removing Node->Containers mapping, node {} container {}", node, sdata);
411 Set<String> value = nodeToContainers.get(node);
413 nodeToContainers.put(node, value);
421 * Adds/Remove container data to/from the container. This function is supposed to be
422 * called after all the validation checks have already been run on the
423 * configuration object
425 private Status updateContainerEntryDatabase(String containerName, List<NodeConnector> nodeConnectors, boolean delete) {
426 ContainerData container = getContainerByName(containerName);
428 if (container == null) {
429 return new Status(StatusCode.NOTFOUND, "Container Not Found");
432 // Check changes in the portlist
433 for (NodeConnector port : nodeConnectors) {
434 Node node = port.getNode();
436 container.removePortFromSwitch(port);
437 putContainerDataByName(containerName, container);
439 /* remove <sp> - container mapping */
440 if (nodeConnectorToContainers.containsKey(port)) {
441 nodeConnectorToContainers.remove(port);
444 * If no more ports in the switch, remove switch from container
445 * Generate switchRemoved Event
447 if (container.portListEmpty(node)) {
448 logger.debug("Port List empty for switch {}", node);
449 putContainerDataByName(containerName, container);
450 // remove node->containers mapping
451 Set<String> slist = nodeToContainers.get(node);
453 logger.debug("Removing container from switch-container list. node{}, container{}", node, containerName);
454 slist.remove(container.getContainerName());
455 nodeToContainers.put(node, slist);
456 if (slist.isEmpty()) {
457 logger.debug("Container list empty for switch {}. removing switch-container mapping", node);
458 nodeToContainers.remove(node);
463 if (container.isSwitchInContainer(node) == false) {
464 Set<String> value = nodeToContainers.get(node);
465 // Add node->containers mapping
467 value = new HashSet<String>();
468 logger.debug("Creating new Container Set for switch {}", node);
470 value.add(container.getContainerName());
471 nodeToContainers.put(node, value);
473 container.addPortToSwitch(port);
474 putContainerDataByName(containerName, container);
476 // added nc->containers mapping
477 CopyOnWriteArrayList<String> slist = nodeConnectorToContainers.get(port);
479 slist = new CopyOnWriteArrayList<String>();
481 slist.add(container.getContainerName());
482 nodeConnectorToContainers.put(port, slist);
485 return new Status(StatusCode.SUCCESS);
488 private Status validateContainerFlowAddRemoval(String containerName, ContainerFlow cFlow, boolean delete) {
490 * It used to be the comment below: ~~~~~~~~~~~~~~~~~~~~ If Link Sharing
491 * at Host facing interfaces, then disallow last ContainerFlow removal
492 * ~~~~~~~~~~~~~~~~~~~~ But the interface being host facing is a
493 * condition that can change at runtime and so the final effect will be
494 * unreliable. So now we will always allow the container flow removal,
495 * if this is a link host facing and is shared by many that will cause
496 * issues but that validation should be done not at the configuration
497 * but in the UI/northbound side.
499 ContainerData container = this.getContainerByName(containerName);
500 if (container == null) {
501 String error = String.format("Cannot validate flow specs for container %s: (Container does not exist)", containerName);
503 return new Status(StatusCode.BADREQUEST, error);
507 Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
508 // Go through all the installed containers
509 for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
510 if (containerName.equalsIgnoreCase(entry.getKey())) {
513 // Derive the common ports
514 Set<NodeConnector> commonPorts = entry.getValue().getNodeConnectors();
515 commonPorts.retainAll(thisContainerPorts);
516 if (commonPorts.isEmpty()) {
520 // Check if this operation would remove the only flow spec
521 // assigned to this container
522 if (container.getFlowSpecCount() == 1) {
523 if (!container.hasStaticVlanAssigned()) {
524 // Ports are shared and static vlan is not present: this
526 // regardless the shared ports are host facing or
528 return new Status(StatusCode.BADREQUEST, "Container shares port with another container: "
529 + "The only one flow spec assigned to this container cannot be removed,"
530 + "because this container is not assigned any static vlan");
533 // Check on host facing port
534 ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(
535 ITopologyManager.class, GlobalConstants.DEFAULT.toString(), this);
536 if (topologyManager == null) {
537 return new Status(StatusCode.NOSERVICE,
538 "Cannot validate the request: Required service is not available");
540 for (NodeConnector nc : commonPorts) {
542 * Shared link case : For internal port check if it has
543 * a vlan configured. If vlan is configured, allow the
544 * flowspec to be deleted If the port is host-facing, do
545 * not allow the flowspec to be deleted
547 if (!topologyManager.isInternal(nc)) {
548 return new Status(StatusCode.BADREQUEST, String.format(
549 "Port %s is shared and is host facing port: "
550 + "The only one flow spec assigned to this container cannot be removed", nc));
556 // Adding a new flow spec: need to check if other containers with common
557 // ports do not have same flow spec
558 Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
559 List<ContainerFlow> proposed = new ArrayList<ContainerFlow>(container.getContainerFlowSpecs());
561 for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
562 if (containerName.equalsIgnoreCase(entry.getKey())) {
565 ContainerData otherContainer = entry.getValue();
566 Set<NodeConnector> commonPorts = otherContainer.getNodeConnectors();
567 commonPorts.retainAll(thisContainerPorts);
569 if (!commonPorts.isEmpty()) {
570 Status status = checkCommonContainerFlow(otherContainer.getContainerFlowSpecs(), proposed);
571 if (!status.isSuccess()) {
572 return new Status(StatusCode.BADREQUEST, String.format(
573 "Container %s which shares ports with this container has overlapping flow spec: %s",
574 entry.getKey(), status.getDescription()));
580 return new Status(StatusCode.SUCCESS);
584 * Checks if the passed list of node connectors can be safely applied to the
585 * specified existing container in terms of port sharing with other containers.
587 * @param containerName
588 * the name of the existing container
590 * the list of node connectors to be added to the container
591 * @return the status object representing the result of the check
593 private Status validatePortSharing(String containerName, List<NodeConnector> portList) {
594 ContainerData container = this.getContainerByName(containerName);
595 if (container == null) {
596 String error = String
597 .format("Cannot validate port sharing for container %s: (container does not exist)", containerName);
599 return new Status(StatusCode.BADREQUEST, error);
601 return validatePortSharingInternal(portList, container.getContainerFlowSpecs());
605 * Checks if the proposed container configuration is valid to be applied in
606 * terms of port sharing with other containers.
608 * @param containerConf
609 * the container configuration object containing the list of node
611 * @return the status object representing the result of the check
613 private Status validatePortSharing(ContainerConfig containerConf) {
614 return validatePortSharingInternal(containerConf.getPortList(), containerConf.getContainerFlowSpecs());
618 * If any port is shared with an existing container, need flowSpec to be
619 * configured. If no flowSpec for this or other container, or if containers have any
620 * overlapping flowspec in common, then let the caller know this
621 * configuration has to be rejected.
623 private Status validatePortSharingInternal(List<NodeConnector> portList, List<ContainerFlow> flowSpecList) {
624 for (NodeConnector port : portList) {
625 List<String> slist = nodeConnectorToContainers.get(port);
626 if (slist != null && !slist.isEmpty()) {
627 for (String otherContainerName : slist) {
629 ContainerData other = containerData.get(otherContainerName);
630 if (flowSpecList.isEmpty()) {
631 msg = String.format("Port %s is shared and flow spec is empty for this container", port);
632 } else if (other.isFlowSpecEmpty()) {
633 msg = String.format("Port %s is shared and flow spec is empty for the other container", port);
634 } else if (!checkCommonContainerFlow(flowSpecList, other.getContainerFlowSpecs()).isSuccess()) {
635 msg = String.format("Port %s is shared and other container has common flow spec", port);
639 return new Status(StatusCode.BADREQUEST, msg);
644 return new Status(StatusCode.SUCCESS);
648 * Utility function to check if two lists of container flows share any same
649 * or overlapping container flows.
652 * One of the two lists of container flows to test
654 * One of the two lists of container flows to test
655 * @return The status of the check. Either SUCCESS or CONFLICT. In case of
656 * conflict, the Status will contain the description for the failed
659 private Status checkCommonContainerFlow(List<ContainerFlow> oneFlowList, List<ContainerFlow> twoFlowList) {
660 for (ContainerFlow oneFlow : oneFlowList) {
661 for (ContainerFlow twoFlow : twoFlowList) {
662 if (oneFlow.getMatch().intersetcs(twoFlow.getMatch())) {
663 return new Status(StatusCode.CONFLICT, String.format("Flow Specs overlap: %s %s",
664 oneFlow.getMatch(), twoFlow.getMatch()));
668 return new Status(StatusCode.SUCCESS);
672 * Return the ContainerData object for the passed container name. Given this is a
673 * backend database, the lower case version of the passed name is used while
674 * searching for the corresponding ContainerData object.
677 * The container name in any case
678 * @return The corresponding ContainerData object
680 private ContainerData getContainerByName(String name) {
681 return containerData.get(name.toLowerCase(Locale.ENGLISH));
685 * Add a ContainerData object for the given container name.
688 * The container name in any case
690 * The container data object
692 private void putContainerDataByName(String name, ContainerData sData) {
693 containerData.put(name.toLowerCase(Locale.ENGLISH), sData);
697 * Removes the ContainerData object for the given container name.
700 * The container name in any case
702 private void removeContainerDataByName(String name) {
703 containerData.remove(name.toLowerCase(Locale.ENGLISH));
707 public List<ContainerConfig> getContainerConfigList() {
708 return new ArrayList<ContainerConfig>(containerConfigs.values());
712 public ContainerConfig getContainerConfig(String containerName) {
713 ContainerConfig target = containerConfigs.get(containerName);
714 return (target == null) ? null : new ContainerConfig(target);
718 public List<String> getContainerNameList() {
720 * Return container names as they were configured by user (case sensitive)
721 * along with the default container
723 List<String> containerNameList = new ArrayList<String>();
724 containerNameList.add(GlobalConstants.DEFAULT.toString());
725 containerNameList.addAll(containerConfigs.keySet());
726 return containerNameList;
730 public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
731 Map<String, List<ContainerFlowConfig>> flowSpecConfig = new HashMap<String, List<ContainerFlowConfig>>();
732 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
733 List<ContainerFlowConfig> set = entry.getValue().getContainerFlowConfigs();
734 flowSpecConfig.put(entry.getKey(), set);
736 return flowSpecConfig;
739 private Status saveContainerConfig() {
740 return saveContainerConfigLocal();
743 public Status saveContainerConfigLocal() {
744 Status status = configurationService.persistConfiguration(
745 new ArrayList<ConfigurationObject>(containerConfigs.values()), CONTAINERS_FILE_NAME);
747 if (!status.isSuccess()) {
748 return new Status(StatusCode.INTERNALERROR, "Failed to save container configurations: "
749 + status.getDescription());
755 * Create and initialize default all resource group and create association
756 * with default well known users and profiles, if not already learnt from
757 * another cluster node
759 private void createDefaultAuthorizationGroups() {
760 allResourcesGroupName = ContainerManager.allContainersGroup;
762 // Add the default container to the all containers group if needed
763 String defaultContainer = GlobalConstants.DEFAULT.toString();
764 Set<String> allContainers = (resourceGroups.containsKey(allResourcesGroupName)) ? resourceGroups
765 .get(allResourcesGroupName) : new HashSet<String>();
766 if (!allContainers.contains(defaultContainer)) {
767 // Add Default container
768 allContainers.add(defaultContainer);
770 resourceGroups.put(allResourcesGroupName, allContainers);
773 // Add the controller well known roles, if not known already
774 if (!roles.containsKey(UserLevel.SYSTEMADMIN.toString())) {
775 roles.put(UserLevel.SYSTEMADMIN.toString(), AppRoleLevel.APPADMIN);
777 if (!roles.containsKey(UserLevel.NETWORKADMIN.toString())) {
778 roles.put(UserLevel.NETWORKADMIN.toString(), AppRoleLevel.APPADMIN);
780 if (!roles.containsKey(UserLevel.NETWORKOPERATOR.toString())) {
781 roles.put(UserLevel.NETWORKOPERATOR.toString(), AppRoleLevel.APPOPERATOR);
785 * Create and add the all containers user groups and associate them to the
786 * default well known user roles, if not present already
788 if (!groupsAuthorizations.containsKey(UserLevel.NETWORKADMIN.toString())) {
789 Set<ResourceGroup> writeProfile = new HashSet<ResourceGroup>(1);
790 Set<ResourceGroup> readProfile = new HashSet<ResourceGroup>(1);
791 writeProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.WRITE));
792 readProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.READ));
793 groupsAuthorizations.put(UserLevel.SYSTEMADMIN.toString(), writeProfile);
794 groupsAuthorizations.put(UserLevel.NETWORKADMIN.toString(), writeProfile);
795 groupsAuthorizations.put(UserLevel.NETWORKOPERATOR.toString(), readProfile);
800 * Until manual configuration is not available, automatically maintain the
801 * well known resource groups
803 * @param containerName
806 private void updateResourceGroups(ContainerConfig containerConf, boolean delete) {
807 // Container Roles and Container Resource Group
808 String containerName = containerConf.getContainer();
809 String groupName = containerConf.getContainerGroupName();
810 String containerAdminRole = containerConf.getContainerAdminRole();
811 String containerOperatorRole = containerConf.getContainerOperatorRole();
812 Set<String> allContainerSet = resourceGroups.get(allResourcesGroupName);
814 resourceGroups.remove(groupName);
815 groupsAuthorizations.remove(containerAdminRole);
816 groupsAuthorizations.remove(containerOperatorRole);
817 roles.remove(containerAdminRole);
818 roles.remove(containerOperatorRole);
819 // Update the all container group
820 allContainerSet.remove(containerName);
822 Set<String> resources = new HashSet<String>(1);
823 resources.add(containerName);
824 resourceGroups.put(groupName, resources);
825 Set<ResourceGroup> adminGroups = new HashSet<ResourceGroup>(1);
826 Set<ResourceGroup> operatorGroups = new HashSet<ResourceGroup>(1);
827 adminGroups.add(new ResourceGroup(groupName, Privilege.WRITE));
828 operatorGroups.add(new ResourceGroup(groupName, Privilege.READ));
829 groupsAuthorizations.put(containerAdminRole, adminGroups);
830 groupsAuthorizations.put(containerOperatorRole, operatorGroups);
831 roles.put(containerAdminRole, AppRoleLevel.APPADMIN);
832 roles.put(containerOperatorRole, AppRoleLevel.APPOPERATOR);
833 // Update the all containers resource group
834 allContainerSet.add(containerName);
836 // Update resource groups in cluster
837 resourceGroups.put(allResourcesGroupName, allContainerSet);
841 * Notify ContainerAware listeners of the creation/deletion of the container
843 * @param containerName
845 * true is container was removed, false otherwise
847 private void notifyContainerAwareListeners(String containerName, boolean delete) {
848 // Back-end World: container name forced to lower case
849 String name = containerName.toLowerCase(Locale.ENGLISH);
851 synchronized (this.iContainerAware) {
852 for (IContainerAware i : this.iContainerAware) {
854 i.containerDestroy(name);
856 i.containerCreate(name);
863 * Notify the ContainerListener listeners in case the container mode has
864 * changed following a container configuration operation Note: this call
865 * must happen after the configuration db has been updated
867 * @param lastActionDelete
868 * true if the last container configuration operation was a
869 * container delete operation
871 * if true, the notification is also sent to the
872 * IContainerLocalListener classes besides the IContainerListener
875 private void notifyContainerModeChange(boolean lastActionDelete, boolean notifyLocal) {
876 if (lastActionDelete == false && containerConfigs.size() == 1) {
877 logger.trace("First container Creation. Inform listeners");
878 synchronized (this.iContainerListener) {
879 for (IContainerListener i : this.iContainerListener) {
880 i.containerModeUpdated(UpdateType.ADDED);
884 synchronized (this.iContainerLocalListener) {
885 for (IContainerLocalListener i : this.iContainerLocalListener) {
886 i.containerModeUpdated(UpdateType.ADDED);
890 } else if (lastActionDelete == true && containerConfigs.isEmpty()) {
891 logger.trace("Last container Deletion. Inform listeners");
892 synchronized (this.iContainerListener) {
893 for (IContainerListener i : this.iContainerListener) {
894 i.containerModeUpdated(UpdateType.REMOVED);
898 synchronized (this.iContainerLocalListener) {
899 for (IContainerLocalListener i : this.iContainerLocalListener) {
900 i.containerModeUpdated(UpdateType.REMOVED);
907 private Status addRemoveContainerEntries(String containerName, List<String> nodeConnectorsString, boolean delete) {
908 // Construct action message
909 String action = String.format("Node connector(s) %s container %s: %s", delete ? "removal from" : "addition to",
910 containerName, nodeConnectorsString);
913 if (nodeConnectorsString == null || nodeConnectorsString.isEmpty()) {
914 return new Status(StatusCode.BADREQUEST, "Node connector list is null or empty");
918 ContainerConfig entryConf = containerConfigs.get(containerName);
919 if (entryConf == null) {
920 String msg = String.format("Container not found: %s", containerName);
921 String error = String.format("Failed to apply %s: (%s)", action, msg);
923 return new Status(StatusCode.NOTFOUND, msg);
927 Status status = ContainerConfig.validateNodeConnectors(nodeConnectorsString);
928 if (!status.isSuccess()) {
929 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
934 List<NodeConnector> nodeConnectors = ContainerConfig.nodeConnectorsFromString(nodeConnectorsString);
936 // Port sharing check
939 * Check if the ports being added to this container already belong to
940 * other containers. If so check whether the the appropriate flow specs
941 * are configured on this container
943 status = validatePortSharing(containerName, nodeConnectors);
944 if (!status.isSuccess()) {
945 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
952 status = updateContainerEntryDatabase(containerName, nodeConnectors, delete);
953 if (!status.isSuccess()) {
954 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
959 // Update Configuration
960 status = (delete) ? entryConf.removeNodeConnectors(nodeConnectorsString) : entryConf
961 .addNodeConnectors(nodeConnectorsString);
962 if (!status.isSuccess()) {
963 String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
965 // Revert backend changes
966 Status statusRevert = updateContainerEntryDatabase(containerName, nodeConnectors, !delete);
967 if (!statusRevert.isSuccess()) {
969 logger.error("Failed to revert changes in database (CRITICAL)");
974 // Update cluster Configuration cache
975 containerConfigs.put(containerName, entryConf);
977 // Notify global and local listeners
978 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
979 notifyContainerEntryChangeInternal(containerName, nodeConnectors, update, true);
980 // Trigger cluster notification
981 containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(nodeConnectors, update));
986 private void notifyContainerChangeInternal(ContainerConfig conf, UpdateType update, boolean notifyLocal) {
987 String containerName = conf.getContainerName();
988 logger.trace("Notifying listeners on {} for container {}", update, containerName);
989 // Back-end World: container name forced to lower case
990 String container = containerName.toLowerCase(Locale.ENGLISH);
991 boolean delete = (update == UpdateType.REMOVED);
992 // Check if a container mode change notification is needed
993 notifyContainerModeChange(delete, notifyLocal);
995 notifyContainerAwareListeners(container, delete);
998 private void notifyContainerEntryChangeInternal(String containerName, List<NodeConnector> ncList, UpdateType update, boolean notifyLocal) {
999 logger.trace("Notifying listeners on {} for ports {} in container {}", update, ncList, containerName);
1000 // Back-end World: container name forced to lower case
1001 String container = containerName.toLowerCase(Locale.ENGLISH);
1002 for (NodeConnector nodeConnector : ncList) {
1003 // Now signal that the port has been added/removed
1004 synchronized (this.iContainerListener) {
1005 for (IContainerListener i : this.iContainerListener) {
1006 i.nodeConnectorUpdated(container, nodeConnector, update);
1009 // Check if the Functional Modules need to be notified as well
1011 synchronized (this.iContainerLocalListener) {
1012 for (IContainerLocalListener i : this.iContainerLocalListener) {
1013 i.nodeConnectorUpdated(container, nodeConnector, update);
1020 private void notifyCFlowChangeInternal(String containerName, List<ContainerFlowConfig> confList, UpdateType update,
1021 boolean notifyLocal) {
1022 logger.trace("Notifying listeners on {} for flow specs {} in container {}", update, confList, containerName);
1023 // Back-end World: container name forced to lower case
1024 String container = containerName.toLowerCase(Locale.ENGLISH);
1026 for (ContainerFlowConfig conf : confList) {
1027 for (Match match : conf.getMatches()) {
1028 ContainerFlow cFlow = new ContainerFlow(match);
1029 synchronized (this.iContainerListener) {
1030 for (IContainerListener i : this.iContainerListener) {
1031 i.containerFlowUpdated(container, cFlow, cFlow, update);
1034 // Check if the Functional Modules need to be notified as well
1036 synchronized (this.iContainerLocalListener) {
1037 for (IContainerLocalListener i : this.iContainerLocalListener) {
1038 i.containerFlowUpdated(container, cFlow, cFlow, update);
1046 private Status addRemoveContainerFlow(String containerName, List<ContainerFlowConfig> cFlowConfList, boolean delete) {
1047 // Construct action message
1048 String action = String.format("Flow spec(s) %s container %s: %s", delete ? "removal from" : "addition to",
1049 containerName, cFlowConfList);
1052 ContainerConfig containerConfig = this.containerConfigs.get(containerName);
1053 if (containerConfig == null) {
1054 String msg = String.format("Container not found: %s", containerName);
1055 String error = String.format("Failed to apply %s: (%s)", action, msg);
1057 return new Status(StatusCode.NOTFOUND, "Container not present");
1060 // Validity check, check for overlaps on current container configuration
1061 Status status = containerConfig.validateContainerFlowModify(cFlowConfList, delete);
1062 if (!status.isSuccess()) {
1063 String msg = status.getDescription();
1064 String error = String.format("Failed to apply %s: (%s)", action, msg);
1066 return new Status(StatusCode.BADREQUEST, msg);
1069 // Validate the operation in terms to the port sharing with other containers
1070 for (ContainerFlowConfig conf : cFlowConfList) {
1071 for (Match match : conf.getMatches()) {
1072 ContainerFlow cFlow = new ContainerFlow(match);
1073 status = validateContainerFlowAddRemoval(containerName, cFlow, delete);
1074 if (!status.isSuccess()) {
1075 String msg = "Validation failed: " + status.getDescription();
1076 String error = String.format("Failed to apply %s: (%s)", action, msg);
1078 return new Status(StatusCode.BADREQUEST, msg);
1084 status = updateContainerFlow(containerName, cFlowConfList, delete);
1085 if (!status.isSuccess()) {
1086 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
1087 logger.error(error);
1091 // Update Configuration
1092 status = (delete) ? containerConfig.removeContainerFlows(cFlowConfList) : containerConfig
1093 .addContainerFlows(cFlowConfList);
1094 if (!status.isSuccess()) {
1095 String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
1096 logger.error(error);
1097 // Revert backend changes
1098 Status statusRevert = updateContainerFlow(containerName, cFlowConfList, !delete);
1099 if (!statusRevert.isSuccess()) {
1101 logger.error("Failed to revert changes in database (CRITICAL)");
1105 // Update cluster cache
1106 this.containerConfigs.put(containerName, containerConfig);
1108 // Notify global and local listeners
1109 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1110 notifyCFlowChangeInternal(containerName, cFlowConfList, update, true);
1111 // Trigger cluster notification
1112 containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(cFlowConfList, update));
1117 private Status addRemoveContainer(ContainerConfig containerConf, boolean delete) {
1118 // Construct action message
1119 String action = String.format("Container %s", delete ? "removal" : "creation");
1121 // Valid configuration check
1122 Status status = null;
1123 String error = (containerConfigs == null) ? String.format("Invalid %s configuration: (null config object)", action)
1124 : (!(status = containerConf.validate()).isSuccess()) ? String.format("Invalid %s configuration: (%s)",
1125 action, status.getDescription()) : null;
1126 if (error != null) {
1128 return new Status(StatusCode.BADREQUEST, error);
1131 // Configuration presence check
1132 String containerName = containerConf.getContainerName();
1134 if (!containerConfigs.containsKey(containerName)) {
1135 String msg = String.format("%s Failed: (Container does not exist: %s)", action, containerName);
1137 return new Status(StatusCode.NOTFOUND, msg);
1140 if (containerConfigs.containsKey(containerName)) {
1141 String msg = String.format("%s Failed: (Container already exist: %s)", action, containerName);
1143 return new Status(StatusCode.CONFLICT, msg);
1148 * The proposed container configuration could be a complex one containing
1149 * both ports and flow spec. If so, check if it has shared ports with
1150 * other existing containers. If that is the case verify flow spec isolation
1151 * is in place. No need to check on flow spec validation first. This
1152 * would take care of both
1155 status = validatePortSharing(containerConf);
1156 if (!status.isSuccess()) {
1157 error = String.format("%s Failed: (%s)", action, status.getDescription());
1158 logger.error(error);
1164 status = updateContainerDatabase(containerConf, delete);
1166 // Abort and exit here if back-end database update failed
1167 if (!status.isSuccess()) {
1172 * Update Configuration: This will trigger the notifications on cache
1173 * update callback locally and on the other cluster nodes
1176 this.containerConfigs.remove(containerName);
1178 this.containerConfigs.put(containerName, containerConf);
1181 // Automatically create and populate user and resource groups
1182 updateResourceGroups(containerConf, delete);
1184 // Notify global and local listeners
1185 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1186 notifyContainerChangeInternal(containerConf, update, true);
1188 // Trigger cluster notification
1189 containerChangeEvents.put(containerName, new ContainerChangeEvent(containerConf, update));
1191 if (update == UpdateType.ADDED) {
1192 if (containerConf.hasFlowSpecs()) {
1193 List<ContainerFlowConfig> specList = containerConf.getContainerFlowConfigs();
1194 // Notify global and local listeners about flow spec addition
1195 notifyCFlowChangeInternal(containerName, specList, update, true);
1197 // Trigger cluster notification
1198 containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(specList, update));
1201 if (containerConf.hasNodeConnectors()) {
1202 List<NodeConnector> ncList = containerConf.getPortList();
1203 // Notify global and local listeners about port(s) addition
1204 notifyContainerEntryChangeInternal(containerName, ncList, update, true);
1205 // Trigger cluster notification
1206 containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(ncList, update));
1211 clusterServices.removeContainerCaches(containerName);
1217 public Status addContainer(ContainerConfig containerConf) {
1218 return addRemoveContainer(containerConf, false);
1222 public Status removeContainer(ContainerConfig containerConf) {
1223 return addRemoveContainer(containerConf, true);
1227 public Status removeContainer(String containerName) {
1228 // Construct action message
1229 String action = String.format("Container removal: %s", containerName);
1231 ContainerConfig containerConf = containerConfigs.get(containerName);
1232 if (containerConf == null) {
1233 String msg = String.format("Container not found");
1234 String error = String.format("Failed to apply %s: (%s)", action, msg);
1236 return new Status(StatusCode.NOTFOUND, msg);
1238 return addRemoveContainer(containerConf, true);
1242 public Status addContainerEntry(String containerName, List<String> nodeConnectors) {
1243 return addRemoveContainerEntries(containerName, nodeConnectors, false);
1247 public Status removeContainerEntry(String containerName, List<String> nodeConnectors) {
1248 return addRemoveContainerEntries(containerName, nodeConnectors, true);
1252 public Status addContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1253 return addRemoveContainerFlow(containerName, fSpecConf, false);
1257 public Status removeContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1258 return addRemoveContainerFlow(containerName, fSpecConf, true);
1262 public Status removeContainerFlows(String containerName, Set<String> names) {
1263 // Construct action message
1264 String action = String.format("Flow spec(s) removal from container %s: %s", containerName, names);
1267 ContainerConfig sc = containerConfigs.get(containerName);
1269 String msg = String.format("Container not found: %s", containerName);
1270 String error = String.format("Failed to apply %s: (%s)", action, msg);
1272 return new Status(StatusCode.NOTFOUND, msg);
1274 List<ContainerFlowConfig> list = sc.getContainerFlowConfigs(names);
1275 if (list.isEmpty() || list.size() != names.size()) {
1276 String msg = String.format("Cannot find all the specified flow specs");
1277 String error = String.format("Failed to apply %s: (%s)", action, msg);
1279 return new Status(StatusCode.BADREQUEST, msg);
1281 return addRemoveContainerFlow(containerName, list, true);
1285 public List<ContainerFlowConfig> getContainerFlows(String containerName) {
1286 ContainerConfig sc = containerConfigs.get(containerName);
1287 return (sc == null) ? new ArrayList<ContainerFlowConfig>(0) : sc.getContainerFlowConfigs();
1291 public List<String> getContainerFlowNameList(String containerName) {
1292 ContainerConfig sc = containerConfigs.get(containerName);
1293 return (sc == null) ? new ArrayList<String>(0) : sc.getContainerFlowConfigsNames();
1297 public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
1298 // Perform the class deserialization locally, from inside the package
1299 // where the class is defined
1300 return ois.readObject();
1303 private void loadContainerConfig() {
1304 for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, CONTAINERS_FILE_NAME)) {
1305 addContainer((ContainerConfig) conf);
1309 public void _psc(CommandInterpreter ci) {
1310 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1311 ContainerConfig sc = entry.getValue();
1312 ci.println(String.format("%s: %s", sc.getContainerName(), sc.toString()));
1314 ci.println("Total number of containers: " + containerConfigs.entrySet().size());
1317 public void _pfc(CommandInterpreter ci) {
1318 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1319 ContainerConfig sc = entry.getValue();
1320 ci.println(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
1324 public void _psd(CommandInterpreter ci) {
1325 for (String containerName : containerData.keySet()) {
1326 ContainerData sd = containerData.get(containerName);
1327 for (Node sid : sd.getSwPorts().keySet()) {
1328 Set<NodeConnector> s = sd.getSwPorts().get(sid);
1329 ci.println("\t" + sid + " : " + s);
1332 for (ContainerFlow s : sd.getContainerFlowSpecs()) {
1333 ci.println("\t" + s.toString());
1338 public void _psp(CommandInterpreter ci) {
1339 for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
1340 ci.println(nodeConnectorToContainers.get(sp));
1344 public void _psm(CommandInterpreter ci) {
1345 for (Node sp : nodeToContainers.keySet()) {
1346 ci.println(nodeToContainers.get(sp));
1350 public void _addContainer(CommandInterpreter ci) {
1351 String containerName = ci.nextArgument();
1352 if (containerName == null) {
1353 ci.print("Container Name not specified");
1356 String staticVlan = ci.nextArgument();
1357 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
1358 ci.println(this.addRemoveContainer(containerConfig, false));
1361 public void _createContainer(CommandInterpreter ci) {
1362 String containerName = ci.nextArgument();
1363 if (containerName == null) {
1364 ci.print("Container Name not specified");
1367 String staticVlan = ci.nextArgument();
1368 if (staticVlan == null) {
1369 ci.print("Static Vlan not specified");
1372 List<String> ports = new ArrayList<String>();
1373 for (long l = 1L; l < 10L; l++) {
1374 ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
1376 List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
1377 cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
1378 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
1379 ci.println(this.addRemoveContainer(containerConfig, false));
1382 public void _removeContainer(CommandInterpreter ci) {
1383 String containerName = ci.nextArgument();
1384 if (containerName == null) {
1385 ci.print("Container Name not specified");
1388 ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
1389 ci.println(this.addRemoveContainer(containerConfig, true));
1392 public void _addContainerEntry(CommandInterpreter ci) {
1393 String containerName = ci.nextArgument();
1394 if (containerName == null) {
1395 ci.print("Container Name not specified");
1398 String nodeId = ci.nextArgument();
1399 if (nodeId == null) {
1400 ci.print("Node Id not specified");
1403 String portId = ci.nextArgument();
1404 if (portId == null) {
1405 ci.print("Port not specified");
1408 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1409 Short port = Short.valueOf(portId);
1410 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1411 List<String> portList = new ArrayList<String>(1);
1412 portList.add(nc.toString());
1413 ci.println(this.addRemoveContainerEntries(containerName, portList, false));
1416 public void _removeContainerEntry(CommandInterpreter ci) {
1417 String containerName = ci.nextArgument();
1418 if (containerName == null) {
1419 ci.print("Container Name not specified");
1422 String nodeId = ci.nextArgument();
1423 if (nodeId == null) {
1424 ci.print("Node Id not specified");
1427 String portId = ci.nextArgument();
1428 if (portId == null) {
1429 ci.print("Port not specified");
1432 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1433 Short port = Short.valueOf(portId);
1434 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1435 List<String> portList = new ArrayList<String>(1);
1436 portList.add(nc.toString());
1437 ci.println(this.addRemoveContainerEntries(containerName, portList, true));
1440 private ContainerFlowConfig createSampleContainerFlowConfig(String cflowName, boolean boolUnidirectional) {
1441 ContainerFlowConfig cfg = new ContainerFlowConfig(cflowName, "9.9.1.0/24", "19.9.1.2", "TCP", "1234", "25");
1445 public void _addContainerFlow(CommandInterpreter ci) {
1446 String containerName = ci.nextArgument();
1447 if (containerName == null) {
1448 ci.print("Container Name not specified");
1451 String cflowName = ci.nextArgument();
1452 if (cflowName == null) {
1453 ci.print("cflowName not specified");
1456 String unidirectional = ci.nextArgument();
1457 boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
1458 List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
1459 list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
1460 ci.println(this.addRemoveContainerFlow(containerName, list, false));
1463 public void _removeContainerFlow(CommandInterpreter ci) {
1464 String containerName = ci.nextArgument();
1465 if (containerName == null) {
1466 ci.print("Container Name not specified");
1469 String cflowName = ci.nextArgument();
1470 if (cflowName == null) {
1471 ci.print("cflowName not specified");
1474 Set<String> set = new HashSet<String>(1);
1476 ci.println(this.removeContainerFlows(containerName, set));
1480 public String getHelp() {
1481 StringBuffer help = new StringBuffer();
1482 help.append("---ContainerManager Testing---\n");
1483 help.append("\tpsc - Print ContainerConfigs\n");
1484 help.append("\tpfc - Print FlowSpecConfigs\n");
1485 help.append("\tpsd - Print ContainerData\n");
1486 help.append("\tpsp - Print nodeConnectorToContainers\n");
1487 help.append("\tpsm - Print nodeToContainers\n");
1488 help.append("\t addContainer <containerName> <staticVlan> \n");
1489 help.append("\t removeContainer <containerName> \n");
1490 help.append("\t addContainerEntry <containerName> <nodeId> <port> \n");
1491 help.append("\t removeContainerEntry <containerName> <nodeId> <port> \n");
1492 help.append("\t addContainerFlow <containerName> <cflowName> <unidirectional true/false>\n");
1493 help.append("\t removeContainerFlow <containerName> <cflowName> \n");
1494 return help.toString();
1498 public boolean doesContainerExist(String containerName) {
1499 // Test for default container
1500 if (GlobalConstants.DEFAULT.toString().equalsIgnoreCase(containerName)) {
1503 // Test for non-default one
1504 return (getContainerByName(containerName) != null);
1508 public ContainerData getContainerData(String containerName) {
1509 return (getContainerByName(containerName));
1513 public Status saveConfiguration() {
1514 return saveContainerConfig();
1517 public void _containermgrGetRoles(CommandInterpreter ci) {
1518 ci.println("Configured roles for Container Mgr:");
1519 List<String> list = this.getRoles();
1520 for (String role : list) {
1521 ci.println(role + "\t" + roles.get(role));
1525 public void _containermgrGetAuthorizedGroups(CommandInterpreter ci) {
1526 String roleName = ci.nextArgument();
1527 if (roleName == null || roleName.trim().isEmpty()) {
1528 ci.println("Invalid argument");
1529 ci.println("mmGetAuthorizedGroups <role_name>");
1532 ci.println("Resource Groups associated to role " + roleName + ":");
1533 List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
1534 for (ResourceGroup group : list) {
1535 ci.println(group.toString());
1539 public void _containermgrGetAuthorizedResources(CommandInterpreter ci) {
1540 String roleName = ci.nextArgument();
1541 if (roleName == null || roleName.trim().isEmpty()) {
1542 ci.println("Invalid argument");
1543 ci.println("mmGetAuthorizedResources <role_name>");
1546 ci.println("Resource associated to role " + roleName + ":");
1547 List<Resource> list = this.getAuthorizedResources(roleName);
1548 for (Resource resource : list) {
1549 ci.println(resource.toString());
1553 public void _containermgrGetResourcesForGroup(CommandInterpreter ci) {
1554 String groupName = ci.nextArgument();
1555 if (groupName == null || groupName.trim().isEmpty()) {
1556 ci.println("Invalid argument");
1557 ci.println("containermgrResourcesForGroup <group_name>");
1560 ci.println("Group " + groupName + " contains the following resources:");
1561 List<Object> resources = this.getResources(groupName);
1562 for (Object resource : resources) {
1563 ci.println(resource.toString());
1567 public void _containermgrGetUserLevel(CommandInterpreter ci) {
1568 String userName = ci.nextArgument();
1569 if (userName == null || userName.trim().isEmpty()) {
1570 ci.println("Invalid argument");
1571 ci.println("containermgrGetUserLevel <user_name>");
1574 ci.println("User " + userName + " has level: " + this.getUserLevel(userName));
1577 public void _containermgrGetUserResources(CommandInterpreter ci) {
1578 String userName = ci.nextArgument();
1579 if (userName == null || userName.trim().isEmpty()) {
1580 ci.println("Invalid argument");
1581 ci.println("containermgrGetUserResources <user_name>");
1584 ci.println("User " + userName + " owns the following resources: ");
1585 Set<Resource> resources = this.getAllResourcesforUser(userName);
1586 for (Resource resource : resources) {
1587 ci.println(resource.toString());
1592 * For scalability testing where as of now controller gui is unresponsive
1593 * providing here an osgi hook to trigger the save config so that DT do not
1594 * have to reaply the scalable configuration each time they restart the
1597 // TODO: remove when no longer needed
1598 public void _saveConfig(CommandInterpreter ci) {
1599 Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
1601 IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
1602 IConfigurationService.class, this);
1603 if (configService != null) {
1604 status = configService.saveConfigurations();
1606 ci.println(status.toString());
1610 public List<String> getContainerNames() {
1611 return getContainerNameList();
1615 public boolean hasNonDefaultContainer() {
1616 return !containerConfigs.keySet().isEmpty();
1620 public boolean inContainerMode() {
1621 return this.containerConfigs.size() > 0;
1624 public List<String> psc() {
1625 List<String> result = new ArrayList<String>();
1626 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1627 ContainerConfig sc = entry.getValue();
1628 result.add(String.format("%s: %s", sc.getContainerName(), sc.toString()));
1630 result.add("Total number of containers: " + containerConfigs.entrySet().size());
1634 public List<String> pfc() {
1635 List<String> result = new ArrayList<String>();
1636 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1637 ContainerConfig sc = entry.getValue();
1638 result.add(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
1643 public List<String> psd() {
1644 List<String> result = new ArrayList<String>();
1645 for (String containerName : containerData.keySet()) {
1646 ContainerData sd = containerData.get(containerName);
1647 for (Node sid : sd.getSwPorts().keySet()) {
1648 Set<NodeConnector> s = sd.getSwPorts().get(sid);
1649 result.add("\t" + sid + " : " + s);
1652 for (ContainerFlow s : sd.getContainerFlowSpecs()) {
1653 result.add("\t" + s.toString());
1659 public List<String> psp() {
1660 List<String> result = new ArrayList<String>();
1661 for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
1662 result.add(nodeConnectorToContainers.get(sp).toString());
1667 public List<String> psm() {
1668 List<String> result = new ArrayList<String>();
1669 for (Node sp : nodeToContainers.keySet()) {
1670 result.add(nodeToContainers.get(sp).toString());
1675 public List<String> addContainer(String arg1, String arg2) {
1676 List<String> result = new ArrayList<String>();
1677 String containerName = arg1;
1678 if (containerName == null) {
1679 result.add("Container Name not specified");
1682 String staticVlan = arg2;
1683 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
1684 result.add((this.addRemoveContainer(containerConfig, false)).toString());
1688 public List<String> createContainer(String arg1, String arg2) {
1689 List<String> result = new ArrayList<String>();
1690 String containerName = arg1;
1691 if (containerName == null) {
1692 result.add("Container Name not specified");
1695 String staticVlan = arg2;
1696 if (staticVlan == null) {
1697 result.add("Static Vlan not specified");
1700 List<String> ports = new ArrayList<String>();
1701 for (long l = 1L; l < 10L; l++) {
1702 ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
1704 List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
1705 cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
1706 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
1707 result.add((this.addRemoveContainer(containerConfig, false)).toString());
1711 public List<String> removeContainerShell(String arg1) {
1712 List<String> result = new ArrayList<String>();
1713 String containerName = arg1;
1714 if (containerName == null) {
1715 result.add("Container Name not specified");
1718 ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
1719 result.add((this.addRemoveContainer(containerConfig, true)).toString());
1723 public List<String> addContainerEntry(String arg1, String arg2, String arg3) {
1724 List<String> result = new ArrayList<String>();
1725 String containerName = arg1;
1726 if (containerName == null) {
1727 result.add("Container Name not specified");
1730 String nodeId = arg2;
1731 if (nodeId == null) {
1732 result.add("Node Id not specified");
1735 String portId = arg3;
1736 if (portId == null) {
1737 result.add("Port not specified");
1740 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1741 Short port = Short.valueOf(portId);
1742 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1743 List<String> portList = new ArrayList<String>(1);
1744 portList.add(nc.toString());
1745 result.add((this.addRemoveContainerEntries(containerName, portList, false)).toString());
1749 public List<String> removeContainerEntry(String arg1, String arg2, String arg3) {
1750 List<String> result = new ArrayList<String>();
1751 String containerName = arg1;
1752 if (containerName == null) {
1753 result.add("Container Name not specified");
1756 String nodeId = arg2;
1757 if (nodeId == null) {
1758 result.add("Node Id not specified");
1761 String portId = arg3;
1762 if (portId == null) {
1763 result.add("Port not specified");
1766 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1767 Short port = Short.valueOf(portId);
1768 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1769 List<String> portList = new ArrayList<String>(1);
1770 portList.add(nc.toString());
1771 result.add((this.addRemoveContainerEntries(containerName, portList, true)).toString());
1774 public List<String> addContainerFlow(String arg1, String arg2, String arg3) {
1775 List<String> result = new ArrayList<String>();
1776 String containerName = arg1;
1777 if (containerName == null) {
1778 result.add("Container Name not specified");
1781 String cflowName = arg2;
1782 if (cflowName == null) {
1783 result.add("cflowName not specified");
1786 String unidirectional = arg3;
1787 boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
1788 List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
1789 list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
1790 result.add((this.addRemoveContainerFlow(containerName, list, false)).toString());
1794 public List<String> removeContainerFlow(String arg1, String arg2) {
1795 List<String> result = new ArrayList<String>();
1796 String containerName = arg1;
1797 if (containerName == null) {
1798 result.add("Container Name not specified");
1801 String cflowName = arg2;
1802 if (cflowName == null) {
1803 result.add("cflowName not specified");
1806 Set<String> set = new HashSet<String>(1);
1808 result.add((this.removeContainerFlows(containerName, set)).toString());
1812 public List<String> containermgrGetRoles() {
1813 List<String> result = new ArrayList<String>();
1814 result.add("Configured roles for Container Mgr:");
1815 List<String> list = this.getRoles();
1816 for (String role : list) {
1817 result.add(role + "\t" + roles.get(role));
1822 public List<String> containermgrGetAuthorizedGroups(String arg1) {
1823 List<String> result = new ArrayList<String>();
1824 String roleName = arg1;
1825 if (roleName == null || roleName.trim().isEmpty()) {
1826 result.add("Invalid argument");
1827 result.add("mmGetAuthorizedGroups <role_name>");
1830 result.add("Resource Groups associated to role " + roleName + ":");
1831 List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
1832 for (ResourceGroup group : list) {
1833 result.add(group.toString());
1837 public List<String> containermgrGetAuthorizedResources(String arg1) {
1838 List<String> result = new ArrayList<String>();
1839 String roleName = arg1;
1840 if (roleName == null || roleName.trim().isEmpty()) {
1841 result.add("Invalid argument");
1842 result.add("mmGetAuthorizedResources <role_name>");
1845 result.add("Resource associated to role " + roleName + ":");
1846 List<Resource> list = this.getAuthorizedResources(roleName);
1847 for (Resource resource : list) {
1848 result.add(resource.toString());
1852 public List<String> containermgrGetResourcesForGroup(String arg1) {
1853 List<String> result = new ArrayList<String>();
1854 String groupName = arg1;
1855 if (groupName == null || groupName.trim().isEmpty()) {
1856 result.add("Invalid argument");
1857 result.add("containermgrResourcesForGroup <group_name>");
1860 result.add("Group " + groupName + " contains the following resources:");
1861 List<Object> resources = this.getResources(groupName);
1862 for (Object resource : resources) {
1863 result.add(resource.toString());
1867 public List<String> containermgrGetUserLevel(String arg1) {
1868 List<String> result = new ArrayList<String>();
1869 String userName = arg1;
1870 if (userName == null || userName.trim().isEmpty()) {
1871 result.add("Invalid argument");
1872 result.add("containermgrGetUserLevel <user_name>");
1875 result.add("User " + userName + " has level: " + this.getUserLevel(userName));
1878 public List<String> containermgrGetUserResources(String arg1) {
1879 List<String> result = new ArrayList<String>();
1880 String userName = arg1;
1881 if (userName == null || userName.trim().isEmpty()) {
1882 result.add("Invalid argument");
1883 result.add("containermgrGetUserResources <user_name>");
1886 result.add("User " + userName + " owns the following resources: ");
1887 Set<Resource> resources = this.getAllResourcesforUser(userName);
1888 for (Resource resource : resources) {
1889 result.add(resource.toString());
1893 public List<String> saveConfig() {
1894 List<String> result = new ArrayList<String>();
1895 Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
1897 IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
1898 IConfigurationService.class, this);
1899 if (configService != null) {
1900 status = configService.saveConfigurations();
1902 result.add(status.toString());