3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.containermanager.internal;
13 import java.io.FileNotFoundException;
14 import java.io.IOException;
15 import java.io.ObjectInputStream;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.EnumSet;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Locale;
25 import java.util.Map.Entry;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.CopyOnWriteArrayList;
31 import org.eclipse.osgi.framework.console.CommandInterpreter;
32 import org.eclipse.osgi.framework.console.CommandProvider;
33 import org.opendaylight.controller.clustering.services.CacheConfigException;
34 import org.opendaylight.controller.clustering.services.CacheExistException;
35 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
36 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
37 import org.opendaylight.controller.clustering.services.IClusterServices;
38 import org.opendaylight.controller.configuration.IConfigurationAware;
39 import org.opendaylight.controller.configuration.IConfigurationService;
40 import org.opendaylight.controller.containermanager.IContainerAuthorization;
41 import org.opendaylight.controller.containermanager.IContainerManager;
42 import org.opendaylight.controller.sal.authorization.AppRoleLevel;
43 import org.opendaylight.controller.sal.authorization.Privilege;
44 import org.opendaylight.controller.sal.authorization.Resource;
45 import org.opendaylight.controller.sal.authorization.ResourceGroup;
46 import org.opendaylight.controller.sal.authorization.UserLevel;
47 import org.opendaylight.controller.sal.core.ContainerFlow;
48 import org.opendaylight.controller.sal.core.IContainerAware;
49 import org.opendaylight.controller.sal.core.IContainerListener;
50 import org.opendaylight.controller.sal.core.Node;
51 import org.opendaylight.controller.sal.core.NodeConnector;
52 import org.opendaylight.controller.sal.core.UpdateType;
53 import org.opendaylight.controller.sal.match.Match;
54 import org.opendaylight.controller.sal.utils.GlobalConstants;
55 import org.opendaylight.controller.sal.utils.IObjectReader;
56 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
57 import org.opendaylight.controller.sal.utils.NodeCreator;
58 import org.opendaylight.controller.sal.utils.ObjectReader;
59 import org.opendaylight.controller.sal.utils.ObjectWriter;
60 import org.opendaylight.controller.sal.utils.ServiceHelper;
61 import org.opendaylight.controller.sal.utils.Status;
62 import org.opendaylight.controller.sal.utils.StatusCode;
63 import org.opendaylight.controller.topologymanager.ITopologyManager;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
67 import org.opendaylight.controller.appauth.authorization.Authorization;
68 import org.opendaylight.controller.containermanager.ContainerFlowChangeEvent;
69 import org.opendaylight.controller.containermanager.ContainerFlowConfig;
70 import org.opendaylight.controller.containermanager.NodeConnectorsChangeEvent;
71 import org.opendaylight.controller.containermanager.ContainerChangeEvent;
72 import org.opendaylight.controller.containermanager.ContainerConfig;
73 import org.opendaylight.controller.containermanager.ContainerData;
75 public class ContainerManager extends Authorization<String> implements IContainerManager, IObjectReader,
76 CommandProvider, ICacheUpdateAware<String, Object>, IContainerInternal, IContainerAuthorization,
78 private static final Logger logger = LoggerFactory.getLogger(ContainerManager.class);
79 private static String ROOT = GlobalConstants.STARTUPHOME.toString();
80 private static String containersFileName = ROOT + "containers.conf";
81 private static final String allContainersGroup = "allContainers";
82 private IClusterGlobalServices clusterServices;
84 * Collection containing the configuration objects. This is configuration
85 * world: container names (also the map key) are maintained as they were
86 * configured by user, same case
88 private ConcurrentMap<String, ContainerConfig> containerConfigs;
89 private ConcurrentMap<String, ContainerData> containerData;
90 private ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>> nodeConnectorToContainers;
91 private ConcurrentMap<Node, Set<String>> nodeToContainers;
92 private ConcurrentMap<String, Object> containerChangeEvents;
93 private final Set<IContainerAware> iContainerAware = Collections.synchronizedSet(new HashSet<IContainerAware>());
94 private final Set<IContainerListener> iContainerListener = Collections
95 .synchronizedSet(new HashSet<IContainerListener>());
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 public void setIContainerAware(IContainerAware iContainerAware) {
132 if (!this.iContainerAware.contains(iContainerAware)) {
133 this.iContainerAware.add(iContainerAware);
134 // Now call the container creation for all the known containers so far
135 for (String container : getContainerNameList()) {
136 iContainerAware.containerCreate(container.toLowerCase(Locale.ENGLISH));
141 public void unsetIContainerAware(IContainerAware iContainerAware) {
142 this.iContainerAware.remove(iContainerAware);
143 // There is no need to do cleanup of the component when
144 // unregister because it will be taken care by the Containerd
148 public void setClusterServices(IClusterGlobalServices i) {
149 this.clusterServices = i;
150 logger.debug("IClusterServices set");
153 public void unsetClusterServices(IClusterGlobalServices i) {
154 if (this.clusterServices == i) {
155 this.clusterServices = null;
156 logger.debug("IClusterServices Unset");
160 private void allocateCaches() {
161 logger.debug("Container Manager allocating caches");
163 if (clusterServices == null) {
164 logger.warn("un-initialized Cluster Services, can't allocate caches");
168 clusterServices.createCache("containermgr.containerConfigs", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
170 clusterServices.createCache("containermgr.event.containerChange",
171 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
173 clusterServices.createCache("containermgr.containerData", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
175 clusterServices.createCache("containermgr.nodeConnectorToContainers",
176 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
178 clusterServices.createCache("containermgr.nodeToContainers", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
180 clusterServices.createCache("containermgr.containerGroups", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
182 clusterServices.createCache("containermgr.containerAuthorizations",
183 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
185 clusterServices.createCache("containermgr.roles", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
186 } catch (CacheConfigException cce) {
187 logger.error("Cache configuration invalid - check cache mode");
188 } catch (CacheExistException ce) {
189 logger.error("Cache already exits - destroy and recreate if needed");
193 @SuppressWarnings({ "unchecked" })
194 private void retrieveCaches() {
195 logger.debug("Container Manager retrieving caches");
197 if (clusterServices == null) {
198 logger.warn("un-initialized Cluster Services, can't retrieve caches");
202 containerConfigs = (ConcurrentMap<String, ContainerConfig>) clusterServices.getCache("containermgr.containerConfigs");
204 containerChangeEvents = (ConcurrentMap<String, Object>) clusterServices.getCache("containermgr.event.containerChange");
206 containerData = (ConcurrentMap<String, ContainerData>) clusterServices.getCache("containermgr.containerData");
208 nodeConnectorToContainers = (ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>>) clusterServices
209 .getCache("containermgr.nodeConnectorToContainers");
211 nodeToContainers = (ConcurrentMap<Node, Set<String>>) clusterServices.getCache("containermgr.nodeToContainers");
213 resourceGroups = (ConcurrentMap<String, Set<String>>) clusterServices.getCache("containermgr.containerGroups");
215 groupsAuthorizations = (ConcurrentMap<String, Set<ResourceGroup>>) clusterServices
216 .getCache("containermgr.containerAuthorizations");
218 roles = (ConcurrentMap<String, AppRoleLevel>) clusterServices.getCache("containermgr.roles");
220 if (containerConfigs.size() > 0) {
221 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
222 notifyContainerChangeInternal(entry.getValue(), UpdateType.ADDED);
228 public void entryCreated(String containerName, String cacheName, boolean originLocal) {
233 public void entryUpdated(String key, Object value, String cacheName, boolean originLocal) {
235 if (value instanceof NodeConnectorsChangeEvent) {
236 NodeConnectorsChangeEvent event = (NodeConnectorsChangeEvent) value;
237 List<NodeConnector> ncList = event.getNodeConnectors();
238 notifyContainerEntryChangeInternal(key, ncList, event.getUpdateType());
239 } else if (value instanceof ContainerFlowChangeEvent) {
240 ContainerFlowChangeEvent event = (ContainerFlowChangeEvent) value;
241 notifyCFlowChangeInternal(key, event.getConfigList(), event.getUpdateType());
242 } else if (value instanceof ContainerChangeEvent) {
243 ContainerChangeEvent event = (ContainerChangeEvent) value;
244 notifyContainerChangeInternal(event.getConfig(), event.getUpdateType());
250 public void entryDeleted(String containerName, String cacheName, boolean originLocal) {
253 public ContainerManager() {
260 public void start() {
261 // Get caches from cluster manager
265 // Allocates default groups and association to default roles
266 createDefaultAuthorizationGroups();
268 // Read startup configuration and create local database
269 loadConfigurations();
272 public void destroy() {
273 // Clear local states
274 this.iContainerAware.clear();
275 this.iContainerListener.clear();
279 * Adds/Remove the list of flow specs to/from the specified container. This
280 * function is supposed to be called after all the validation checks have
281 * already been run on the proposed configuration.
283 private Status updateContainerFlow(String containerName, List<ContainerFlowConfig> confList, boolean delete) {
284 ContainerData container = getContainerByName(containerName);
285 if (container == null) {
286 return new Status(StatusCode.GONE, "Container not present");
289 for (ContainerFlowConfig conf : confList) {
290 // Validation was fine. Modify the database now.
291 for (Match match : conf.getMatches()) {
292 ContainerFlow cFlow = new ContainerFlow(match);
294 logger.trace("Removing Flow Spec {} from Container {}", conf.getName(), containerName);
295 container.deleteFlowSpec(cFlow);
297 logger.trace("Adding Flow Spec {} to Container {}", conf.getName(), containerName);
298 container.addFlowSpec(cFlow);
302 putContainerDataByName(containerName, container);
305 return new Status(StatusCode.SUCCESS);
309 * Adds/Remove this container to/from the Container database, no updates are going
310 * to be generated here other that the destroying and creation of the container.
311 * This function is supposed to be called after all the validation checks
312 * have already been run on the configuration object
314 private Status updateContainerDatabase(ContainerConfig containerConf, boolean delete) {
316 * Back-end world here, container names are all stored in lower case
318 String containerName = containerConf.getContainerName();
319 ContainerData container = getContainerByName(containerName);
320 if (delete && container == null) {
321 return new Status(StatusCode.NOTFOUND, "Container is not present");
323 if (!delete && container != null) {
324 // A container with the same (lower case) name already exists
325 return new Status(StatusCode.CONFLICT, "A container with the same name already exists");
328 logger.debug("Removing container {}", containerName);
329 removeNodeToContainersMapping(container);
330 removeNodeConnectorToContainersMapping(container);
331 removeContainerDataByName(containerName);
333 logger.debug("Adding container {}", containerName);
334 container = new ContainerData(containerConf);
335 putContainerDataByName(containerName, container);
337 // If flow specs are specified, add them
338 if (containerConf.hasFlowSpecs()) {
339 updateContainerFlow(containerName, containerConf.getContainerFlowConfigs(), delete);
342 // If ports are specified, add them
343 if (!containerConf.getPortList().isEmpty()) {
344 updateContainerEntryDatabase(containerName, containerConf.getPortList(), delete);
347 return new Status(StatusCode.SUCCESS);
350 private void removeNodeConnectorToContainersMapping(ContainerData container) {
351 Iterator<Entry<NodeConnector, CopyOnWriteArrayList<String>>> it = nodeConnectorToContainers.entrySet().iterator();
352 String containerName = container.getContainerName();
353 for (; it.hasNext();) {
354 Entry<NodeConnector, CopyOnWriteArrayList<String>> entry = it.next();
355 final NodeConnector nc = entry.getKey();
356 final CopyOnWriteArrayList<String> slist = entry.getValue();
357 for (final String sdata : slist) {
358 if (sdata.equalsIgnoreCase(containerName)) {
359 logger.debug("Removing NodeConnector->Containers mapping, nodeConnector: {}", nc);
360 slist.remove(containerName);
361 if (slist.isEmpty()) {
362 nodeConnectorToContainers.remove(nc);
364 nodeConnectorToContainers.put(nc, slist);
372 private void removeNodeToContainersMapping(ContainerData container) {
373 for (Entry<Node, Set<String>> entry : nodeToContainers.entrySet()) {
374 Node node = entry.getKey();
375 for (String sdata : entry.getValue()) {
376 if (sdata.equals(container.getContainerName())) {
377 logger.debug("Removing Node->Containers mapping, node {} container {}", node, sdata);
378 Set<String> value = nodeToContainers.get(node);
380 nodeToContainers.put(node, value);
388 * Adds/Remove container data to/from the container. This function is supposed to be
389 * called after all the validation checks have already been run on the
390 * configuration object
392 private Status updateContainerEntryDatabase(String containerName, List<NodeConnector> nodeConnectors, boolean delete) {
393 ContainerData container = getContainerByName(containerName);
395 if (container == null) {
396 return new Status(StatusCode.NOTFOUND, "Container Not Found");
399 // Check changes in the portlist
400 for (NodeConnector port : nodeConnectors) {
401 Node node = port.getNode();
403 container.removePortFromSwitch(port);
404 putContainerDataByName(containerName, container);
406 /* remove <sp> - container mapping */
407 if (nodeConnectorToContainers.containsKey(port)) {
408 nodeConnectorToContainers.remove(port);
411 * If no more ports in the switch, remove switch from container
412 * Generate switchRemoved Event
414 if (container.portListEmpty(node)) {
415 logger.debug("Port List empty for switch {}", node);
416 putContainerDataByName(containerName, container);
417 // remove node->containers mapping
418 Set<String> slist = nodeToContainers.get(node);
420 logger.debug("Removing container from switch-container list. node{}, container{}", node, containerName);
421 slist.remove(container.getContainerName());
422 nodeToContainers.put(node, slist);
423 if (slist.isEmpty()) {
424 logger.debug("Container list empty for switch {}. removing switch-container mapping", node);
425 nodeToContainers.remove(node);
430 if (container.isSwitchInContainer(node) == false) {
431 Set<String> value = nodeToContainers.get(node);
432 // Add node->containers mapping
434 value = new HashSet<String>();
435 logger.debug("Creating new Container Set for switch {}", node);
437 value.add(container.getContainerName());
438 nodeToContainers.put(node, value);
440 container.addPortToSwitch(port);
441 putContainerDataByName(containerName, container);
443 // added nc->containers mapping
444 CopyOnWriteArrayList<String> slist = nodeConnectorToContainers.get(port);
446 slist = new CopyOnWriteArrayList<String>();
448 slist.add(container.getContainerName());
449 nodeConnectorToContainers.put(port, slist);
452 return new Status(StatusCode.SUCCESS);
455 private Status validateContainerFlowAddRemoval(String containerName, ContainerFlow cFlow, boolean delete) {
457 * It used to be the comment below: ~~~~~~~~~~~~~~~~~~~~ If Link Sharing
458 * at Host facing interfaces, then disallow last ContainerFlow removal
459 * ~~~~~~~~~~~~~~~~~~~~ But the interface being host facing is a
460 * condition that can change at runtime and so the final effect will be
461 * unreliable. So now we will always allow the container flow removal,
462 * if this is a link host facing and is shared by many that will cause
463 * issues but that validation should be done not at the configuration
464 * but in the UI/northbound side.
466 ContainerData container = this.getContainerByName(containerName);
467 if (container == null) {
468 String error = String.format("Cannot validate flow specs for container %s: (Container does not exist)", containerName);
470 return new Status(StatusCode.BADREQUEST, error);
474 Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
475 // Go through all the installed containers
476 for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
477 if (containerName.equalsIgnoreCase(entry.getKey())) {
480 // Derive the common ports
481 Set<NodeConnector> commonPorts = entry.getValue().getNodeConnectors();
482 commonPorts.retainAll(thisContainerPorts);
483 if (commonPorts.isEmpty()) {
487 // Check if this operation would remove the only flow spec
488 // assigned to this container
489 if (container.getFlowSpecCount() == 1) {
490 if (!container.hasStaticVlanAssigned()) {
491 // Ports are shared and static vlan is not present: this
493 // regardless the shared ports are host facing or
495 return new Status(StatusCode.BADREQUEST, "Container shares port with another container: "
496 + "The only one flow spec assigned to this container cannot be removed,"
497 + "because this container is not assigned any static vlan");
500 // Check on host facing port
501 ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(
502 ITopologyManager.class, GlobalConstants.DEFAULT.toString(), this);
503 if (topologyManager == null) {
504 return new Status(StatusCode.NOSERVICE,
505 "Cannot validate the request: Required service is not available");
507 for (NodeConnector nc : commonPorts) {
509 * Shared link case : For internal port check if it has
510 * a vlan configured. If vlan is configured, allow the
511 * flowspec to be deleted If the port is host-facing, do
512 * not allow the flowspec to be deleted
514 if (!topologyManager.isInternal(nc)) {
515 return new Status(StatusCode.BADREQUEST, String.format(
516 "Port %s is shared and is host facing port: "
517 + "The only one flow spec assigned to this container cannot be removed", nc));
523 // Adding a new flow spec: need to check if other containers with common
524 // ports do not have same flow spec
525 Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
526 List<ContainerFlow> proposed = new ArrayList<ContainerFlow>(container.getContainerFlowSpecs());
528 for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
529 if (containerName.equalsIgnoreCase(entry.getKey())) {
532 ContainerData otherContainer = entry.getValue();
533 Set<NodeConnector> commonPorts = otherContainer.getNodeConnectors();
534 commonPorts.retainAll(thisContainerPorts);
536 if (!commonPorts.isEmpty()) {
537 Status status = checkCommonContainerFlow(otherContainer.getContainerFlowSpecs(), proposed);
538 if (!status.isSuccess()) {
539 return new Status(StatusCode.BADREQUEST, String.format(
540 "Container %s which shares ports with this container has overlapping flow spec: %s",
541 entry.getKey(), status.getDescription()));
547 return new Status(StatusCode.SUCCESS);
551 * Checks if the passed list of node connectors can be safely applied to the
552 * specified existing container in terms of port sharing with other containers.
554 * @param containerName
555 * the name of the existing container
557 * the list of node connectors to be added to the container
558 * @return the status object representing the result of the check
560 private Status validatePortSharing(String containerName, List<NodeConnector> portList) {
561 ContainerData container = this.getContainerByName(containerName);
562 if (container == null) {
563 String error = String
564 .format("Cannot validate port sharing for container %s: (container does not exist)", containerName);
566 return new Status(StatusCode.BADREQUEST, error);
568 return validatePortSharingInternal(portList, container.getContainerFlowSpecs());
572 * Checks if the proposed container configuration is valid to be applied in
573 * terms of port sharing with other containers.
575 * @param containerConf
576 * the container configuration object containing the list of node
578 * @return the status object representing the result of the check
580 private Status validatePortSharing(ContainerConfig containerConf) {
581 return validatePortSharingInternal(containerConf.getPortList(), containerConf.getContainerFlowSpecs());
585 * If any port is shared with an existing container, need flowSpec to be
586 * configured. If no flowSpec for this or other container, or if containers have any
587 * overlapping flowspec in common, then let the caller know this
588 * configuration has to be rejected.
590 private Status validatePortSharingInternal(List<NodeConnector> portList, List<ContainerFlow> flowSpecList) {
591 for (NodeConnector port : portList) {
592 List<String> slist = nodeConnectorToContainers.get(port);
593 if (slist != null && !slist.isEmpty()) {
594 for (String otherContainerName : slist) {
596 ContainerData other = containerData.get(otherContainerName);
597 if (flowSpecList.isEmpty()) {
598 msg = String.format("Port %s is shared and flow spec is emtpy for this container", port);
599 } else if (other.isFlowSpecEmpty()) {
600 msg = String.format("Port %s is shared and flow spec is emtpy for the other container", port);
601 } else if (!checkCommonContainerFlow(flowSpecList, other.getContainerFlowSpecs()).isSuccess()) {
602 msg = String.format("Port %s is shared and other container has common flow spec", port);
606 return new Status(StatusCode.BADREQUEST, msg);
611 return new Status(StatusCode.SUCCESS);
615 * Utility function to check if two lists of container flows share any same
616 * or overlapping container flows.
619 * One of the two lists of container flows to test
621 * One of the two lists of container flows to test
622 * @return The status of the check. Either SUCCESS or CONFLICT. In case of
623 * conflict, the Status will contain the description for the failed
626 private Status checkCommonContainerFlow(List<ContainerFlow> oneFlowList, List<ContainerFlow> twoFlowList) {
627 for (ContainerFlow oneFlow : oneFlowList) {
628 for (ContainerFlow twoFlow : twoFlowList) {
629 if (oneFlow.getMatch().intersetcs(twoFlow.getMatch())) {
630 return new Status(StatusCode.CONFLICT, String.format("Flow Specs overlap: %s %s",
631 oneFlow.getMatch(), twoFlow.getMatch()));
635 return new Status(StatusCode.SUCCESS);
639 * Return the ContainerData object for the passed container name. Given this is a
640 * backend database, the lower case version of the passed name is used while
641 * searching for the corresponding ContainerData object.
644 * The container name in any case
645 * @return The corresponding ContainerData object
647 private ContainerData getContainerByName(String name) {
648 return containerData.get(name.toLowerCase(Locale.ENGLISH));
652 * Add a ContainerData object for the given container name.
655 * The container name in any case
657 * The container data object
659 private void putContainerDataByName(String name, ContainerData sData) {
660 containerData.put(name.toLowerCase(Locale.ENGLISH), sData);
664 * Removes the ContainerData object for the given container name.
667 * The container name in any case
669 private void removeContainerDataByName(String name) {
670 containerData.remove(name.toLowerCase(Locale.ENGLISH));
674 public List<ContainerConfig> getContainerConfigList() {
675 return new ArrayList<ContainerConfig>(containerConfigs.values());
679 public ContainerConfig getContainerConfig(String containerName) {
680 ContainerConfig target = containerConfigs.get(containerName);
681 return (target == null) ? null : new ContainerConfig(target);
685 public List<String> getContainerNameList() {
687 * Return container names as they were configured by user (case sensitive)
688 * along with the default container
690 List<String> containerNameList = new ArrayList<String>();
691 containerNameList.add(GlobalConstants.DEFAULT.toString());
692 containerNameList.addAll(containerConfigs.keySet());
693 return containerNameList;
697 public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
698 Map<String, List<ContainerFlowConfig>> flowSpecConfig = new HashMap<String, List<ContainerFlowConfig>>();
699 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
700 List<ContainerFlowConfig> set = entry.getValue().getContainerFlowConfigs();
701 flowSpecConfig.put(entry.getKey(), set);
703 return flowSpecConfig;
706 private void loadConfigurations() {
708 * Read containers, container flows and finally containers' entries from file
709 * and program the database accordingly
711 if (containerConfigs.isEmpty()) {
712 loadContainerConfig();
716 private Status saveContainerConfig() {
717 return saveContainerConfigLocal();
720 public Status saveContainerConfigLocal() {
721 ObjectWriter objWriter = new ObjectWriter();
723 Status status = objWriter.write(new ConcurrentHashMap<String, ContainerConfig>(containerConfigs), containersFileName);
724 if (!status.isSuccess()) {
725 return new Status(StatusCode.INTERNALERROR, "Failed to save container configurations: "
726 + status.getDescription());
728 return new Status(StatusCode.SUCCESS);
731 private void removeComponentsStartUpfiles(String containerName) {
732 String startupLocation = String.format("./%s", GlobalConstants.STARTUPHOME.toString());
733 String containerPrint = String.format("_%s.", containerName.toLowerCase(Locale.ENGLISH));
735 File directory = new File(startupLocation);
736 String[] fileList = directory.list();
738 logger.trace("Deleteing startup configuration files for container {}", containerName);
739 if (fileList != null) {
740 for (String fileName : fileList) {
741 if (fileName.contains(containerPrint)) {
742 String fullPath = String.format("%s/%s", startupLocation, fileName);
743 File file = new File(fullPath);
744 boolean done = file.delete();
745 logger.trace("{} {}", (done ? "Deleted: " : "Failed to delete: "), fileName);
752 * Create and initialize default all resource group and create association
753 * with default well known users and profiles, if not already learnt from
754 * another cluster node
756 private void createDefaultAuthorizationGroups() {
757 allResourcesGroupName = ContainerManager.allContainersGroup;
759 // Add the default container to the all containers group if needed
760 String defaultContainer = GlobalConstants.DEFAULT.toString();
761 Set<String> allContainers = (resourceGroups.containsKey(allResourcesGroupName)) ? resourceGroups
762 .get(allResourcesGroupName) : new HashSet<String>();
763 if (!allContainers.contains(defaultContainer)) {
764 // Add Default container
765 allContainers.add(defaultContainer);
767 resourceGroups.put(allResourcesGroupName, allContainers);
770 // Add the controller well known roles, if not known already
771 if (!roles.containsKey(UserLevel.SYSTEMADMIN.toString())) {
772 roles.put(UserLevel.SYSTEMADMIN.toString(), AppRoleLevel.APPADMIN);
774 if (!roles.containsKey(UserLevel.NETWORKADMIN.toString())) {
775 roles.put(UserLevel.NETWORKADMIN.toString(), AppRoleLevel.APPADMIN);
777 if (!roles.containsKey(UserLevel.NETWORKOPERATOR.toString())) {
778 roles.put(UserLevel.NETWORKOPERATOR.toString(), AppRoleLevel.APPOPERATOR);
782 * Create and add the all containers user groups and associate them to the
783 * default well known user roles, if not present already
785 if (!groupsAuthorizations.containsKey(UserLevel.NETWORKADMIN.toString())) {
786 Set<ResourceGroup> writeProfile = new HashSet<ResourceGroup>(1);
787 Set<ResourceGroup> readProfile = new HashSet<ResourceGroup>(1);
788 writeProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.WRITE));
789 readProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.READ));
790 groupsAuthorizations.put(UserLevel.SYSTEMADMIN.toString(), writeProfile);
791 groupsAuthorizations.put(UserLevel.NETWORKADMIN.toString(), writeProfile);
792 groupsAuthorizations.put(UserLevel.NETWORKOPERATOR.toString(), readProfile);
797 * Until manual configuration is not available, automatically maintain the
798 * well known resource groups
800 * @param containerName
803 private void updateResourceGroups(String containerName, boolean delete) {
804 String containerProfile = System.getProperty("container.profile");
805 if (containerProfile == null) containerProfile = "Container";
806 // Container Roles and Container Resource Group
807 String groupName = containerProfile+"-" + containerName;
808 String containerAdminRole = containerProfile+"-" + containerName + "-Admin";
809 String containerOperatorRole = containerProfile+"-" + containerName + "-Operator";
810 Set<String> allContainerSet = resourceGroups.get(allResourcesGroupName);
812 resourceGroups.remove(groupName);
813 groupsAuthorizations.remove(containerAdminRole);
814 groupsAuthorizations.remove(containerOperatorRole);
815 roles.remove(containerAdminRole);
816 roles.remove(containerOperatorRole);
817 // Update the all container group
818 allContainerSet.remove(containerName);
820 Set<String> resources = new HashSet<String>(1);
821 resources.add(containerName);
822 resourceGroups.put(groupName, resources);
823 Set<ResourceGroup> adminGroups = new HashSet<ResourceGroup>(1);
824 Set<ResourceGroup> operatorGroups = new HashSet<ResourceGroup>(1);
825 adminGroups.add(new ResourceGroup(groupName, Privilege.WRITE));
826 operatorGroups.add(new ResourceGroup(groupName, Privilege.READ));
827 groupsAuthorizations.put(containerAdminRole, adminGroups);
828 groupsAuthorizations.put(containerOperatorRole, operatorGroups);
829 roles.put(containerAdminRole, AppRoleLevel.APPADMIN);
830 roles.put(containerOperatorRole, AppRoleLevel.APPOPERATOR);
831 // Update the all containers resource group
832 allContainerSet.add(containerName);
834 // Update resource groups in cluster
835 resourceGroups.put(allResourcesGroupName, allContainerSet);
839 * Notify ContainerAware listeners of the creation/deletion of the container
841 * @param containerName
843 * true is container was removed, false otherwise
845 private void notifyContainerAwareListeners(String containerName, boolean delete) {
846 // Back-end World: container name forced to lower case
847 String name = containerName.toLowerCase(Locale.ENGLISH);
849 synchronized (this.iContainerAware) {
850 for (IContainerAware i : this.iContainerAware) {
852 i.containerDestroy(name);
854 i.containerCreate(name);
861 * Notify the ContainerListener listeners in case the container mode has changed
862 * following a container configuration operation Note: this call must happen
863 * after the configuration db has been updated
865 * @param lastActionDelete
866 * true if the last container configuration operation was a container
869 private void notifyContainerModeChange(boolean lastActionDelete) {
870 if (lastActionDelete == false && containerConfigs.size() == 1) {
871 logger.info("First container Creation. Inform listeners");
872 synchronized (this.iContainerListener) {
873 for (IContainerListener i : this.iContainerListener) {
874 i.containerModeUpdated(UpdateType.ADDED);
877 } else if (lastActionDelete == true && containerConfigs.isEmpty()) {
878 logger.info("Last container Deletion. Inform listeners");
879 synchronized (this.iContainerListener) {
880 for (IContainerListener i : this.iContainerListener) {
881 i.containerModeUpdated(UpdateType.REMOVED);
887 private Status addRemoveContainerEntries(String containerName, List<String> nodeConnectorsString, boolean delete) {
888 // Construct action message
889 String action = String.format("Node conenctor(s) %s container %s: %s", delete ? "removal from" : "addition to",
890 containerName, nodeConnectorsString);
893 if (nodeConnectorsString == null || nodeConnectorsString.isEmpty()) {
894 return new Status(StatusCode.BADREQUEST, "Node connector list is null or empty");
898 ContainerConfig entryConf = containerConfigs.get(containerName);
899 if (entryConf == null) {
900 String msg = String.format("Container not found: %s", containerName);
901 String error = String.format("Failed to apply %s: (%s)", action, msg);
903 return new Status(StatusCode.NOTFOUND, msg);
907 Status status = ContainerConfig.validateNodeConnectors(nodeConnectorsString);
908 if (!status.isSuccess()) {
909 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
914 List<NodeConnector> nodeConnectors = ContainerConfig.nodeConnectorsFromString(nodeConnectorsString);
916 // Port sharing check
919 * Check if the ports being added to this container already belong to
920 * other containers. If so check whether the the appropriate flow specs
921 * are configured on this container
923 status = validatePortSharing(containerName, nodeConnectors);
924 if (!status.isSuccess()) {
925 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
932 status = updateContainerEntryDatabase(containerName, nodeConnectors, delete);
933 if (!status.isSuccess()) {
934 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
939 // Update Configuration
940 status = (delete) ? entryConf.removeNodeConnectors(nodeConnectorsString) : entryConf
941 .addNodeConnectors(nodeConnectorsString);
942 if (!status.isSuccess()) {
943 String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
945 // Revert backend changes
946 Status statusRevert = updateContainerEntryDatabase(containerName, nodeConnectors, !delete);
947 if (!statusRevert.isSuccess()) {
949 logger.error("Failed to revert changes in database (CRITICAL)");
954 // Update cluster Configuration cache
955 containerConfigs.put(containerName, entryConf);
958 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
959 notifyContainerEntryChangeInternal(containerName, nodeConnectors, update);
960 // Trigger cluster notification
961 containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(nodeConnectors, update));
966 private void notifyContainerChangeInternal(ContainerConfig conf, UpdateType update) {
967 String containerName = conf.getContainerName();
968 logger.trace("Notifying listeners on {} for container {}", update, containerName);
969 // Back-end World: container name forced to lower case
970 String container = containerName.toLowerCase(Locale.ENGLISH);
971 boolean delete = (update == UpdateType.REMOVED);
972 // Check if a container mode change notification is needed
973 notifyContainerModeChange(delete);
975 notifyContainerAwareListeners(container, delete);
978 private void notifyContainerEntryChangeInternal(String containerName, List<NodeConnector> ncList, UpdateType update) {
979 logger.trace("Notifying listeners on {} for ports {} in container {}", update, ncList, containerName);
980 // Back-end World: container name forced to lower case
981 String container = containerName.toLowerCase(Locale.ENGLISH);
982 for (NodeConnector nodeConnector : ncList) {
983 // Now signal that the port has been added/removed
984 synchronized (this.iContainerListener) {
985 for (IContainerListener i : this.iContainerListener) {
986 i.nodeConnectorUpdated(container, nodeConnector, update);
992 private void notifyCFlowChangeInternal(String containerName, List<ContainerFlowConfig> confList, UpdateType update) {
993 logger.trace("Notifying listeners on {} for flow specs {} in container {}", update, confList, containerName);
994 // Back-end World: container name forced to lower case
995 String container = containerName.toLowerCase(Locale.ENGLISH);
996 synchronized (this.iContainerListener) {
997 for (ContainerFlowConfig conf : confList) {
998 for (Match match : conf.getMatches()) {
999 ContainerFlow cFlow = new ContainerFlow(match);
1000 for (IContainerListener i : this.iContainerListener) {
1001 i.containerFlowUpdated(container, cFlow, cFlow, update);
1008 private Status addRemoveContainerFlow(String containerName, List<ContainerFlowConfig> cFlowConfList, boolean delete) {
1009 // Construct action message
1010 String action = String.format("Flow spec(s) %s container %s: %s", delete ? "removal from" : "addition to",
1011 containerName, cFlowConfList);
1014 ContainerConfig containerConfig = this.containerConfigs.get(containerName);
1015 if (containerConfig == null) {
1016 String msg = String.format("Container not found: %s", containerName);
1017 String error = String.format("Failed to apply %s: (%s)", action, msg);
1019 return new Status(StatusCode.NOTFOUND, "Container not present");
1022 // Validity check, check for overlaps on current container configuration
1023 Status status = containerConfig.validateContainerFlowModify(cFlowConfList, delete);
1024 if (!status.isSuccess()) {
1025 String msg = status.getDescription();
1026 String error = String.format("Failed to apply %s: (%s)", action, msg);
1028 return new Status(StatusCode.BADREQUEST, msg);
1031 // Validate the operation in terms to the port sharing with other containers
1032 for (ContainerFlowConfig conf : cFlowConfList) {
1033 for (Match match : conf.getMatches()) {
1034 ContainerFlow cFlow = new ContainerFlow(match);
1035 status = validateContainerFlowAddRemoval(containerName, cFlow, delete);
1036 if (!status.isSuccess()) {
1037 String msg = "Validation failed: " + status.getDescription();
1038 String error = String.format("Failed to apply %s: (%s)", action, msg);
1040 return new Status(StatusCode.BADREQUEST, msg);
1046 status = updateContainerFlow(containerName, cFlowConfList, delete);
1047 if (!status.isSuccess()) {
1048 String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
1049 logger.error(error);
1053 // Update Configuration
1054 status = (delete) ? containerConfig.removeContainerFlows(cFlowConfList) : containerConfig
1055 .addContainerFlows(cFlowConfList);
1056 if (!status.isSuccess()) {
1057 String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
1058 logger.error(error);
1059 // Revert backend changes
1060 Status statusRevert = updateContainerFlow(containerName, cFlowConfList, !delete);
1061 if (!statusRevert.isSuccess()) {
1063 logger.error("Failed to revert changes in database (CRITICAL)");
1067 // Update cluster cache
1068 this.containerConfigs.put(containerName, containerConfig);
1071 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1072 notifyCFlowChangeInternal(containerName, cFlowConfList, update);
1073 // Trigger cluster notification
1074 containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(cFlowConfList, update));
1079 private Status addRemoveContainer(ContainerConfig containerConf, boolean delete) {
1080 // Construct action message
1081 String action = String.format("Container %s", delete ? "removal" : "creation");
1083 // Valid configuration check
1084 Status status = null;
1085 String error = (containerConfigs == null) ? String.format("Invalid %s configuration: (null config object)", action)
1086 : (!(status = containerConf.validate()).isSuccess()) ? String.format("Invalid %s configuration: (%s)",
1087 action, status.getDescription()) : null;
1088 if (error != null) {
1090 return new Status(StatusCode.BADREQUEST, error);
1093 // Configuration presence check
1094 String containerName = containerConf.getContainerName();
1096 if (!containerConfigs.containsKey(containerName)) {
1097 String msg = String.format("%s Failed: (Container does not exist: %s)", action, containerName);
1099 return new Status(StatusCode.NOTFOUND, msg);
1102 if (containerConfigs.containsKey(containerName)) {
1103 String msg = String.format("%s Failed: (Container already exist: %s)", action, containerName);
1105 return new Status(StatusCode.CONFLICT, msg);
1110 * The proposed container configuration could be a complex one containing
1111 * both ports and flow spec. If so, check if it has shared ports with
1112 * other existing containers. If that is the case verify flow spec isolation
1113 * is in place. No need to check on flow spec validation first. This
1114 * would take care of both
1117 status = validatePortSharing(containerConf);
1118 if (!status.isSuccess()) {
1119 error = String.format("%s Failed: (%s)", action, status.getDescription());
1120 logger.error(error);
1126 status = updateContainerDatabase(containerConf, delete);
1128 // Abort and exit here if back-end database update failed
1129 if (!status.isSuccess()) {
1134 * This is a quick fix until configuration service becomes the
1135 * centralized configuration management place. Here container manager will
1136 * remove the startup files for all the bundles that are present in the
1137 * container being deleted. Do the cleanup here in Container manger as do not
1138 * want to put this temporary code in Configuration manager yet which is
1142 // TODO: remove when Config Mgr takes over
1143 removeComponentsStartUpfiles(containerName);
1147 * Update Configuration: This will trigger the notifications on cache
1148 * update callback locally and on the other cluster nodes
1151 this.containerConfigs.remove(containerName);
1153 this.containerConfigs.put(containerName, containerConf);
1156 // Automatically create and populate user and resource groups
1157 updateResourceGroups(containerName, delete);
1160 UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
1161 notifyContainerChangeInternal(containerConf, update);
1163 // Trigger cluster notification
1164 containerChangeEvents.put(containerName, new ContainerChangeEvent(containerConf, update));
1166 if (update == UpdateType.ADDED) {
1167 if (containerConf.hasFlowSpecs()) {
1168 List<ContainerFlowConfig> specList = containerConf.getContainerFlowConfigs();
1169 // Notify flow spec addition
1170 notifyCFlowChangeInternal(containerName, specList, update);
1172 // Trigger cluster notification
1173 containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(specList, update));
1176 if (containerConf.hasNodeConnectors()) {
1177 List<NodeConnector> ncList = containerConf.getPortList();
1178 // Notify port(s) addition
1179 notifyContainerEntryChangeInternal(containerName, ncList, update);
1180 // Trigger cluster notification
1181 containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(ncList, update));
1189 public Status addContainer(ContainerConfig containerConf) {
1190 return addRemoveContainer(containerConf, false);
1194 public Status removeContainer(ContainerConfig containerConf) {
1195 return addRemoveContainer(containerConf, true);
1199 public Status removeContainer(String containerName) {
1200 // Construct action message
1201 String action = String.format("Container removal: %s", containerName);
1203 ContainerConfig containerConf = containerConfigs.get(containerName);
1204 if (containerConf == null) {
1205 String msg = String.format("Container not found");
1206 String error = String.format("Failed to apply %s: (%s)", action, msg);
1208 return new Status(StatusCode.NOTFOUND, msg);
1210 return addRemoveContainer(containerConf, true);
1214 public Status addContainerEntry(String containerName, List<String> nodeConnectors) {
1215 return addRemoveContainerEntries(containerName, nodeConnectors, false);
1219 public Status removeContainerEntry(String containerName, List<String> nodeConnectors) {
1220 return addRemoveContainerEntries(containerName, nodeConnectors, true);
1224 public Status addContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1225 return addRemoveContainerFlow(containerName, fSpecConf, false);
1229 public Status removeContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
1230 return addRemoveContainerFlow(containerName, fSpecConf, true);
1234 public Status removeContainerFlows(String containerName, Set<String> names) {
1235 // Construct action message
1236 String action = String.format("Flow spec(s) removal from container %s: %s", containerName, names);
1239 ContainerConfig sc = containerConfigs.get(containerName);
1241 String msg = String.format("Container not found: %s", containerName);
1242 String error = String.format("Failed to apply %s: (%s)", action, msg);
1244 return new Status(StatusCode.NOTFOUND, msg);
1246 List<ContainerFlowConfig> list = sc.getContainerFlowConfigs(names);
1247 if (list.isEmpty() || list.size() != names.size()) {
1248 String msg = String.format("Cannot find all the specified flow specs");
1249 String error = String.format("Failed to apply %s: (%s)", action, msg);
1251 return new Status(StatusCode.BADREQUEST, msg);
1253 return addRemoveContainerFlow(containerName, list, true);
1257 public List<ContainerFlowConfig> getContainerFlows(String containerName) {
1258 ContainerConfig sc = containerConfigs.get(containerName);
1259 return (sc == null) ? new ArrayList<ContainerFlowConfig>(0) : sc.getContainerFlowConfigs();
1263 public List<String> getContainerFlowNameList(String containerName) {
1264 ContainerConfig sc = containerConfigs.get(containerName);
1265 return (sc == null) ? new ArrayList<String>(0) : sc.getContainerFlowConfigsNames();
1269 public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
1270 // Perform the class deserialization locally, from inside the package
1271 // where the class is defined
1272 return ois.readObject();
1275 @SuppressWarnings("unchecked")
1276 private void loadContainerConfig() {
1277 ObjectReader objReader = new ObjectReader();
1278 ConcurrentMap<String, ContainerConfig> configMap = (ConcurrentMap<String, ContainerConfig>) objReader.read(this,
1279 containersFileName);
1281 if (configMap == null) {
1285 for (Map.Entry<String, ContainerConfig> configEntry : configMap.entrySet()) {
1286 addContainer(configEntry.getValue());
1290 public void _psc(CommandInterpreter ci) {
1291 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1292 ContainerConfig sc = entry.getValue();
1293 ci.println(String.format("%s: %s", sc.getContainerName(), sc.toString()));
1295 ci.println("Total number of containers: " + containerConfigs.entrySet().size());
1298 public void _pfc(CommandInterpreter ci) {
1299 for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
1300 ContainerConfig sc = entry.getValue();
1301 ci.println(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
1305 public void _psd(CommandInterpreter ci) {
1306 for (String containerName : containerData.keySet()) {
1307 ContainerData sd = containerData.get(containerName);
1308 for (Node sid : sd.getSwPorts().keySet()) {
1309 Set<NodeConnector> s = sd.getSwPorts().get(sid);
1310 ci.println("\t" + sid + " : " + s);
1313 for (ContainerFlow s : sd.getContainerFlowSpecs()) {
1314 ci.println("\t" + s.toString());
1319 public void _psp(CommandInterpreter ci) {
1320 for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
1321 ci.println(nodeConnectorToContainers.get(sp));
1325 public void _psm(CommandInterpreter ci) {
1326 for (Node sp : nodeToContainers.keySet()) {
1327 ci.println(nodeToContainers.get(sp));
1331 public void _addContainer(CommandInterpreter ci) {
1332 String containerName = ci.nextArgument();
1333 if (containerName == null) {
1334 ci.print("Container Name not specified");
1337 String staticVlan = ci.nextArgument();
1338 if (staticVlan == null) {
1339 ci.print("Static Vlan not specified");
1342 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
1343 ci.println(this.addRemoveContainer(containerConfig, false));
1346 public void _createContainer(CommandInterpreter ci) {
1347 String containerName = ci.nextArgument();
1348 if (containerName == null) {
1349 ci.print("Container Name not specified");
1352 String staticVlan = ci.nextArgument();
1353 if (staticVlan == null) {
1354 ci.print("Static Vlan not specified");
1357 List<String> ports = new ArrayList<String>();
1358 for (long l = 1L; l < 10L; l++) {
1359 ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
1361 List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
1362 cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
1363 ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
1364 ci.println(this.addRemoveContainer(containerConfig, false));
1367 public void _removeContainer(CommandInterpreter ci) {
1368 String containerName = ci.nextArgument();
1369 if (containerName == null) {
1370 ci.print("Container Name not specified");
1373 ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
1374 ci.println(this.addRemoveContainer(containerConfig, true));
1377 public void _addContainerEntry(CommandInterpreter ci) {
1378 String containerName = ci.nextArgument();
1379 if (containerName == null) {
1380 ci.print("Container Name not specified");
1383 String nodeId = ci.nextArgument();
1384 if (nodeId == null) {
1385 ci.print("Node Id not specified");
1388 String portId = ci.nextArgument();
1389 if (portId == null) {
1390 ci.print("Port not specified");
1393 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1394 Short port = Short.valueOf(portId);
1395 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1396 List<String> portList = new ArrayList<String>(1);
1397 portList.add(nc.toString());
1398 ci.println(this.addRemoveContainerEntries(containerName, portList, false));
1401 public void _removeContainerEntry(CommandInterpreter ci) {
1402 String containerName = ci.nextArgument();
1403 if (containerName == null) {
1404 ci.print("Container Name not specified");
1407 String nodeId = ci.nextArgument();
1408 if (nodeId == null) {
1409 ci.print("Node Id not specified");
1412 String portId = ci.nextArgument();
1413 if (portId == null) {
1414 ci.print("Port not specified");
1417 Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
1418 Short port = Short.valueOf(portId);
1419 NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
1420 List<String> portList = new ArrayList<String>(1);
1421 portList.add(nc.toString());
1422 ci.println(this.addRemoveContainerEntries(containerName, portList, true));
1425 private ContainerFlowConfig createSampleContainerFlowConfig(String cflowName, boolean boolUnidirectional) {
1426 ContainerFlowConfig cfg = new ContainerFlowConfig(cflowName, "9.9.1.0/24", "19.9.1.2", "TCP", "1234", "25");
1430 public void _addContainerFlow(CommandInterpreter ci) {
1431 String containerName = ci.nextArgument();
1432 if (containerName == null) {
1433 ci.print("Container Name not specified");
1436 String cflowName = ci.nextArgument();
1437 if (cflowName == null) {
1438 ci.print("cflowName not specified");
1441 String unidirectional = ci.nextArgument();
1442 boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
1443 List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
1444 list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
1445 ci.println(this.addRemoveContainerFlow(containerName, list, false));
1448 public void _removeContainerFlow(CommandInterpreter ci) {
1449 String containerName = ci.nextArgument();
1450 if (containerName == null) {
1451 ci.print("Container Name not specified");
1454 String cflowName = ci.nextArgument();
1455 if (cflowName == null) {
1456 ci.print("cflowName not specified");
1459 Set<String> set = new HashSet<String>(1);
1461 ci.println(this.removeContainerFlows(containerName, set));
1465 public String getHelp() {
1466 StringBuffer help = new StringBuffer();
1467 help.append("---ContainerManager Testing---\n");
1468 help.append("\tpsc - Print ContainerConfigs\n");
1469 help.append("\tpfc - Print FlowSpecConfigs\n");
1470 help.append("\tpsd - Print ContainerData\n");
1471 help.append("\tpsp - Print nodeConnectorToContainers\n");
1472 help.append("\tpsm - Print nodeToContainers\n");
1473 help.append("\t addContainer <containerName> <staticVlan> \n");
1474 help.append("\t removeContainer <containerName> \n");
1475 help.append("\t addContainerEntry <containerName> <nodeId> <port> \n");
1476 help.append("\t removeContainerEntry <containerName> <nodeId> <port> \n");
1477 help.append("\t addContainerFlow <containerName> <cflowName> <unidirectional true/false>\n");
1478 help.append("\t removeContainerFlow <containerName> <cflowName> \n");
1479 return help.toString();
1483 public boolean doesContainerExist(String containerName) {
1484 // Test for default container
1485 if (GlobalConstants.DEFAULT.toString().equalsIgnoreCase(containerName)) {
1488 // Test for non-default one
1489 return (getContainerByName(containerName) != null);
1493 public ContainerData getContainerData(String containerName) {
1494 return (getContainerByName(containerName));
1498 public Status saveConfiguration() {
1499 return saveContainerConfig();
1502 public void _containermgrGetRoles(CommandInterpreter ci) {
1503 ci.println("Configured roles for Container Mgr:");
1504 List<String> list = this.getRoles();
1505 for (String role : list) {
1506 ci.println(role + "\t" + roles.get(role));
1510 public void _containermgrGetAuthorizedGroups(CommandInterpreter ci) {
1511 String roleName = ci.nextArgument();
1512 if (roleName == null || roleName.trim().isEmpty()) {
1513 ci.println("Invalid argument");
1514 ci.println("mmGetAuthorizedGroups <role_name>");
1517 ci.println("Resource Groups associated to role " + roleName + ":");
1518 List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
1519 for (ResourceGroup group : list) {
1520 ci.println(group.toString());
1524 public void _containermgrGetAuthorizedResources(CommandInterpreter ci) {
1525 String roleName = ci.nextArgument();
1526 if (roleName == null || roleName.trim().isEmpty()) {
1527 ci.println("Invalid argument");
1528 ci.println("mmGetAuthorizedResources <role_name>");
1531 ci.println("Resource associated to role " + roleName + ":");
1532 List<Resource> list = this.getAuthorizedResources(roleName);
1533 for (Resource resource : list) {
1534 ci.println(resource.toString());
1538 public void _containermgrGetResourcesForGroup(CommandInterpreter ci) {
1539 String groupName = ci.nextArgument();
1540 if (groupName == null || groupName.trim().isEmpty()) {
1541 ci.println("Invalid argument");
1542 ci.println("containermgrResourcesForGroup <group_name>");
1545 ci.println("Group " + groupName + " contains the following resources:");
1546 List<Object> resources = this.getResources(groupName);
1547 for (Object resource : resources) {
1548 ci.println(resource.toString());
1552 public void _containermgrGetUserLevel(CommandInterpreter ci) {
1553 String userName = ci.nextArgument();
1554 if (userName == null || userName.trim().isEmpty()) {
1555 ci.println("Invalid argument");
1556 ci.println("containermgrGetUserLevel <user_name>");
1559 ci.println("User " + userName + " has level: " + this.getUserLevel(userName));
1562 public void _containermgrGetUserResources(CommandInterpreter ci) {
1563 String userName = ci.nextArgument();
1564 if (userName == null || userName.trim().isEmpty()) {
1565 ci.println("Invalid argument");
1566 ci.println("containermgrGetUserResources <user_name>");
1569 ci.println("User " + userName + " owns the following resources: ");
1570 Set<Resource> resources = this.getAllResourcesforUser(userName);
1571 for (Resource resource : resources) {
1572 ci.println(resource.toString());
1577 * For scalability testing where as of now controller gui is unresponsive
1578 * providing here an osgi hook to trigger the save config so that DT do not
1579 * have to reaply the scalable configuration each time they restart the
1582 // TODO: remove when no longer needed
1583 public void _saveConfig(CommandInterpreter ci) {
1584 Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
1586 IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
1587 IConfigurationService.class, this);
1588 if (configService != null) {
1589 status = configService.saveConfigurations();
1591 ci.println(status.toString());
1595 public List<String> getContainerNames() {
1596 return getContainerNameList();
1600 public boolean hasNonDefaultContainer() {
1601 return !containerConfigs.keySet().isEmpty();