--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../commons/opendaylight</relativePath>
+ </parent>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+ </scm>
+ <artifactId>appauth</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.sal.authorization,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.usermanager,
+ org.slf4j,
+ org.apache.felix.dm,
+ org.apache.commons.lang3.builder,
+ org.eclipse.osgi.framework.console
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.controller.appauth,
+ org.opendaylight.controller.appauth.authorization
+ </Export-Package>
+ <Bundle-Activator>
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>usermanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.appauth;
+
+import org.opendaylight.controller.appauth.authorization.Authorization;
+
+public abstract class Application<T> extends Authorization<T> {
+
+ public Application() {
+ super();
+ }
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.appauth.authorization;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.sal.authorization.AppRoleLevel;
+import org.opendaylight.controller.sal.authorization.IResourceAuthorization;
+import org.opendaylight.controller.sal.authorization.Privilege;
+import org.opendaylight.controller.sal.authorization.Resource;
+import org.opendaylight.controller.sal.authorization.ResourceGroup;
+import org.opendaylight.controller.sal.authorization.UserLevel;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.usermanager.IUserManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This abstract class implements the methods defined by IResourceAuthorization
+ * interface for each application class.
+ */
+public abstract class Authorization<T> implements IResourceAuthorization {
+private static final Logger logger = LoggerFactory.getLogger(Authorization.class);
+ private static final String namesRegex = "^[a-zA-Z0-9]+[{\\.|\\_|\\-}[a-zA-Z0-9]]*$";
+ /*
+ * The configured resource groups
+ */
+ protected ConcurrentMap<String, Set<T>> resourceGroups;
+ /*
+ * The configured roles along with their level
+ */
+ protected ConcurrentMap<String, AppRoleLevel> roles;
+ /*
+ * The association of groups to roles
+ */
+ protected ConcurrentMap<String, Set<ResourceGroup>> groupsAuthorizations;
+ /*
+ * The name of the default group. It is the group which contains all the
+ * resources
+ */
+ protected String allResourcesGroupName;
+
+ @Override
+ public Status createRole(String role, AppRoleLevel level) {
+ if (role == null || role.trim().isEmpty()
+ || !role.matches(Authorization.namesRegex)) {
+ return new Status(StatusCode.BADREQUEST,
+ "Role name must start with alphanumeric, no special characters.");
+ }
+ if (isControllerRole(role)) {
+ return new Status(StatusCode.NOTALLOWED,
+ "Controller roles cannot be explicitely "
+ + "created in App context");
+ }
+ if (isRoleInUse(role)) {
+ return new Status(StatusCode.CONFLICT, "Role already in use");
+ }
+ if (roles.containsKey(role)) {
+ if (roles.get(role).equals(level)) {
+ return new Status(StatusCode.SUCCESS, "Role is already present");
+ } else {
+ return new Status(StatusCode.BADREQUEST,
+ "Role exists and has different level");
+ }
+ }
+
+ return createRoleInternal(role, level);
+ }
+
+ protected Status createRoleInternal(String role, AppRoleLevel level) {
+ roles.put(role, level);
+ groupsAuthorizations.put(role, new HashSet<ResourceGroup>());
+ return new Status(StatusCode.SUCCESS, null);
+ }
+
+ @Override
+ public Status removeRole(String role) {
+ if (role == null || role.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
+ }
+ if (isControllerRole(role)) {
+ return new Status(StatusCode.NOTALLOWED,
+ "Controller roles cannot be removed");
+ }
+
+ return removeRoleInternal(role);
+ }
+
+ protected Status removeRoleInternal(String role) {
+ groupsAuthorizations.remove(role);
+ roles.remove(role);
+ return new Status(StatusCode.SUCCESS, null);
+ }
+
+ @Override
+ public List<String> getRoles() {
+ return new ArrayList<String>(groupsAuthorizations.keySet());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Status createResourceGroup(String groupName, List<Object> resources) {
+ //verify group name not null/empty
+ if (groupName == null || groupName.trim().isEmpty()
+ || !groupName.matches(Authorization.namesRegex)) {
+ return new Status(StatusCode.BADREQUEST, "Group name must start with alphanumeric, no special characters");
+ }
+ //verify group name is not same as all-resources
+ if (groupName.equals(this.allResourcesGroupName)) {
+ return new Status(StatusCode.NOTALLOWED, "All resource group cannot be created");
+ }
+ //verify group name is unique
+ if (resourceGroups.containsKey(groupName)) {
+ return new Status(StatusCode.CONFLICT, "Group name already exists");
+ }
+
+ //try adding resources, discard if not of type T
+ Set<T> toBeAdded = new HashSet<T>();
+ boolean allAdded = true;
+ for (Object obj : resources) {
+ try {
+ toBeAdded.add((T) obj);
+ } catch (ClassCastException e) {
+ allAdded = false;
+ }
+ }
+ /*if (toBeAdded.size() == 0){ // TODO andrekim - we should have the ability to create a group with no resources (so we can add and delete entries)
+ return new Status(StatusCode.NOTACCEPTABLE, "Group not created. No valid resources specified");
+ }*/
+ resourceGroups.put(groupName, toBeAdded);
+ return (allAdded ? new Status(StatusCode.SUCCESS, "All resources added succesfully") :
+ new Status(StatusCode.SUCCESS, "One or more resources couldn't be added"));
+ }
+
+ public Status addResourceToGroup(String groupName, T resource) {
+ if (groupName == null || groupName.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid group name");
+ }
+
+ Set<T> group = resourceGroups.get(groupName);
+ if (group != null && resource != null) {
+ group.add(resource);
+ return new Status(StatusCode.SUCCESS, "Resource added successfully");
+ }
+
+ return new Status(StatusCode.NOTFOUND, "Group not found or incompatible resource");
+ }
+
+ public Status removeRoleResourceGroupMapping(String groupName) {
+ List<String> affectedRoles = new ArrayList<String>();
+ Status result;
+ for (Entry<String, Set<ResourceGroup>> pairs : groupsAuthorizations
+ .entrySet()) {
+ String role = pairs.getKey();
+ Set<ResourceGroup> groups = pairs.getValue();
+ for (ResourceGroup group : groups) {
+ if (group.getGroupName().equals(groupName)) {
+ affectedRoles.add(role);
+ break;
+ }
+ }
+ }
+ StringBuffer msg = new StringBuffer();
+ for (String role : affectedRoles) {
+ result = unassignResourceGroupFromRole(groupName, role);
+ if (!result.isSuccess()) {
+ msg.append(result.getDescription());
+ msg.append(' ');
+ }
+ }
+
+ if (msg.length() != 0) {
+ return new Status(StatusCode.BADREQUEST, msg.toString());
+ } else {
+ return new Status(StatusCode.SUCCESS);
+ }
+ }
+
+ @Override
+ public Status removeResourceGroup(String groupName) {
+ // Default resource group cannot be deleted
+ if (groupName == null || groupName.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid group name");
+ }
+ if (groupName.equals(this.allResourcesGroupName)) {
+ return new Status(StatusCode.NOTALLOWED,
+ "All resource group cannot be removed");
+ }
+
+ Status result = removeRoleResourceGroupMapping(groupName);
+
+ resourceGroups.remove(groupName);
+
+ if (!result.isSuccess()) {
+ return result;
+ }
+
+ return new Status(StatusCode.SUCCESS, null);
+ }
+
+
+ public Status removeResourceFromGroup(String groupName, T resource) {
+ if (groupName == null || groupName.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid group name");
+ }
+
+ Set<T> group = resourceGroups.get(groupName);
+ if (group != null && group.remove(resource)) {
+ return new Status(StatusCode.SUCCESS, "Resource removed successfully");
+ }
+
+ return new Status(StatusCode.NOTFOUND, "Group/Resource not found");
+ }
+
+
+ /**
+ * Relay the call to user manager
+ */
+ @Override
+ public UserLevel getUserLevel(String userName) {
+ IUserManager userManager = (IUserManager) ServiceHelper
+ .getGlobalInstance(IUserManager.class, this);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("User {} has UserLevel {}", userName, userManager.getUserLevel(userName));
+ }
+
+ return (userManager == null) ? UserLevel.NOUSER : userManager
+ .getUserLevel(userName);
+ }
+
+ @Override
+ public Set<Resource> getAllResourcesforUser(String userName) {
+ Set<Resource> resources = new HashSet<Resource>();
+ IUserManager userManager = (IUserManager) ServiceHelper
+ .getGlobalInstance(IUserManager.class, this);
+ if (userManager == null) {
+ return new HashSet<Resource>(0);
+ }
+
+ // Get the roles associated with this user
+ List<String> roles = userManager.getUserRoles(userName);
+ if (roles == null) {
+ return resources;
+ }
+
+ for (String role : roles) {
+ // Get our resource groups associated with this role
+ List<ResourceGroup> groups = this.getAuthorizedGroups(role);
+ if (groups.isEmpty()) {
+ continue;
+ }
+ for (ResourceGroup group : groups) {
+ // Get the list of resources in this group
+ List<Object> list = this.getResources(group.getGroupName());
+ if (list.isEmpty()) {
+ continue;
+ }
+ for (Object resource : list) {
+ Resource toBeAdded = new Resource(resource,
+ group.getPrivilege());
+ /*
+ * Add the resource to the set if the same resource with
+ * higher privilege is not present. If the same resource
+ * with lower privilege is present, remove it. No check on
+ * same privilege resource as set guarantees no duplicates.
+ */
+ Resource existing = higherPrivilegeResourcePresent(
+ resources, toBeAdded);
+ if (existing == null) {
+ resources.add(toBeAdded);
+ }
+ existing = lowerPrivilegeResourcePresent(resources,
+ toBeAdded);
+ if (existing != null) {
+ resources.remove(existing);
+ }
+ }
+ }
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("User {} has resources {}", userName, resources);
+ }
+
+ return resources;
+ }
+
+ @Override
+ public List<Resource> getAuthorizedResources(String role) {
+ Set<Resource> resources = new HashSet<Resource>();
+
+ // Get our resource groups associated with this role
+ List<ResourceGroup> groups = this.getAuthorizedGroups(role);
+ if (groups.isEmpty()) {
+ return new ArrayList<Resource>(0);
+ }
+ for (ResourceGroup group : groups) {
+ // Get the list of resources in this group
+ List<Object> list = this.getResources(group.getGroupName());
+ if (list.isEmpty()) {
+ continue;
+ }
+ for (Object resource : list) {
+ Resource toBeAdded = new Resource(resource,
+ group.getPrivilege());
+ /*
+ * Add the resource to the set if the same resource with higher
+ * privilege is not present. If the same resource with lower
+ * privilege is present, remove it. No check on same privilege
+ * resource as set guarantees no duplicates.
+ */
+ Resource existing = higherPrivilegeResourcePresent(resources,
+ toBeAdded);
+ if (existing == null) {
+ resources.add(toBeAdded);
+ }
+ existing = lowerPrivilegeResourcePresent(resources, toBeAdded);
+ if (existing != null) {
+ resources.remove(existing);
+ }
+ }
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("For the Role {}, Authorized Resources are {}", role, resources);
+ }
+
+ return new ArrayList<Resource>(resources);
+ }
+
+ /**
+ * Given a set of resources and a resource to test, it returns the element
+ * in the set which represent the same resource with a lower privilege
+ * associated to it, if present.
+ *
+ * @param resources
+ * @param resource
+ * @return
+ */
+ private Resource lowerPrivilegeResourcePresent(Set<Resource> resources,
+ Resource resource) {
+
+ if (resource == null || resources == null) {
+ return null;
+ }
+
+ Object resourceElement = resource.getResource();
+ Privilege resourcePrivilege = resource.getPrivilege();
+ for (Resource element : resources) {
+ if (element.getResource().equals(resourceElement)
+ && element.getPrivilege().ordinal() < resourcePrivilege
+ .ordinal()) {
+ return element;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Given a set of resources and a resource to test, it returns the element
+ * in the set which represents the same resource with an higher privilege,
+ * if present.
+ *
+ * @param resources
+ * @param resource
+ * @return
+ */
+ private Resource higherPrivilegeResourcePresent(Set<Resource> resources,
+ Resource resource) {
+
+ if (resource == null || resources == null) {
+ return null;
+ }
+
+ Object resourceElement = resource.getResource();
+ Privilege resourcePrivilege = resource.getPrivilege();
+ for (Resource element : resources) {
+ if (element.getResource().equals(resourceElement)
+ && element.getPrivilege().ordinal() > resourcePrivilege
+ .ordinal()) {
+ return element;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Privilege getResourcePrivilege(String userName, Object resource) {
+
+ if (userName == null || userName.trim().isEmpty() || resource == null) {
+ return Privilege.NONE;
+ }
+
+ Set<Resource> hisResources = getAllResourcesforUser(userName);
+ for (Resource element : hisResources) {
+ if (element.getResource().equals(resource)) {
+ return element.getPrivilege();
+ }
+ }
+
+ return Privilege.NONE;
+ }
+
+ @Override
+ public List<String> getResourceGroups() {
+ return new ArrayList<String>(resourceGroups.keySet());
+ }
+
+ @Override
+ public Status assignResourceGroupToRole(String group, Privilege privilege,
+ String role) {
+ if (group == null || group.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid group name");
+ }
+ if (role == null || role.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
+ }
+ if (isControllerRole(role)) {
+ return new Status(StatusCode.NOTALLOWED,
+ "No group assignment is accepted for Controller roles");
+ }
+
+ return assignResourceGroupToRoleInternal(group, privilege, role);
+ }
+
+ protected Status assignResourceGroupToRoleInternal(String group,
+ Privilege privilege, String role) {
+ groupsAuthorizations.get(role).add(new ResourceGroup(group, privilege));
+ return new Status(StatusCode.SUCCESS, null);
+ }
+
+ @Override
+ public Status assignResourceGroupToRole(String groupName, String roleName) {
+ if (groupName == null || groupName.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
+ }
+ if (roleName == null || roleName.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
+ }
+ // Infer group privilege from role's level
+ Privilege privilege = Privilege.NONE;
+ switch (this.getApplicationRoleLevel(roleName)) {
+ case APPADMIN:
+ privilege = Privilege.WRITE;
+ break;
+ case APPUSER:
+ privilege = Privilege.USE;
+ break;
+ case APPOPERATOR:
+ privilege = Privilege.READ;
+ break;
+ default:
+ break;
+ }
+ return this.assignResourceGroupToRole(groupName, privilege, roleName);
+ }
+
+ @Override
+ public Status unassignResourceGroupFromRole(String group, String role) {
+ if (group == null || group.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
+ }
+ if (role == null || role.trim().isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
+ }
+ if (isControllerRole(role)) {
+ return new Status(StatusCode.NOTALLOWED,
+ "No group assignment change is allowed for "
+ + "Controller roles");
+ }
+
+ return unassignResourceGroupFromRoleInternal(group, role);
+ }
+
+ protected Status unassignResourceGroupFromRoleInternal(String group,
+ String role) {
+ ResourceGroup target = null;
+ for (ResourceGroup rGroup : groupsAuthorizations.get(role)) {
+ if (rGroup.getGroupName().equals(group)) {
+ target = rGroup;
+ break;
+ }
+ }
+ if (target == null) {
+ return new Status(StatusCode.SUCCESS, "Group " + group
+ + " was not assigned to " + role);
+ } else {
+ groupsAuthorizations.get(role).remove(target);
+ return new Status(StatusCode.SUCCESS);
+
+ }
+ }
+
+ @Override
+ public List<ResourceGroup> getAuthorizedGroups(String role) {
+ return (groupsAuthorizations.containsKey(role)) ? new ArrayList<ResourceGroup>(
+ groupsAuthorizations.get(role))
+ : new ArrayList<ResourceGroup>();
+ }
+
+ @Override
+ public List<Object> getResources(String groupName) {
+ return (resourceGroups.containsKey(groupName)) ? new ArrayList<Object>(
+ resourceGroups.get(groupName)) : new ArrayList<Object>();
+ }
+
+ @Override
+ public boolean isApplicationRole(String roleName) {
+ if (roleName == null) {
+ return false;
+ }
+ return roles.containsKey(roleName);
+ }
+
+ @Override
+ public AppRoleLevel getApplicationRoleLevel(String roleName) {
+ if (roleName == null || roleName.trim().isEmpty()) {
+ return AppRoleLevel.NOUSER;
+ }
+
+ if (isControllerRole(roleName)) {
+ if (roleName.equals(UserLevel.NETWORKADMIN.toString()) ||
+ roleName.equals(UserLevel.SYSTEMADMIN.toString())) {
+ return AppRoleLevel.APPADMIN;
+ } else {
+ return AppRoleLevel.APPOPERATOR;
+ }
+ }
+
+ return (roles.containsKey(roleName)) ? roles.get(roleName)
+ : AppRoleLevel.NOUSER;
+ }
+
+ @Override
+ public AppRoleLevel getUserApplicationLevel(String userName) {
+ List<String> roles = null;
+ IUserManager userManager = (IUserManager) ServiceHelper
+ .getGlobalInstance(IUserManager.class, this);
+
+ if (userName == null || userName.trim().isEmpty()
+ || (userManager == null)
+ || (roles = userManager.getUserRoles(userName)).isEmpty()) {
+ return AppRoleLevel.NOUSER;
+ }
+ AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
+ for (String role : roles) {
+ AppRoleLevel level = getApplicationRoleLevel(role);
+ if (level.ordinal() < highestLevel.ordinal()) {
+ highestLevel = level;
+ }
+ }
+ return highestLevel;
+ }
+
+ /**
+ * Returns the highest role the specified user has in this application
+ * context
+ *
+ * @param user
+ * The user name
+ * @return The highest role associated to the user in this application
+ * context
+ */
+ public String getHighestUserRole(String user) {
+ IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
+ String highestRole = "";
+ if (userManager != null && !userManager.getUserRoles(user).isEmpty()) {
+ List<String> roles = userManager.getUserRoles(user);
+ AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
+ for (String role : roles) {
+ AppRoleLevel current;
+ if (isApplicationRole(role)
+ && (current = getApplicationRoleLevel(role)).ordinal() < highestLevel.ordinal()) {
+ highestRole = role;
+ highestLevel = current;
+ }
+ }
+ }
+ return highestRole;
+ }
+
+ private boolean isControllerRole(String role) {
+ return (role.equals(UserLevel.NETWORKADMIN.toString())
+ || role.equals(UserLevel.SYSTEMADMIN.toString()) || role
+ .equals(UserLevel.NETWORKOPERATOR.toString()));
+ }
+
+ private boolean isRoleInUse(String role) {
+ IUserManager userManager = (IUserManager) ServiceHelper
+ .getGlobalInstance(IUserManager.class, this);
+
+ if (userManager == null) {
+ return true;
+ }
+ return userManager.isRoleInUse(role);
+ }
+}
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.implementation</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <artifactId>containermanager.it.implementation</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
</dependencies>
<properties>
"sal.implementation").versionAsInProject(),\r
mavenBundle("org.opendaylight.controller", "containermanager").versionAsInProject(),\r
mavenBundle("org.opendaylight.controller",\r
- "containermanager.implementation").versionAsInProject(),\r
+ "containermanager.it.implementation").versionAsInProject(),\r
mavenBundle("org.jboss.spec.javax.transaction",\r
"jboss-transaction-api_1.1_spec").versionAsInProject(),\r
mavenBundle("org.apache.commons", "commons-lang3").versionAsInProject(),\r
* Function that will be called when there is the event of
* coordinator change in the cluster.
*/
- public void coordinatorChanged();
+ void coordinatorChanged();
}
* active-standby milestone is reached, after will be removed.
*
*/
- public void newActiveAvailable();
+ void newActiveAvailable();
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.clustering.services_implementation.internal;
+
+import java.lang.ref.WeakReference;
+
+import org.jboss.marshalling.ContextClassResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ClassResolver extends ContextClassResolver {
+ private WeakReference<ClassLoader> osgiClassLoader = null;
+ private static final Logger logger = LoggerFactory.getLogger(ClassResolver.class);
+
+ public ClassResolver() {
+ ClassLoader cl = this.getClass()
+ .getClassLoader();
+ if (cl != null) {
+ this.osgiClassLoader = new WeakReference<ClassLoader>(cl);
+ logger.trace("Acquired weak reference to OSGi classLoader {}", cl);
+ }
+ }
+
+ @Override
+ protected ClassLoader getClassLoader() {
+ ClassLoader ret = null;
+ if (this.osgiClassLoader != null) {
+ ret = this.osgiClassLoader.get();
+ if (ret != null) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Returning OSGi class loader {}", ret);
+ }
+ return ret;
+ }
+ }
+
+ logger.warn("Could not resolve classloader!");
+ return ret;
+ }
+}
import org.infinispan.Cache;
import org.infinispan.configuration.cache.Configuration;
+import org.infinispan.configuration.global.GlobalConfigurationBuilder;
+import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
+import org.infinispan.configuration.parsing.ParserRegistry;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.notifications.Listener;
}
logger.info("Starting the ClusterManager");
try {
- //FIXME keeps throwing FileNotFoundException
- this.cm = new DefaultCacheManager("config/infinispan-config.xml");
+ ParserRegistry parser = new ParserRegistry(this.getClass()
+ .getClassLoader());
+ ConfigurationBuilderHolder holder = parser.parseFile("config/infinispan-config.xml");
+ GlobalConfigurationBuilder globalBuilder = holder.getGlobalConfigurationBuilder();
+ globalBuilder.serialization()
+ .classResolver(new ClassResolver())
+ .build();
+ this.cm = new DefaultCacheManager(holder, false);
logger.debug("Allocated ClusterManager");
if (this.cm != null) {
this.cm.start();
/**
* Trigger from configuration component to persist the configuration state.
*/
- public Status saveConfiguration();
+ Status saveConfiguration();
}
*
*/
public interface IConfigurationServiceCommon {
- public Status saveConfigurations();
+ Status saveConfigurations();
}
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.implementation</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <artifactId>containermanager.it.implementation</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
mavenBundle("org.opendaylight.controller", "protocol_plugins.stub").versionAsInProject(),
// needed bundles by switchmanager
mavenBundle("org.opendaylight.controller", "containermanager").versionAsInProject(),
- mavenBundle("org.opendaylight.controller", "containermanager.implementation").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller", "containermanager.it.implementation").versionAsInProject(),
// needed bundles by configuration
mavenBundle("org.opendaylight.controller", "clustering.services").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "clustering.stub").versionAsInProject(),
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import javax.transaction.SystemException;
-
import org.opendaylight.controller.clustering.services.CacheConfigException;
import org.opendaylight.controller.clustering.services.CacheExistException;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
return controllerNodesMap.get(controller);
}
- @SuppressWarnings("deprecation")
public Set<Node> getNodes() {
return getNodes(clusterServices.getMyAddress());
}
return nodeConnections;
}
- @SuppressWarnings("deprecation")
public boolean isLocal(Node node) {
if (nodeConnections == null) return false;
InetAddress myController = clusterServices.getMyAddress();
return (controllers != null && controllers.contains(myController));
}
- @SuppressWarnings("deprecation")
public Status removeNode (Node node) {
return removeNodeFromController(node, clusterServices.getMyAddress());
}
</scm>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<build>
<configuration>
<instructions>
<Import-Package>
- org.opendaylight.controller.sal.action,
org.opendaylight.controller.sal.authorization,
+ org.opendaylight.controller.sal.utils,
org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.action,
org.opendaylight.controller.sal.flowprogrammer,
org.opendaylight.controller.sal.match,
+ org.opendaylight.controller.sal.packet,
org.opendaylight.controller.sal.reader,
- org.opendaylight.controller.sal.utils,
- org.apache.commons.lang3.builder
+ org.apache.commons.lang3.builder,
+ org.slf4j,
+ javax.xml.bind.annotation,
+ javax.xml.bind
</Import-Package>
<Export-Package>
org.opendaylight.controller.containermanager
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager;
+
+import java.io.Serializable;
+
+import org.opendaylight.controller.sal.core.UpdateType;
+
+public class ContainerChangeEvent implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private ContainerConfig config;
+ private UpdateType update;
+
+ public ContainerChangeEvent(ContainerConfig config, UpdateType update) {
+ this.config = config;
+ this.update = update;
+ }
+
+ public UpdateType getUpdateType() {
+ return update;
+ }
+
+ public ContainerConfig getConfig() {
+ return config;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+
+/**
+ * Container Configuration Java Object for Container Manager Represents a container
+ * configuration information for Container Manager.
+ *
+ * Objects of this class are also serialized to and deserialized from binary
+ * files through java serialization API when saving to/reading from Container
+ * Manager startup configuration file.
+ */
+@XmlRootElement(name = "container-config")
+@XmlAccessorType(XmlAccessType.NONE)
+public class ContainerConfig implements Serializable {
+ private static final long serialVersionUID = 2L;
+ private static final String regexName = "^\\w+$";
+
+ @XmlElement
+ private String container;
+
+ @XmlElement
+ private String staticVlan;
+
+ @XmlElement(name = "nodeConnectors")
+ private List<String> ports;
+
+ @XmlElement(name = "flowSpecs")
+ private List<ContainerFlowConfig> containerFlows;
+
+ public String getContainer() {
+ return container;
+ }
+
+ public void setContainer(String container) {
+ this.container = container;
+ }
+
+ public String getStaticVlan() {
+ return staticVlan;
+ }
+
+ public void setStaticVlan(String staticVlan) {
+ this.staticVlan = staticVlan;
+ }
+
+ public List<ContainerFlowConfig> getContainerFlows() {
+ return containerFlows;
+ }
+
+ public void setContainerFlows(List<ContainerFlowConfig> containerFlows) {
+ this.containerFlows = containerFlows;
+ }
+
+ public static long getSerialversionuid() {
+ return serialVersionUID;
+ }
+
+ public static String getRegexname() {
+ return regexName;
+ }
+
+ public void setPorts(List<String> ports) {
+ this.ports = ports;
+ }
+
+ /**
+ * Default constructor needed by Gson.
+ *
+ * @return a Default ContainerConfig
+ */
+ public ContainerConfig() {
+ this.container = null;
+ this.staticVlan = null;
+ this.ports = new ArrayList<String>(0);
+ this.containerFlows = new ArrayList<ContainerFlowConfig>(0);
+ }
+
+ /**
+ * Constructor for the ContainerConfig.
+ *
+ * @param container
+ * Name of the container in this configuration
+ * @param vlan
+ * vlan assigned to this container
+ * @param nodeName
+ * the name of the node assigned to the container from this
+ * configuration
+ * @param ports
+ * the list of NodeConnectors on the Node belonging to the container
+ * @return the constructed object
+ */
+ public ContainerConfig(String container, String vlan, List<String> portList, List<ContainerFlowConfig> containerFlows) {
+ this.container = container;
+ this.staticVlan = vlan;
+ this.ports = (portList == null) ? new ArrayList<String>(0) : new ArrayList<String>(portList);
+ this.containerFlows = (containerFlows == null) ? new ArrayList<ContainerFlowConfig>(0)
+ : new ArrayList<ContainerFlowConfig>(containerFlows);
+ }
+
+ public ContainerConfig(ContainerConfig config) {
+ this.container = config.container;
+ this.staticVlan = config.staticVlan;
+ this.ports = (config.ports == null) ? new ArrayList<String>(0) : new ArrayList<String>(config.ports);
+ this.containerFlows = (config.containerFlows == null) ? new ArrayList<ContainerFlowConfig>(0)
+ : new ArrayList<ContainerFlowConfig>(config.containerFlows);
+ }
+
+ /**
+ * Returns the container name.
+ *
+ * @return the container name
+ */
+ public String getContainerName() {
+ return container;
+ }
+
+ /**
+ * Returns the Vlan tag.
+ *
+ * @return the Vlan Tag configured for this container configuration
+ */
+ public String getVlanTag() {
+ return staticVlan;
+ }
+
+ /**
+ * Returns the configured ports.
+ *
+ * @return the string with the list of ports associated to the container on this
+ * configuration
+ */
+ public List<String> getPorts() {
+ return new ArrayList<String>(ports);
+ }
+
+ /**
+ * Returns the list of container flows configured for this container
+ *
+ * @return
+ */
+ public List<ContainerFlowConfig> getContainerFlowConfigs() {
+ return (containerFlows == null || containerFlows.isEmpty()) ? new ArrayList<ContainerFlowConfig>(0)
+ : new ArrayList<ContainerFlowConfig>(containerFlows);
+ }
+
+ /**
+ * Matches container name against passed parameter.
+ *
+ * @param name
+ * name of the container to be matched
+ * @return true if the passed argument correspond with the container name in the
+ * configuration
+ */
+ public boolean matchName(String name) {
+ return this.container.equals(name);
+ }
+
+ /**
+ * Parse the port list in several NodeConnector descriptor.
+ *
+ * @return the list of NodeConnector corresponding to the ports configured
+ * on this configuration
+ */
+ public List<NodeConnector> getPortList() {
+ List<NodeConnector> portList = new ArrayList<NodeConnector>();
+ if (ports != null && !ports.isEmpty()) {
+ for (String portString : ports) {
+ portList.add(NodeConnector.fromString(portString));
+ }
+ }
+ return portList;
+ }
+
+ /**
+ * Checks if this is a valid container configuration
+ *
+ * @return true, if is valid container configuration, false otherwise
+ */
+ public Status validate() {
+ Status status = validateName();
+ if (status.isSuccess()) {
+ status = validateStaticVlan();
+ if (status.isSuccess()) {
+ status = validatePorts();
+ if (status.isSuccess()) {
+ status = validateContainerFlows();
+ }
+ }
+ }
+ return status;
+ }
+
+ /**
+ * Checks for valid name.
+ *
+ * @return true, if successful
+ */
+ private Status validateName() {
+ // No Container configuration allowed to container default
+ return (container.matches(regexName) && !container.equalsIgnoreCase(GlobalConstants.DEFAULT.toString())) ?
+ new Status(StatusCode.SUCCESS) : new Status(StatusCode.BADREQUEST, "Invalid container name");
+ }
+
+ /**
+ * Checks for valid ports.
+ *
+ * @return true, if successful
+ */
+ private Status validatePorts() {
+ return validateNodeConnectors(this.ports);
+ }
+
+ public static Status validateNodeConnectors(List<String> connectorList) {
+ if (connectorList != null && !connectorList.isEmpty()) {
+ for (String ncString : connectorList) {
+ if (NodeConnector.fromString(ncString) == null) {
+ return new Status(StatusCode.BADREQUEST, "Invalid node connector: " + ncString);
+ }
+ }
+
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ public static List<NodeConnector> nodeConnectorsFromString(List<String> nodeConnectorStrings) {
+ List<NodeConnector> list = new ArrayList<NodeConnector>(nodeConnectorStrings.size());
+ for (String str : nodeConnectorStrings) {
+ list.add(NodeConnector.fromString(str));
+ }
+ return list;
+ }
+
+ /**
+ * Checks for valid static vlan.
+ *
+ * @return true, if successful
+ */
+ private Status validateStaticVlan() {
+ if (staticVlan != null && !staticVlan.trim().isEmpty()) {
+ short vl = 0;
+ try {
+ vl = Short.valueOf(staticVlan);
+ } catch (NumberFormatException e) {
+ return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
+ }
+ if ((vl < 1) || (vl > 4095)) {
+ return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ private Status validateContainerFlows() {
+ if (containerFlows != null && !containerFlows.isEmpty()) {
+ for (ContainerFlowConfig conf : containerFlows) {
+ Status status = conf.validate();
+ if (!status.isSuccess()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec: " + status.getDescription());
+ }
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ /**
+ * Returns Vlan value in short
+ *
+ * @return the Vlan tag
+ */
+ public short getStaticVlanValue() {
+ if ((staticVlan == null) || (staticVlan.trim().isEmpty())) {
+ return 0;
+ }
+ try {
+ return Short.valueOf(staticVlan);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ public Status addNodeConnectors(List<String> ncList) {
+ // Syntax check
+ Status status = ContainerConfig.validateNodeConnectors(ncList);
+ if (!status.isSuccess()) {
+ return status;
+ }
+
+ /* Allow adding ports which are already present
+ if (!ports.isEmpty()) {
+ List<String> intersection = new ArrayList<String>(ports);
+ intersection.retainAll(ncList);
+ if (!intersection.isEmpty()) {
+ return new Status(StatusCode.CONFLICT, "The following node connectors are already part of this container: "
+ + intersection);
+ }
+ }
+ */
+
+ // Add ports
+ ports.addAll(ncList);
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ public Status removeNodeConnectors(List<String> ncList) {
+ // Syntax check
+ Status status = ContainerConfig.validateNodeConnectors(ncList);
+ if (!status.isSuccess()) {
+ return status;
+ }
+ // Presence check
+ if (ports.isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "The following node connectors are not part of this container: "
+ + ncList);
+ }
+ List<String> extra = new ArrayList<String>(ncList);
+ extra.removeAll(ports);
+ if (!extra.isEmpty()) {
+ return new Status(StatusCode.CONFLICT, "The following node connectors are not part of this container: " + extra);
+ }
+ // Remove ports
+ ports.removeAll(ncList);
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ public Status validateContainerFlowModify(List<ContainerFlowConfig> cFlowConfigs, boolean delete) {
+ // Sanity Check
+ if (cFlowConfigs == null || cFlowConfigs.isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec configuration(s): null or empty list");
+ }
+ // Validity check
+ for (ContainerFlowConfig cFlowConf : cFlowConfigs) {
+ Status status = cFlowConf.validate();
+ if (!status.isSuccess()) {
+ return new Status(StatusCode.BADREQUEST, String.format("Invalid Flow Spec configuration (%s): %s",
+ cFlowConf.getName(), status.getDescription()));
+ }
+ }
+ // Name conflict check
+ List<String> existingNames = this.getContainerFlowConfigsNames();
+ List<String> proposedNames = this.getContainerFlowConfigsNames(cFlowConfigs);
+
+ // Check for duplicates in the request
+ if (proposedNames.size() < cFlowConfigs.size()) {
+ return new Status(StatusCode.BADREQUEST,
+ "Invalid Flow Spec configuration(s): duplicate name configs present");
+ }
+
+ // Check for overflow
+ if (delete) {
+ // Raw size check
+ if (proposedNames.size() > existingNames.size()) {
+ return new Status(StatusCode.BADREQUEST,
+ "Invalid request: requested to remove more flow spec configs than available ones");
+ }
+ // Presence check
+ for (ContainerFlowConfig config : cFlowConfigs) {
+ if (!this.containerFlows.contains(config)) {
+ return new Status(StatusCode.BADREQUEST, String.format(
+ "Invalid request: requested to remove nonexistent flow spec config: %s",
+ config.getName()));
+ }
+ }
+ }
+ /*
+ * Revisit the following flow-spec confict validation later based on more testing.
+ */
+ /*
+ if (!delete) {
+ // Check for overlapping container flows in the request
+ int size = cFlowConfigs.size();
+ for (int i = 0; i < size; i++) {
+ ContainerFlowConfig first = cFlowConfigs.get(i);
+ for (int j = i + 1; j < size; j++) {
+ ContainerFlowConfig second = cFlowConfigs.get(j);
+ if (first.overlap(second)) {
+ return new Status(StatusCode.BADREQUEST, String.format(
+ "Invalid Request: the proposed flow specs overlap: %s <-> %s", first.getName(),
+ second.getName()));
+ }
+ }
+ }
+ // Check if any of the proposed container flows overlap with the
+ // existing ones
+ for (ContainerFlowConfig current : cFlowConfigs) {
+ for (ContainerFlowConfig existing : this.containerFlows) {
+ if (current.overlap(existing)) {
+ return new Status(StatusCode.BADREQUEST, String.format(
+ "Invalid Request: the proposed flow specs overlap: %s <-> %s", current.getName(),
+ existing.getName()));
+ }
+ }
+ }
+ }
+ */
+
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ public ContainerFlowConfig getContainerFlowConfig(String name) {
+ if (this.containerFlows != null && !this.containerFlows.isEmpty()) {
+ for (ContainerFlowConfig conf : this.containerFlows) {
+ if (conf.getName().equals(name)) {
+ return new ContainerFlowConfig(conf);
+ }
+ }
+ }
+ return null;
+ }
+
+ public List<String> getContainerFlowConfigsNames() {
+ return getContainerFlowConfigsNames(this.containerFlows);
+ }
+
+ /**
+ * Returns the list of unique names for the passed list of
+ * ContainerFlowConfig objects. the list will not contain duplicates even
+ * though the passed object list has ContainerFlowConfig objects with same
+ * names
+ *
+ * @param confList
+ * the list of ContainerFlowConfig objects
+ * @return the list of correspondent unique container flow names. The return
+ * list may differ from the passed list in size, if the latter
+ * contains duplicates
+ */
+ private List<String> getContainerFlowConfigsNames(List<ContainerFlowConfig> confList) {
+ // Use set to check for duplicates later
+ Set<String> namesSet = new HashSet<String>();
+ if (confList != null) {
+ for (ContainerFlowConfig conf : confList) {
+ namesSet.add(conf.getName());
+ }
+ }
+ return new ArrayList<String>(namesSet);
+ }
+
+ /**
+ * Add the proposed list of container flow configurations to this container
+ * configuration. A validation check on the operation is first run.
+ *
+ * @param containerFlowConfigs
+ * the proposed list of container flow configuration objects to
+ * add to this container configuration object
+ * @return the result of this request as Status object
+ */
+ public Status addContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
+ Status status = this.validateContainerFlowModify(containerFlowConfigs, false);
+ if (!status.isSuccess()) {
+ return status;
+ }
+ if (this.containerFlows.addAll(containerFlowConfigs) == false) {
+ return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ public Status removeContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
+ Status status = this.validateContainerFlowModify(containerFlowConfigs, true);
+ if (!status.isSuccess()) {
+ return status;
+ }
+ if (this.containerFlows.removeAll(containerFlowConfigs) == false) {
+ return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ public Status removeContainerFlows(Set<String> names) {
+ // Sanity check
+ if (names == null || names.isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid flow spec names list");
+ }
+ // Validation check
+ List<String> present = this.getContainerFlowConfigsNames();
+ if (!present.containsAll(names)) {
+ List<String> notPresent = new ArrayList<String>(names);
+ notPresent.retainAll(present);
+ return new Status(StatusCode.BADREQUEST, "Following flow spec(s) are not present: " + notPresent);
+ }
+ // Remove
+ List<ContainerFlowConfig> toDelete = new ArrayList<ContainerFlowConfig>(names.size());
+ for (ContainerFlowConfig config : this.containerFlows) {
+ if (names.contains(config.getName())) {
+ toDelete.add(config);
+ }
+ }
+ if (this.containerFlows.removeAll(toDelete) == false) {
+ return new Status(StatusCode.INTERNALERROR, "Unable to remove the flow spec configuration(s)");
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ public List<ContainerFlowConfig> getContainerFlowConfigs(Set<String> names) {
+ List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>(names.size());
+ for (String name : names) {
+ ContainerFlowConfig conf = this.getContainerFlowConfig(name);
+ if (conf != null) {
+ list.add(new ContainerFlowConfig(conf));
+ }
+ }
+ return list;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((containerFlows == null) ? 0 : containerFlows.hashCode());
+ result = prime * result + ((ports == null) ? 0 : ports.hashCode());
+ result = prime * result + ((container == null) ? 0 : container.hashCode());
+ result = prime * result + ((staticVlan == null) ? 0 : staticVlan.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ContainerConfig other = (ContainerConfig) obj;
+ if (containerFlows == null) {
+ if (other.containerFlows != null) {
+ return false;
+ }
+ } else if (!containerFlows.equals(other.containerFlows)) {
+ return false;
+ }
+ if (ports == null) {
+ if (other.ports != null) {
+ return false;
+ }
+ } else if (!ports.equals(other.ports)) {
+ return false;
+ }
+ if (container == null) {
+ if (other.container != null) {
+ return false;
+ }
+ } else if (!container.equals(other.container)) {
+ return false;
+ }
+ if (staticVlan == null) {
+ if (other.staticVlan != null) {
+ return false;
+ }
+ } else if (!staticVlan.equals(other.staticVlan)) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ String vlString = "";
+ if (staticVlan != null) {
+ vlString = staticVlan;
+ }
+ return "container=" + container + ((vlString.equals("") ? "" : " static Vlan=" + vlString)) + " ports=" + ports + " flowspecs=" + containerFlows;
+ }
+
+ /**
+ * Returns whether this Container configuration object has any ports specified
+ *
+ * @return true if any port is specified, false otherwise
+ */
+ public boolean hasNodeConnectors() {
+ return (ports != null && !ports.isEmpty());
+ }
+
+ /**
+ * Returns whether this Container configuration object has any flow specs specified
+ *
+ * @return true if any flow spec is specified, false otherwise
+ */
+ public boolean hasFlowSpecs() {
+ return (containerFlows != null && !containerFlows.isEmpty());
+ }
+
+ public List<ContainerFlow> getContainerFlowSpecs() {
+ List<ContainerFlow> list = new ArrayList<ContainerFlow>();
+ if (containerFlows != null && !containerFlows.isEmpty()) {
+ for (ContainerFlowConfig flowSpec : containerFlows) {
+ for (Match match : flowSpec.getMatches()) {
+ list.add(new ContainerFlow(match));
+ }
+ }
+ }
+ return list;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+
+
+/**
+ * This Class contains all the configurations pertained to this container
+ *
+ */
+public class ContainerData implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+ private ConcurrentMap<Node, Set<NodeConnector>> swPorts;
+ private short staticVlan;
+ private List<ContainerFlow> cFlowList;
+
+ /**
+ * Default constructor
+
+ *
+ * @return constructed ContainerData
+ */
+ public ContainerData() {
+ name = null;
+ swPorts = new ConcurrentHashMap<Node, Set<NodeConnector>>();
+ staticVlan = 0;
+ cFlowList = new ArrayList<ContainerFlow>();
+ }
+
+ /**
+ * Build a ContainerData from container configuration
+ *
+ * @param conf configuration from where the ContainerData need to be extracted
+ *
+ * @return the constructed containerData
+ */
+ public ContainerData(ContainerConfig conf) {
+ /*
+ * Back-end Non-Configuration Classes store names in lower case
+ */
+ name = conf.getContainerName().toLowerCase(Locale.ENGLISH);
+ swPorts = new ConcurrentHashMap<Node, Set<NodeConnector>>();
+ cFlowList = new ArrayList<ContainerFlow>();
+ staticVlan = conf.getStaticVlanValue();
+ }
+
+ /**
+ * getter for containerName
+ *
+ *
+ * @return the container name
+ */
+ public String getContainerName() {
+ return name;
+ }
+
+ /**
+ * getter for static vlan
+ *
+ *
+ * @return attribute static vlan
+ */
+ public short getStaticVlan() {
+ return staticVlan;
+ }
+
+ /**
+ * Check if the static vlan has non-zero value in that case is
+ * assumed to be assigned
+ *
+ *
+ * @return true if static vlan non-zero
+ */
+ public boolean hasStaticVlanAssigned() {
+ return (staticVlan != 0);
+ }
+
+ /**
+ * retrieve all NodeConnectors on a given switch on the containerName
+ * used in this object
+ *
+ * @param switchId the Node for switch we want to retrieve the list of ports
+ *
+ * @return get all the ports in form of a set of nodeconnectors
+ */
+ public Set<NodeConnector> getPorts(Node switchId) {
+ Set<NodeConnector> swpinfo = swPorts.get(switchId);
+ if (swpinfo != null) {
+ return swpinfo;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the DB of all the association Node -> List of
+ * NodeConnectors composing this container
+ *
+ * @return the ConcurrentMap of all the association Node -> List
+ * of NodeConnectors
+ */
+ public ConcurrentMap<Node, Set<NodeConnector>> getSwPorts() {
+ return swPorts;
+ }
+
+ /**
+ * Add to the container a NodeConnector, that implicitely means also
+ * the Node being added
+ *
+ * @param port NodeConnector corresponding to the port being added
+ * in the container
+ */
+ public void addPortToSwitch(NodeConnector port) {
+ Node switchId = port.getNode();
+ if (switchId == null) {
+ return;
+ }
+ Set<NodeConnector> portSet = swPorts.get(switchId);
+ if (portSet == null) {
+ portSet = new HashSet<NodeConnector>();
+ swPorts.put(switchId, portSet);
+ }
+ portSet.add(port);
+ }
+
+ /**
+ * Remove from the container a NodeConnector, that implicitely means also
+ * the Node being removed
+ *
+ * @param port NodeConnector corresponding to the port being removed
+ * in the container
+ */
+ public void removePortFromSwitch(NodeConnector port) {
+ Node switchId = port.getNode();
+ if (switchId == null) {
+ return;
+ }
+ Set<NodeConnector> swpinfo = swPorts.get(switchId);
+ if (swpinfo != null) {
+ swpinfo.remove(port);
+ if (swpinfo.isEmpty()) {
+ // There are no more ports lets get ride of the switch
+ swPorts.remove(switchId);
+ }
+ }
+ }
+
+ /**
+ * Existence check of a Node in a container
+ *
+ * @param switchId See if a particular node is part of the container
+ *
+ * @return true if the node is in it else false
+ */
+ public boolean isSwitchInContainer(Node switchId) {
+ if (swPorts.containsKey(switchId)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Test for existance of NodeConnectors on a Node in a container
+ *
+ * @param switchId Node we are testing for port existance
+ *
+ * @return ture if the portset is non-empty
+ */
+ public boolean portListEmpty(Node switchId) {
+ Set<NodeConnector> swpinfo = swPorts.get(switchId);
+ if (swpinfo != null) {
+ if (!swpinfo.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void deleteFlowSpec(ContainerFlow cFlow) {
+ cFlowList.remove(cFlow);
+ }
+
+ public void addFlowSpec(ContainerFlow cFlow) {
+ cFlowList.add(cFlow);
+ }
+
+ public boolean isFlowSpecEmpty() {
+ return (cFlowList.isEmpty());
+
+ }
+
+ public boolean containsFlowSpec(ContainerFlow cFlow) {
+ return cFlowList.contains(cFlow);
+ }
+
+ public int getFlowSpecCount() {
+ return cFlowList.size();
+ }
+
+ public List<ContainerFlow> getContainerFlowSpecs() {
+ return cFlowList;
+ }
+
+ public boolean matchName(String name) {
+ return this.name.equalsIgnoreCase(name);
+ }
+
+ /**
+ * Returns all the node connectors part of the container
+ * @return The node connectors belonging to this container. The returning set is never null.
+ */
+ public Set<NodeConnector> getNodeConnectors() {
+ Set<NodeConnector> set = new HashSet<NodeConnector>();
+ for (Map.Entry<Node, Set<NodeConnector>> entry : swPorts.entrySet()) {
+ set.addAll(entry.getValue());
+ }
+ return set;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.opendaylight.controller.sal.core.UpdateType;
+
+public class ContainerFlowChangeEvent implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private List<ContainerFlowConfig> configs;
+ private UpdateType updateType;
+
+ public ContainerFlowChangeEvent(List<ContainerFlowConfig> configs, UpdateType updateType) {
+ this.configs = configs;
+ this.updateType = updateType;
+ }
+
+ public List<ContainerFlowConfig> getConfigList() {
+ return configs;
+ }
+
+ public UpdateType getUpdateType() {
+ return updateType;
+ }
+
+ @Override
+ public String toString() {
+ return "ContainerFlowChangeEvent [configs: " + configs + " updateType: " + updateType + "]";
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager;
+
+import java.io.Serializable;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.BitBufferHelper;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Flow Specification Java Object for Container Manager
+ * Represents a container flow configuration information for Container Manager.
+ *
+ * Objects of this class are serialized to and de-serialized from binary files through
+ * java serialization API when saving to/reading from Container manager startup
+ * configuration file.
+ */
+@XmlRootElement (name = "flow-spec-config")
+@XmlAccessorType(XmlAccessType.NONE)
+public class ContainerFlowConfig implements Serializable {
+ private static Logger log = LoggerFactory.getLogger(ContainerFlowConfig.class);
+
+ /** The Constant serialVersionUID. */
+ private static final long serialVersionUID = 1L;
+
+ /** The Constant regexName. */
+ private static final String regexName = "^[\\w-+.@]+$";
+
+ /** Flow Spec name. */
+ @XmlElement
+ private String name;
+
+ /** The network Source. */
+ @XmlElement
+ private String nwSrc;
+
+ /** The network Destination */
+ @XmlElement
+ private String nwDst;
+
+ /** The protocol. */
+ @XmlElement
+ private String protocol;
+
+ /** The transport source */
+ @XmlElement
+ private String tpSrc;
+
+ /** The transport destination */
+ @XmlElement
+ private String tpDst;
+
+ /* unidirectional flag
+ do not include this flag in equality check
+ @XmlElement */
+ private static boolean unidirectional = false;
+
+
+ /**
+ * Instantiates a new container flow config.
+ */
+ public ContainerFlowConfig() {
+ }
+
+ /**
+ * Instantiates a new container flow config.
+ *
+ * @param name Flow Spec configuration name
+ * @param container Container Name
+ * @param srcIP Source IP Address
+ * @param dstIP Destination IP Address
+ * @param proto Protocol
+ * @param srcPort Source Layer4 Port
+ * @param dstPort Destination Layer4 Port
+ */
+ public ContainerFlowConfig(String name, String srcIP, String dstIP, String proto, String srcPort,
+ String dstPort) {
+ this.name = name;
+ this.nwSrc = srcIP;
+ this.nwDst = dstIP;
+ this.protocol = proto;
+ this.tpSrc = srcPort;
+ this.tpDst = dstPort;
+ //this.unidirectional = false;
+ }
+
+
+ public ContainerFlowConfig(ContainerFlowConfig containerFlowConfig) {
+ this.name = containerFlowConfig.name;
+ this.nwSrc = containerFlowConfig.nwSrc;
+ this.nwDst = containerFlowConfig.nwDst;
+ this.protocol = containerFlowConfig.protocol;
+ this.tpSrc = containerFlowConfig.tpSrc;
+ this.tpDst = containerFlowConfig.tpDst;
+ //this.unidirectional = containerFlowConfig.unidirectional;
+ }
+
+ /**
+ * Returns the name of this Flow Specification
+ *
+ * @return the name of the Flow Specification
+ */
+ public String getName() {
+ // mandatory field
+ return name;
+ }
+
+ /**
+ * Returns the Source IP Address.
+ *
+ * @return the Source IP Address
+ */
+ public String getSrcIP() {
+ return (nwSrc == null || nwSrc.isEmpty()) ? null : nwSrc;
+ }
+
+ /**
+ * Returns the Destination IP Address.
+ *
+ * @return the Destination IP Address
+ */
+ public String getDstIP() {
+ return (nwDst == null || nwDst.isEmpty()) ? null : nwDst;
+ }
+
+ /**
+ * Returns the protocol.
+ *
+ * @return the protocol
+ */
+ public String getProtocol() {
+ return protocol;
+ }
+
+ /**
+ * Returns Source Layer4 Port.
+ *
+ * @return Source Layer4 Port
+ */
+ public String getSrcPort() {
+ return (tpSrc == null || tpSrc.isEmpty()) ? null : tpSrc;
+ }
+
+ /**
+ * Returns Destination Layer4 Port.
+ *
+ * @return Destination Layer4 Port
+ */
+ public String getDstPort() {
+ return (tpDst == null || tpDst.isEmpty()) ? null : tpDst;
+ }
+
+ /*
+ * @return the unidirectional flag
+ */
+ public boolean isUnidirectional() {
+ return unidirectional;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((protocol == null) ? 0 : protocol.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((nwDst == null) ? 0 : nwDst.hashCode());
+ result = prime * result + ((tpDst == null) ? 0 : tpDst.hashCode());
+ result = prime * result + ((nwSrc == null) ? 0 : nwSrc.hashCode());
+ result = prime * result + ((tpSrc == null) ? 0 : tpSrc.hashCode());
+ return result;
+ }
+
+ /*
+ * For comparison, consider that container flow can have empty fields
+ */
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ /*
+ * Configuration will be stored in collection only if it is valid
+ * Hence we don't check here for uninitialized fields
+ */
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ContainerFlowConfig other = (ContainerFlowConfig) obj;
+ if (matchName(other) && matchSrcIP(other)
+ && matchDstIP(other) && matchProtocol(other)
+ && matchSrcPort(other) && matchDstPort(other)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Equals by Flow Spec name.
+ *
+ * @param name flow spec name for comparison
+ * @return true, if successful
+ */
+ public boolean equalsByName(String name) {
+ return this.name.equals(name);
+ }
+
+ /**
+ * equalsByMatch
+ *
+ * @param that ContainerFlowConfig for comparison
+ * @return true, if any match is equal
+ */
+ public boolean equalsByMatch(ContainerFlowConfig that) {
+ // get both matches
+ // the match is equal if any of the match is equal
+ List<Match> thisMatch = this.getMatches();
+ List<Match> otherMatch = that.getMatches();
+ // both the lists cannot be null
+ for(Match m1 : thisMatch) {
+ for(Match m2 : otherMatch) {
+ if(m1.equals(m2)) {
+ return true;
+ }
+ }
+ }
+ // if you have reached here without a match
+ // being found
+ // return false
+ return false;
+ }
+
+ /**
+ * Matches the name of this flow spec with that of ContainerFlowConfig parameter's flow spec.
+ *
+ * @param o the ContainerFlowConfig parameter
+ * @return true, if successful
+ */
+ private boolean matchName(ContainerFlowConfig flowSpec) {
+ if (name == flowSpec.name) {
+ return true;
+ }
+ if (name == null || flowSpec.name == null) {
+ return false;
+ }
+ return name.equals(flowSpec.name);
+ }
+
+
+ /**
+ * Match Source IP Address.
+ *
+ * @param flowSpec Flow Specification
+ * @return true, if successful
+ */
+ private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
+ if (nwSrc == flowSpec.nwSrc) {
+ return true;
+ }
+ if (nwSrc == null || flowSpec.nwSrc == null) {
+ return false;
+ }
+ return nwSrc.equals(flowSpec.nwSrc);
+ }
+
+ /**
+ * Match Destination IP Address.
+ *
+ * @param flowSpec Flow Specification
+ * @return true, if successful
+ */
+ private boolean matchDstIP(ContainerFlowConfig flowSpec) {
+ if (nwDst == flowSpec.nwDst) {
+ return true;
+ }
+ if (nwDst == null || flowSpec.nwDst == null) {
+ return false;
+ }
+ return this.nwDst.equals(flowSpec.nwDst);
+ }
+
+ /**
+ * Match protocol.
+ *
+ * @param flowSpec Flow Specification
+ * @return true, if successful
+ */
+ private boolean matchProtocol(ContainerFlowConfig flowSpec) {
+ if (protocol == flowSpec.protocol) {
+ return true;
+ }
+ if (protocol == null || flowSpec.protocol == null) {
+ return false;
+ }
+ return this.protocol.equals(flowSpec.protocol);
+ }
+
+ /**
+ * Match Source Layer4 Port.
+ *
+ * @param flowSpec Flow Specification
+ * @return true, if successful
+ */
+ private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
+ if (tpSrc == flowSpec.tpSrc) {
+ return true;
+ }
+ if (tpSrc == null || flowSpec.tpSrc == null) {
+ return false;
+ }
+ return tpSrc.equals(flowSpec.tpSrc);
+ }
+
+ /**
+ * Match Destination Layer4 Port.
+ *
+ * @param flowSpec Flow Specification
+ * @return true, if successful
+ */
+ private boolean matchDstPort(ContainerFlowConfig flowSpec) {
+ if (tpDst == flowSpec.tpDst) {
+ return true;
+ }
+ if (tpDst == null || flowSpec.tpDst == null) {
+ return false;
+ }
+ return this.tpDst.equals(flowSpec.tpDst);
+ }
+
+ /**
+ * Returns the Source IP Address mask length.
+ *
+ * @return the Source IP Address mask length
+ */
+ public Short getSrcIPMaskLen() {
+ Short maskLen = 0;
+
+ if (nwSrc != null && !nwSrc.isEmpty()) {
+ String[] s = nwSrc.split("/");
+ if (s.length == 2) {
+ try {
+ maskLen = Short.valueOf(s[1]);
+ } catch (Exception e) {
+ // no mask or bad mask
+ }
+ } else {
+ InetAddress ip = this.getSrcIPNum();
+ maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
+ }
+ }
+ return maskLen;
+ }
+
+ /**
+ * Returns the Destination IP Address mask length.
+ *
+ * @return the Destination IP Address mask length
+ */
+ public Short getDstIPMaskLen() {
+ Short maskLen = 0;
+ if (nwDst != null && !nwDst.isEmpty()) {
+ String[] s = nwDst.split("/");
+ if (s.length == 2) {
+ try {
+ maskLen = Short.valueOf(s[1]);
+ } catch (Exception e) {
+ // no mask or bad mask
+ }
+ } else {
+ InetAddress ip = this.getDstIPNum();
+ maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
+ }
+ }
+ return maskLen;
+ }
+
+ /**
+ * Returns the Source IP Address.
+ *
+ * @return the Source IP Address
+ */
+ public InetAddress getSrcIPNum() {
+ InetAddress ip = null;
+ if (nwSrc == null || nwSrc.isEmpty()) {
+ try {
+ ip = InetAddress.getByAddress(new byte[16]);
+ return ip;
+ } catch (UnknownHostException e) {
+ log.error("", e);
+ return null;
+ }
+ }
+ try {
+ ip = InetAddress.getByName(nwSrc.split("/")[0]);
+ } catch (UnknownHostException e1) {
+ log.error("", e1);
+ return null;
+ }
+ return ip;
+ }
+
+ /**
+ * Returns the Destination IP Address.
+ *
+ * @return the Destination IP Address
+ */
+ public InetAddress getDstIPNum() {
+ InetAddress ip = null;
+ if (nwDst == null || nwDst.isEmpty()) {
+ try {
+ ip = InetAddress.getByAddress(new byte[16]);
+ return ip;
+ } catch (UnknownHostException e) {
+ log.error("",e);
+ return null;
+ }
+ }
+ try {
+ ip = InetAddress.getByName(nwDst.split("/")[0]);
+ } catch (UnknownHostException e1) {
+ log.error("", e1);
+ return null;
+ }
+ return ip;
+ }
+
+ /**
+ * Returns Source Layer4 Port number.
+ *
+ * @return Source Layer4 Port number
+ */
+ public Short getSrcPortNum() {
+ return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
+ : Short.valueOf(tpSrc);
+ }
+
+ /**
+ * Returns Destination Layer4 Port number.
+ *
+ * @return Destination Layer4 Port number
+ */
+ public Short getDstPortNum() {
+ return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
+ : Short.valueOf(tpDst);
+ }
+
+ /**
+ * Returns the protocol
+ *
+ * @return the protocol
+ */
+ public Short getProtoNum() {
+ return protocol == null ? IPProtocols.ANY.shortValue() : IPProtocols.getProtocolNumberShort(protocol);
+ }
+
+ /**
+ * Returns whether this container flow overlap with the passed one This is
+ * true when any two of the resulting matches for the two container flow
+ * configurations intersect.
+ *
+ * @param other
+ * the other container flow config with which checking the
+ * overlap
+ * @return true if the two configurations overlap, false otherwise
+ */
+ public boolean overlap(ContainerFlowConfig other) {
+ if (other == null) {
+ return false;
+ }
+ List<Match> myMathes = this.getMatches();
+ List<Match> hisMatches = other.getMatches();
+ for (Match mine : myMathes) {
+ for (Match his : hisMatches) {
+ if (mine.intersetcs(his)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if this flow specification configuration is valid.
+ *
+ * @return true, if is valid
+ */
+ public Status validate() {
+ if (!hasValidName()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid name");
+ }
+ Status status = validateIPs();
+ if (!status.isSuccess()) {
+ return status;
+ }
+ if(!hasValidProtocol()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
+ }
+ if (this.getMatches().get(0).getMatches() == 0) {
+ return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ /**
+ * Checks if this flow specification configuration has a valid name.
+ *
+ * @return true, if successful
+ */
+ private boolean hasValidName() {
+ return (name != null && !name.isEmpty() && name.matches(regexName));
+ }
+
+ /**
+ * Validates the network addresses, checks syntax and semantic
+ *
+ * @return the result of the check as Status object, if successful
+ */
+ private Status validateIPs() {
+ if (nwSrc != null) {
+ if (!NetUtils.isIPAddressValid(nwSrc)) {
+ return new Status(StatusCode.BADREQUEST, "Invalid network source address");
+ }
+ byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
+ long prefix = BitBufferHelper.getLong(bytePrefix);
+ if (prefix == 0) {
+ return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
+ }
+ }
+ if (nwDst != null) {
+ if (!NetUtils.isIPAddressValid(nwDst)) {
+ return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
+ }
+ byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
+ long prefix = BitBufferHelper.getLong(bytePrefix);
+ if (prefix == 0) {
+ return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ private boolean hasValidProtocol() {
+ if (protocol != null && !protocol.isEmpty()) {
+ return (this.getProtoNum() != 0 || protocol.equalsIgnoreCase("any"));
+ }
+ return true;
+ }
+ /**
+ * Returns the matches.
+ * If unidirectional flag is set, there will be only one match in the list
+ * If unidirectional flag is unset there will be two matches in the list,
+ * only if the specified flow has an intrinsic direction.
+ * For Ex. if the cFlow only has the protocol field configured, no matter
+ * if unidirectional flag is set or not, only one match will be returned
+ * The client just has to iterate over the returned list
+ * @return the matches
+ */
+ public List<Match> getMatches() {
+ List<Match> matches = new ArrayList<Match>();
+ Match match = new Match();
+
+ if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
+ String parts[] = this.nwSrc.split("/");
+ InetAddress ip = NetUtils.parseInetAddress(parts[0]);
+ InetAddress mask = null;
+ int maskLen = 0;
+ if (parts.length > 1) {
+ maskLen = Integer.parseInt(parts[1]);
+ } else {
+ maskLen = (ip instanceof Inet6Address) ? 128 : 32;
+ }
+ mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
+ match.setField(MatchType.NW_SRC, ip, mask);
+ }
+ if (this.nwDst != null && !this.nwDst.trim().isEmpty()) {
+ String parts[] = this.nwDst.split("/");
+ InetAddress ip = NetUtils.parseInetAddress(parts[0]);
+ InetAddress mask = null;
+ int maskLen = 0;
+ if (parts.length > 1) {
+ maskLen = Integer.parseInt(parts[1]);
+ } else {
+ maskLen = (ip instanceof Inet6Address) ? 128 : 32;
+ }
+ mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
+ match.setField(MatchType.NW_DST, ip, mask);
+ }
+ if (this.protocol != null && !this.protocol.trim().isEmpty() && !this.protocol.equalsIgnoreCase("any")) {
+ match.setField(MatchType.NW_PROTO, IPProtocols
+ .getProtocolNumberByte(this.protocol));
+ }
+ if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
+ Short srcPort = 0;
+ try {
+ srcPort = Short.parseShort(tpSrc);
+ } catch (NumberFormatException e) {
+ throw e;
+ }
+ match.setField(MatchType.TP_SRC, srcPort);
+ }
+ if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
+ Short dstPort = 0;
+ try {
+ dstPort = Short.parseShort(tpDst);
+ } catch (NumberFormatException e) {
+ throw e;
+ }
+ match.setField(MatchType.TP_DST, dstPort);
+ }
+
+ matches.add(match);
+ if(!ContainerFlowConfig.unidirectional) {
+ Match reverse = match.reverse();
+ if (!match.equals(reverse)) {
+ matches.add(reverse);
+ }
+ }
+ return matches;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Container Flow={name:" + name + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
+ + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";
+ }
+}
package org.opendaylight.controller.containermanager;
import java.util.List;
-
+import java.util.Map;
+import java.util.Set;
import org.opendaylight.controller.sal.utils.Status;
/**
- * Container Manager Interface - provides methods to get information on existing OSGI containers
- *
+ * Container Manager provides an ability for the Network Administrators to
+ * partitions a production network into smaller, isolated and manageable
+ * networks. IContainerManager interface exposes these Container management capabilities
+ * via the supported APIs
*/
public interface IContainerManager {
+ /**
+ * Create a Container
+ *
+ * @param configObject
+ * ContainerConfig object that carries name of the Container to be
+ * created
+ * @return returns the status code of adding a container
+ */
+ public Status addContainer(ContainerConfig configObject);
+
+ /**
+ * Remove a container
+ *
+ * @param configObject
+ * ContainerConfig object that carries the name of the Container to be
+ * removed
+ * @return returns the status code of removing a container
+ */
+ public Status removeContainer(ContainerConfig configObject);
+
+ /**
+ * Remove a container
+ *
+ * @param containerName
+ * the container name
+ * @return returns the status code of removing a container
+ */
+ public Status removeContainer(String containerName);
+
+ /**
+ * Adds resources to a given container. Updates the container data based on new
+ * resources added
+ *
+ * @param configObject
+ * refer to {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+ * ContainerConfig}
+ * @return returns the status code of adding a container entry
+ */
+ public Status addContainerEntry(String containerName, List<String> portList);
+
+ /**
+ * Remove a resource from a given container. Updates the container data based on new
+ * resources removed.
+ *
+ * @param configObject
+ * refer to {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+ * ContainerConfig}
+ * @return returns the status code of removing a container entry
+ */
+ public Status removeContainerEntry(String containerName, List<String> portList);
+
+ /**
+ * Adds/Removes a container flow
+ *
+ * @param configObject
+ * refer to {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+ * ContainerConfig}
+ * @return returns the status code of adding a container flow
+ */
+ public Status addContainerFlows(String containerName, List<ContainerFlowConfig> configObject);
+
+ /**
+ * Remove a container flow
+ *
+ * @param configObject
+ * refer to {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+ * ContainerConfig}
+ * @return returns the status of removing a container flow
+ */
+ public Status removeContainerFlows(String containerName, List<ContainerFlowConfig> configObject);
+
+ /**
+ * Remove a container flow
+ *
+ * @param containerName
+ * the name of the container
+ * @param name
+ * the name of the container flow
+ * @return the status of the request
+ */
+ public Status removeContainerFlows(String containerName, Set<String> name);
+
+ /**
+ * Get the list of {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+ * ContainerConfig} objects representing all the containers that have been
+ * configured previously.
+ *
+ * @return the lsit of {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+ * ContainerConfig} objects configured so far
+ */
+ public List<ContainerConfig> getContainerConfigList();
+
+ /**
+ * Get the configuration object for the specified container
+ *
+ * @param containerName
+ * the name of the container
+ * @return a copy of the {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+ * ContainerConfig} object for the specified container if present, null
+ * otherwise
+ */
+ public ContainerConfig getContainerConfig(String containerName);
+
+ /**
+ * Returns a list of container names that currently exist.
+ *
+ * @return array of String container names
+ */
+ public List<String> getContainerNameList();
+
+ /**
+ * Check for the existence of a container
+ *
+ * @param ContainerId
+ * Name of the Container
+ *
+ * @return true if it exists, false otherwise
+ */
+ public boolean doesContainerExist(String ContainerId);
+
+ /**
+ * Get an array of ContainerFlowConfig objects representing all the
+ * container flows that have been configured previously.
+ *
+ * @return array of {@link org.opendaylight.controller.containermanager.ContainerFlowConfig
+ * ContainerFlowConfig}
+ */
+ public Map<String, List<ContainerFlowConfig>> getContainerFlows();
+
+ /**
+ * Get an array of {@link org.opendaylight.controller.containermanager.ContainerFlowConfig
+ * ContainerFlowConfig} objects representing all the container flows that
+ * have been configured previously on the given containerName
+ *
+ * @param containerName
+ * the container name
+ * @return array of {@link org.opendaylight.controller.containermanager.ContainerFlowConfig
+ * ContainerFlowConfig}
+ */
+ public List<ContainerFlowConfig> getContainerFlows(String containerName);
+
+ /**
+ * Get an the list of names of the container flows that have been configured
+ * previously on the given containerName
+ *
+ * @param containerName
+ * the container name
+ * @return the array containing the names of the container flows configured
+ * on the specified container
+ */
+ public List<String> getContainerFlowNameList(String containerName);
+
/**
* Returns true if there are any non-default Containers present.
*
* @return List of Container name strings.
*/
public List<String> getContainerNames();
-
- /**
- * Save the current container configuration to disk.
- * TODO : REMOVE THIS FUNCTION and make Save as a service rather than the
- * current hack of calling individual save routines.
- *
- * @return status code
- */
- @Deprecated
- public Status saveContainerConfig();
}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.UpdateType;
+
+/**
+ * Class that represent the event of a configuration change for a container
+ */
+public class NodeConnectorsChangeEvent implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private List<NodeConnector> ncList;
+ private UpdateType updateType;
+
+ public NodeConnectorsChangeEvent(List<NodeConnector> ncList, UpdateType updateType) {
+ this.ncList = ncList;
+ this.updateType = updateType;
+ }
+
+ public List<NodeConnector> getNodeConnectors() {
+ return ncList;
+ }
+
+ public UpdateType getUpdateType() {
+ return updateType;
+ }
+
+ @Override
+ public String toString() {
+ return "ContainerConnectorsChangeEvent [ncList: " + ncList + " updateType: " + updateType + "]";
+ }
+}
</scm>
<artifactId>containermanager.implementation</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<build>
<instructions>
<Import-Package>
org.opendaylight.controller.containermanager,
+ org.opendaylight.controller.appauth.authorization,
+ org.opendaylight.controller.usermanager,
+ org.opendaylight.controller.configuration,
org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.sal.authorization,
org.opendaylight.controller.sal.packet,
org.opendaylight.controller.sal.utils,
org.opendaylight.controller.sal.core,
org.opendaylight.controller.sal.flowprogrammer,
org.opendaylight.controller.sal.match,
org.opendaylight.controller.sal.reader,
+ org.opendaylight.controller.topologymanager,
org.eclipse.osgi.framework.console,
org.osgi.framework,
org.slf4j,
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>configuration</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>appauth</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>topologymanager</artifactId>
<version>0.4.0-SNAPSHOT</version>
</dependency>
<dependency>
<artifactId>sal</artifactId>
<version>0.5.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>usermanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
</dependencies>
</project>
package org.opendaylight.controller.containermanager.internal;
-import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Hashtable;
import org.opendaylight.controller.containermanager.IContainerManager;
-import org.opendaylight.controller.sal.core.IContainerAware;
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.containermanager.IContainerAuthorization;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
import org.opendaylight.controller.sal.core.IContainer;
+import org.opendaylight.controller.sal.core.IContainerAware;
import org.opendaylight.controller.sal.core.IContainerListener;
-import org.apache.felix.dm.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
public class Activator extends ComponentActivatorAbstractBase {
- protected static final Logger logger = LoggerFactory
- .getLogger(Activator.class);
+ protected static final Logger logger = LoggerFactory.getLogger(Activator.class);
/**
- * Function called when the activator starts just after some
- * initializations are done by the
- * ComponentActivatorAbstractBase.
+ * Function called when the activator starts just after some initializations
+ * are done by the ComponentActivatorAbstractBase.
*
*/
+ @Override
public void init() {
}
/**
- * Function called when the activator stops just before the
- * cleanup done by ComponentActivatorAbstractBase
+ * Function called when the activator stops just before the cleanup done by
+ * ComponentActivatorAbstractBase
*
*/
+ @Override
public void destroy() {
}
/**
- * Function that is used to communicate to dependency manager the
- * list of known implementations for services inside a container
+ * Function that is used to communicate to dependency manager the list of
+ * known implementations for containerd-services
*
*
* @return An array containing all the CLASS objects that will be
- * instantiated in order to get an fully working implementation
- * Object
+ * instantiated in order to get an fully working implementation
+ * Object
*/
+ @Override
public Object[] getImplementations() {
Object[] res = { ContainerImpl.class };
return res;
}
/**
- * Function that is called when configuration of the dependencies
- * is required.
+ * Function that is called when configuration of the dependencies is
+ * required.
*
- * @param c dependency manager Component object, used for
- * configuring the dependencies exported and imported
- * @param imp Implementation class that is being configured,
- * needed as long as the same routine can configure multiple
- * implementations
- * @param containerName The containerName being configured, this allow
- * also optional per-container different behavior if needed, usually
- * should not be the case though.
+ * @param c
+ * dependency manager Component object, used for configuring the
+ * dependencies exported and imported
+ * @param imp
+ * Implementation class that is being configured, needed as long
+ * as the same routine can configure multiple implementations
+ * @param containerName
+ * The containername being configured, this allow also optional
+ * per-container different behavior if needed, usually should not be
+ * the case though.
*/
+ @Override
public void configureInstance(Component c, Object imp, String containerName) {
if (imp.equals(ContainerImpl.class)) {
// export the service
c.setInterface(new String[] { IContainer.class.getName() }, null);
+
+ // private interface exported by ContainerManager to retrieve
+ // internal data, this is mandatory to implement the
+ // IContainer functionality
+ c.add(createServiceDependency().setService(IContainerInternal.class)
+ .setCallbacks("setIContainerInternal", "unsetIContainerInternal")
+ .setRequired(true));
}
}
/**
- * Method which tells how many Global implementations are
- * supported by the bundle. This way we can tune the number of
- * components created. This components will be created ONLY at the
- * time of bundle startup and will be destroyed only at time of
- * bundle destruction, this is the major difference with the
- * implementation retrieved via getImplementations where all of
- * them are assumed to be in a container!
+ * Method which tells how many NON-containerd implementations are supported by
+ * the bundle. This way we can tune the number of components created. This
+ * components will be created ONLY at the time of bundle startup and will be
+ * destroyed only at time of bundle destruction, this is the major
+ * difference with the implementation retrieved via getImplementations where
+ * all of them are assumed to be containerd!
*
*
- * @return The list of implementations the bundle will support,
- * in Global version
+ * @return The list of implementations the bundle will support, in
+ * non-containerd version
*/
+ @Override
protected Object[] getGlobalImplementations() {
Object[] res = { ContainerManager.class };
return res;
}
/**
- * Configure the dependency for a given instance Global
+ * Configure the dependency for a given instance non-containerd
*
- * @param c Component assigned for this instance, this will be
- * what will be used for configuration
- * @param imp implementation to be configured
- * @param containerName container on which the configuration happens
+ * @param c
+ * Component assigned for this instance, this will be what will
+ * be used for configuration
+ * @param imp
+ * implementation to be configured
+ * @param containerName
+ * container on which the configuration happens
*/
+ @Override
protected void configureGlobalInstance(Component c, Object imp) {
if (imp.equals(ContainerManager.class)) {
+ Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
+ Set<String> propSet = new HashSet<String>();
+ propSet.add("containermgr.event.containerChange");
+ props.put("cachenames", propSet);
// export the service
- c.setInterface(new String[] { IContainerManager.class.getName() },
- null);
+ c.setInterface(
+ new String[] { IContainerManager.class.getName(),
+ IContainerManager.class.getName(),
+ IConfigurationAware.class.getName(),
+ CommandProvider.class.getName(),
+ IContainerInternal.class.getName(),
+ IContainerAuthorization.class.getName(),
+ ICacheUpdateAware.class.getName()}, props);
- c.add(createServiceDependency().setService(
- IClusterGlobalServices.class).setCallbacks(
- "setClusterServices", "unsetClusterServices").setRequired(
- true));
+ c.add(createServiceDependency()
+ .setService(IClusterGlobalServices.class)
+ .setCallbacks("setClusterServices", "unsetClusterServices")
+ .setRequired(true));
// Key kick-starter for container creation in each component
c.add(createServiceDependency().setService(IContainerAware.class)
// protocol plugins to setup proper filtering based on
// slicing events
c.add(createServiceDependency()
- .setService(IContainerListener.class).setCallbacks(
- "setIContainerListener", "unsetIContainerListener")
+ .setService(IContainerListener.class)
+ .setCallbacks("setIContainerListener", "unsetIContainerListener")
.setRequired(false));
}
}
* @brief Class that instantiated per-container implements the
* interface IContainer
*
- *
*/
package org.opendaylight.controller.containermanager.internal;
+import java.util.concurrent.ConcurrentMap;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Dictionary;
+import java.util.Map;
+
import org.apache.felix.dm.Component;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.IContainer;
+import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.core.NodeConnector;
+
+import org.opendaylight.controller.containermanager.ContainerData;
+
import java.util.Set;
-import org.opendaylight.controller.sal.core.Node;
-import org.opendaylight.controller.sal.core.ContainerFlow;
import java.util.List;
-import org.opendaylight.controller.sal.core.IContainer;
public class ContainerImpl implements IContainer {
private String containerName = null;
+ private IContainerInternal iContainerInternal = null;
+
+ public void setIContainerInternal(IContainerInternal s) {
+ this.iContainerInternal = s;
+ }
+
+ public void unsetIContainerInternal(IContainerInternal s) {
+ if (this.iContainerInternal == s) {
+ this.iContainerInternal = null;
+ }
+ }
/**
* Function called by the dependency manager when all the required
@Override
public List<ContainerFlow> getContainerFlows() {
- return null;
+ List<ContainerFlow> list = new ArrayList<ContainerFlow>();
+
+ ContainerData d = this.iContainerInternal.getContainerData(this.containerName);
+ if (d != null) {
+ list.addAll(d.getContainerFlowSpecs());
+ }
+ return list;
}
@Override
public short getTag(Node n) {
+ ContainerData d = this.iContainerInternal.getContainerData(this.containerName);
+ if (d != null) {
+ return d.getStaticVlan();
+ }
+ // Return 0 because in containerData that means an unassigned tag
return (short) 0;
}
@Override
public Set<NodeConnector> getNodeConnectors() {
- return null;
+ Set<NodeConnector> set = new HashSet<NodeConnector>();
+
+ ContainerData d = this.iContainerInternal.getContainerData(this.containerName);
+ if (d != null) {
+ ConcurrentMap<Node, Set<NodeConnector>> m = d.getSwPorts();
+ if (m != null) {
+ for (Map.Entry<Node, Set<NodeConnector>> entry : m.entrySet()) {
+ set.addAll(entry.getValue());
+ }
+ }
+ }
+ return set;
}
@Override
public Set<Node> getNodes() {
- return null;
+ Set<Node> set = new HashSet<Node>();
+
+ ContainerData d = this.iContainerInternal.getContainerData(this.containerName);
+ if (d != null) {
+ ConcurrentMap<Node, Set<NodeConnector>> m = d.getSwPorts();
+ if (m != null) {
+ set.addAll(m.keySet());
+ }
+ }
+ return set;
}
}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-/**
- * @file ContainerManager.java
- *
- * @brief Manage one or many Containers
- *
- *
- */
package org.opendaylight.controller.containermanager.internal;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.configuration.IConfigurationService;
+import org.opendaylight.controller.containermanager.IContainerAuthorization;
import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.sal.authorization.AppRoleLevel;
+import org.opendaylight.controller.sal.authorization.Privilege;
+import org.opendaylight.controller.sal.authorization.Resource;
+import org.opendaylight.controller.sal.authorization.ResourceGroup;
+import org.opendaylight.controller.sal.authorization.UserLevel;
+import org.opendaylight.controller.sal.core.ContainerFlow;
import org.opendaylight.controller.sal.core.IContainerAware;
import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.match.Match;
import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.IObjectReader;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.opendaylight.controller.sal.utils.ObjectReader;
+import org.opendaylight.controller.sal.utils.ObjectWriter;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.appauth.authorization.Authorization;
+import org.opendaylight.controller.containermanager.ContainerFlowChangeEvent;
+import org.opendaylight.controller.containermanager.ContainerFlowConfig;
+import org.opendaylight.controller.containermanager.NodeConnectorsChangeEvent;
+import org.opendaylight.controller.containermanager.ContainerChangeEvent;
+import org.opendaylight.controller.containermanager.ContainerConfig;
+import org.opendaylight.controller.containermanager.ContainerData;
-public class ContainerManager implements IContainerManager {
- private static final Logger logger = LoggerFactory
- .getLogger(ContainerManager.class);
+public class ContainerManager extends Authorization<String> implements IContainerManager, IObjectReader,
+ CommandProvider, ICacheUpdateAware<String, Object>, IContainerInternal, IContainerAuthorization,
+ IConfigurationAware {
+ private static final Logger logger = LoggerFactory.getLogger(ContainerManager.class);
+ private static String ROOT = GlobalConstants.STARTUPHOME.toString();
+ private static String containersFileName = ROOT + "containers.conf";
+ private static final String allContainersGroup = "allContainers";
private IClusterGlobalServices clusterServices;
/*
- * Collection containing the configuration objects.
- * This is configuration world: container names (also the map key)
- * are maintained as they were configured by user, same case
+ * Collection containing the configuration objects. This is configuration
+ * world: container names (also the map key) are maintained as they were
+ * configured by user, same case
*/
- private Set<IContainerAware> iContainerAware = (Set<IContainerAware>) Collections
- .synchronizedSet(new HashSet<IContainerAware>());
- private Set<IContainerListener> iContainerListener = Collections
+ private ConcurrentMap<String, ContainerConfig> containerConfigs;
+ private ConcurrentMap<String, ContainerData> containerData;
+ private ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>> nodeConnectorToContainers;
+ private ConcurrentMap<Node, Set<String>> nodeToContainers;
+ private ConcurrentMap<String, Object> containerChangeEvents;
+ private final Set<IContainerAware> iContainerAware = Collections.synchronizedSet(new HashSet<IContainerAware>());
+ private final Set<IContainerListener> iContainerListener = Collections
.synchronizedSet(new HashSet<IContainerListener>());
void setIContainerListener(IContainerListener s) {
if (this.iContainerListener != null) {
this.iContainerListener.add(s);
+ /*
+ * At boot with startup, containers are created before listeners have
+ * joined. Replaying here the first container creation notification for
+ * the joining listener when containers are already present. Also
+ * replaying all the node connectors and container flows additions
+ * to the existing containers.
+ */
+ if (!this.containerData.isEmpty()) {
+ s.containerModeUpdated(UpdateType.ADDED);
+ }
+ for (ConcurrentMap.Entry<NodeConnector, CopyOnWriteArrayList<String>> entry : nodeConnectorToContainers
+ .entrySet()) {
+ NodeConnector port = entry.getKey();
+ for (String container : entry.getValue()) {
+ s.nodeConnectorUpdated(container, port, UpdateType.ADDED);
+ }
+ }
+ for (Map.Entry<String, ContainerData> container : containerData.entrySet()) {
+ for (ContainerFlow cFlow : container.getValue().getContainerFlowSpecs()) {
+ s.containerFlowUpdated(container.getKey(), cFlow, cFlow, UpdateType.ADDED);
+ }
+ }
}
}
public void setIContainerAware(IContainerAware iContainerAware) {
if (!this.iContainerAware.contains(iContainerAware)) {
this.iContainerAware.add(iContainerAware);
- // Now call the container creation for all the known containers so
- // far
- List<String> containerDB = getContainerNames();
- if (containerDB != null) {
- for (int i = 0; i < containerDB.size(); i++) {
- iContainerAware.containerCreate(containerDB.get(i));
- }
+ // Now call the container creation for all the known containers so far
+ for (String container : getContainerNameList()) {
+ iContainerAware.containerCreate(container.toLowerCase(Locale.ENGLISH));
}
}
}
}
}
+ private void allocateCaches() {
+ logger.debug("Container Manager allocating caches");
+
+ if (clusterServices == null) {
+ logger.warn("un-initialized Cluster Services, can't allocate caches");
+ return;
+ }
+ try {
+ clusterServices.createCache("containermgr.containerConfigs", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+ clusterServices.createCache("containermgr.event.containerChange",
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+ clusterServices.createCache("containermgr.containerData", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+ clusterServices.createCache("containermgr.nodeConnectorToContainers",
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+ clusterServices.createCache("containermgr.nodeToContainers", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+ clusterServices.createCache("containermgr.containerGroups", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+ clusterServices.createCache("containermgr.containerAuthorizations",
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+ clusterServices.createCache("containermgr.roles", EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+ } catch (CacheConfigException cce) {
+ logger.error("Cache configuration invalid - check cache mode");
+ } catch (CacheExistException ce) {
+ logger.error("Cache already exits - destroy and recreate if needed");
+ }
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ private void retrieveCaches() {
+ logger.debug("Container Manager retrieving caches");
+
+ if (clusterServices == null) {
+ logger.warn("un-initialized Cluster Services, can't retrieve caches");
+ return;
+ }
+
+ containerConfigs = (ConcurrentMap<String, ContainerConfig>) clusterServices.getCache("containermgr.containerConfigs");
+
+ containerChangeEvents = (ConcurrentMap<String, Object>) clusterServices.getCache("containermgr.event.containerChange");
+
+ containerData = (ConcurrentMap<String, ContainerData>) clusterServices.getCache("containermgr.containerData");
+
+ nodeConnectorToContainers = (ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>>) clusterServices
+ .getCache("containermgr.nodeConnectorToContainers");
+
+ nodeToContainers = (ConcurrentMap<Node, Set<String>>) clusterServices.getCache("containermgr.nodeToContainers");
+
+ resourceGroups = (ConcurrentMap<String, Set<String>>) clusterServices.getCache("containermgr.containerGroups");
+
+ groupsAuthorizations = (ConcurrentMap<String, Set<ResourceGroup>>) clusterServices
+ .getCache("containermgr.containerAuthorizations");
+
+ roles = (ConcurrentMap<String, AppRoleLevel>) clusterServices.getCache("containermgr.roles");
+
+ if (containerConfigs.size() > 0) {
+ for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+ notifyContainerChangeInternal(entry.getValue(), UpdateType.ADDED);
+ }
+ }
+ }
+
+ @Override
+ public void entryCreated(String containerName, String cacheName, boolean originLocal) {
+
+ }
+
+ @Override
+ public void entryUpdated(String key, Object value, String cacheName, boolean originLocal) {
+ if (!originLocal) {
+ if (value instanceof NodeConnectorsChangeEvent) {
+ NodeConnectorsChangeEvent event = (NodeConnectorsChangeEvent) value;
+ List<NodeConnector> ncList = event.getNodeConnectors();
+ notifyContainerEntryChangeInternal(key, ncList, event.getUpdateType());
+ } else if (value instanceof ContainerFlowChangeEvent) {
+ ContainerFlowChangeEvent event = (ContainerFlowChangeEvent) value;
+ notifyCFlowChangeInternal(key, event.getConfigList(), event.getUpdateType());
+ } else if (value instanceof ContainerChangeEvent) {
+ ContainerChangeEvent event = (ContainerChangeEvent) value;
+ notifyContainerChangeInternal(event.getConfig(), event.getUpdateType());
+ }
+ }
+ }
+
+ @Override
+ public void entryDeleted(String containerName, String cacheName, boolean originLocal) {
+ }
+
+ public ContainerManager() {
+ }
+
public void init() {
- logger.info("ContainerManager startup....");
+
+ }
+
+ public void start() {
+ // Get caches from cluster manager
+ allocateCaches();
+ retrieveCaches();
+
+ // Allocates default groups and association to default roles
+ createDefaultAuthorizationGroups();
+
+ // Read startup configuration and create local database
+ loadConfigurations();
}
public void destroy() {
// Clear local states
this.iContainerAware.clear();
this.iContainerListener.clear();
+ }
+
+ /**
+ * Adds/Remove the list of flow specs to/from the specified container. This
+ * function is supposed to be called after all the validation checks have
+ * already been run on the proposed configuration.
+ */
+ private Status updateContainerFlow(String containerName, List<ContainerFlowConfig> confList, boolean delete) {
+ ContainerData container = getContainerByName(containerName);
+ if (container == null) {
+ return new Status(StatusCode.GONE, "Container not present");
+ }
+
+ for (ContainerFlowConfig conf : confList) {
+ // Validation was fine. Modify the database now.
+ for (Match match : conf.getMatches()) {
+ ContainerFlow cFlow = new ContainerFlow(match);
+ if (delete) {
+ logger.trace("Removing Flow Spec %s from Container {}", conf.getName(), containerName);
+ container.deleteFlowSpec(cFlow);
+ } else {
+ logger.trace("Adding Flow Spec %s to Container {}", conf.getName(), containerName);
+ container.addFlowSpec(cFlow);
+
+ }
+ // Update Database
+ putContainerDataByName(containerName, container);
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ /**
+ * Adds/Remove this container to/from the Container database, no updates are going
+ * to be generated here other that the destroying and creation of the container.
+ * This function is supposed to be called after all the validation checks
+ * have already been run on the configuration object
+ */
+ private Status updateContainerDatabase(ContainerConfig containerConf, boolean delete) {
+ /*
+ * Back-end world here, container names are all stored in lower case
+ */
+ String containerName = containerConf.getContainerName();
+ ContainerData container = getContainerByName(containerName);
+ if (delete && container == null) {
+ return new Status(StatusCode.NOTFOUND, "Container is not present");
+ }
+ if (!delete && container != null) {
+ // A container with the same (lower case) name already exists
+ return new Status(StatusCode.CONFLICT, "A container with the same name already exists");
+ }
+ if (delete) {
+ logger.debug("Removing container {}", containerName);
+ removeNodeToContainersMapping(container);
+ removeNodeConnectorToContainersMapping(container);
+ removeContainerDataByName(containerName);
+ } else {
+ logger.debug("Adding container {}", containerName);
+ container = new ContainerData(containerConf);
+ putContainerDataByName(containerName, container);
+
+ // If flow specs are specified, add them
+ if (containerConf.hasFlowSpecs()) {
+ updateContainerFlow(containerName, containerConf.getContainerFlowConfigs(), delete);
+ }
+
+ // If ports are specified, add them
+ if (!containerConf.getPortList().isEmpty()) {
+ updateContainerEntryDatabase(containerName, containerConf.getPortList(), delete);
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ private void removeNodeConnectorToContainersMapping(ContainerData container) {
+ Iterator<Entry<NodeConnector, CopyOnWriteArrayList<String>>> it = nodeConnectorToContainers.entrySet().iterator();
+ String containerName = container.getContainerName();
+ for (; it.hasNext();) {
+ Entry<NodeConnector, CopyOnWriteArrayList<String>> entry = it.next();
+ final NodeConnector nc = entry.getKey();
+ final CopyOnWriteArrayList<String> slist = entry.getValue();
+ for (final String sdata : slist) {
+ if (sdata.equalsIgnoreCase(containerName)) {
+ logger.debug("Removing NodeConnector->Containers mapping, nodeConnector: {}", nc);
+ slist.remove(containerName);
+ if (slist.isEmpty()) {
+ nodeConnectorToContainers.remove(nc);
+ } else {
+ nodeConnectorToContainers.put(nc, slist);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private void removeNodeToContainersMapping(ContainerData container) {
+ for (Entry<Node, Set<String>> entry : nodeToContainers.entrySet()) {
+ Node node = entry.getKey();
+ for (String sdata : entry.getValue()) {
+ if (sdata.equals(container.getContainerName())) {
+ logger.debug("Removing Node->Containers mapping, node {} container {}", node, sdata);
+ Set<String> value = nodeToContainers.get(node);
+ value.remove(sdata);
+ nodeToContainers.put(node, value);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds/Remove container data to/from the container. This function is supposed to be
+ * called after all the validation checks have already been run on the
+ * configuration object
+ */
+ private Status updateContainerEntryDatabase(String containerName, List<NodeConnector> nodeConnectors, boolean delete) {
+ ContainerData container = getContainerByName(containerName);
+ // Presence check
+ if (container == null) {
+ return new Status(StatusCode.NOTFOUND, "Container Not Found");
+ }
+
+ // Check changes in the portlist
+ for (NodeConnector port : nodeConnectors) {
+ Node node = port.getNode();
+ if (delete) {
+ container.removePortFromSwitch(port);
+ putContainerDataByName(containerName, container);
+
+ /* remove <sp> - container mapping */
+ if (nodeConnectorToContainers.containsKey(port)) {
+ nodeConnectorToContainers.remove(port);
+ }
+ /*
+ * If no more ports in the switch, remove switch from container
+ * Generate switchRemoved Event
+ */
+ if (container.portListEmpty(node)) {
+ logger.debug("Port List empty for switch {}", node);
+ putContainerDataByName(containerName, container);
+ // remove node->containers mapping
+ Set<String> slist = nodeToContainers.get(node);
+ if (slist != null) {
+ logger.debug("Removing container from switch-container list. node{}, container{}", node, containerName);
+ slist.remove(container.getContainerName());
+ nodeToContainers.put(node, slist);
+ if (slist.isEmpty()) {
+ logger.debug("Container list empty for switch {}. removing switch-container mapping", node);
+ nodeToContainers.remove(node);
+ }
+ }
+ }
+ } else {
+ if (container.isSwitchInContainer(node) == false) {
+ Set<String> value = nodeToContainers.get(node);
+ // Add node->containers mapping
+ if (value == null) {
+ value = new HashSet<String>();
+ logger.debug("Creating new Container Set for switch {}", node);
+ }
+ value.add(container.getContainerName());
+ nodeToContainers.put(node, value);
+ }
+ container.addPortToSwitch(port);
+ putContainerDataByName(containerName, container);
+
+ // added nc->containers mapping
+ CopyOnWriteArrayList<String> slist = nodeConnectorToContainers.get(port);
+ if (slist == null) {
+ slist = new CopyOnWriteArrayList<String>();
+ }
+ slist.add(container.getContainerName());
+ nodeConnectorToContainers.put(port, slist);
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ private Status validateContainerFlowAddRemoval(String containerName, ContainerFlow cFlow, boolean delete) {
+ /*
+ * It used to be the comment below: ~~~~~~~~~~~~~~~~~~~~ If Link Sharing
+ * at Host facing interfaces, then disallow last ContainerFlow removal
+ * ~~~~~~~~~~~~~~~~~~~~ But the interface being host facing is a
+ * condition that can change at runtime and so the final effect will be
+ * unreliable. So now we will always allow the container flow removal,
+ * if this is a link host facing and is shared by many that will cause
+ * issues but that validation should be done not at the configuration
+ * but in the UI/northbound side.
+ */
+ ContainerData container = this.getContainerByName(containerName);
+ if (container == null) {
+ String error = String.format("Cannot validate flow specs for container %s: (Container does not exist)", containerName);
+ logger.warn(error);
+ return new Status(StatusCode.BADREQUEST, error);
+ }
+
+ if (delete) {
+ Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
+ // Go through all the installed containers
+ for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
+ if (containerName.equalsIgnoreCase(entry.getKey())) {
+ continue;
+ }
+ // Derive the common ports
+ Set<NodeConnector> commonPorts = entry.getValue().getNodeConnectors();
+ commonPorts.retainAll(thisContainerPorts);
+ if (commonPorts.isEmpty()) {
+ continue;
+ }
+
+ // Check if this operation would remove the only flow spec
+ // assigned to this container
+ if (container.getFlowSpecCount() == 1) {
+ if (!container.hasStaticVlanAssigned()) {
+ // Ports are shared and static vlan is not present: this
+ // is a failure
+ // regardless the shared ports are host facing or
+ // interswitch ports
+ return new Status(StatusCode.BADREQUEST, "Container shares port with another container: "
+ + "The only one flow spec assigned to this container cannot be removed,"
+ + "because this container is not assigned any static vlan");
+ }
+
+ // Check on host facing port
+ ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(
+ ITopologyManager.class, GlobalConstants.DEFAULT.toString(), this);
+ if (topologyManager == null) {
+ return new Status(StatusCode.NOSERVICE,
+ "Cannot validate the request: Required service is not available");
+ }
+ for (NodeConnector nc : commonPorts) {
+ /*
+ * Shared link case : For internal port check if it has
+ * a vlan configured. If vlan is configured, allow the
+ * flowspec to be deleted If the port is host-facing, do
+ * not allow the flowspec to be deleted
+ */
+ if (!topologyManager.isInternal(nc)) {
+ return new Status(StatusCode.BADREQUEST, String.format(
+ "Port %s is shared and is host facing port: "
+ + "The only one flow spec assigned to this container cannot be removed", nc));
+ }
+ }
+ }
+ }
+ } else {
+ // Adding a new flow spec: need to check if other containers with common
+ // ports do not have same flow spec
+ Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
+ List<ContainerFlow> proposed = new ArrayList<ContainerFlow>(container.getContainerFlowSpecs());
+ proposed.add(cFlow);
+ for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
+ if (containerName.equalsIgnoreCase(entry.getKey())) {
+ continue;
+ }
+ ContainerData otherContainer = entry.getValue();
+ Set<NodeConnector> commonPorts = otherContainer.getNodeConnectors();
+ commonPorts.retainAll(thisContainerPorts);
+
+ if (!commonPorts.isEmpty()) {
+ Status status = checkCommonContainerFlow(otherContainer.getContainerFlowSpecs(), proposed);
+ if (!status.isSuccess()) {
+ return new Status(StatusCode.BADREQUEST, String.format(
+ "Container %s which shares ports with this container has overlapping flow spec: %s",
+ entry.getKey(), status.getDescription()));
+ }
+ }
+ }
+ }
+
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ /**
+ * Checks if the passed list of node connectors can be safely applied to the
+ * specified existing container in terms of port sharing with other containers.
+ *
+ * @param containerName
+ * the name of the existing container
+ * @param portList
+ * the list of node connectors to be added to the container
+ * @return the status object representing the result of the check
+ */
+ private Status validatePortSharing(String containerName, List<NodeConnector> portList) {
+ ContainerData container = this.getContainerByName(containerName);
+ if (container == null) {
+ String error = String
+ .format("Cannot validate port sharing for container %s: (container does not exist)", containerName);
+ logger.error(error);
+ return new Status(StatusCode.BADREQUEST, error);
+ }
+ return validatePortSharingInternal(portList, container.getContainerFlowSpecs());
+ }
+
+ /**
+ * Checks if the proposed container configuration is valid to be applied in
+ * terms of port sharing with other containers.
+ *
+ * @param containerConf
+ * the container configuration object containing the list of node
+ * connectors
+ * @return the status object representing the result of the check
+ */
+ private Status validatePortSharing(ContainerConfig containerConf) {
+ return validatePortSharingInternal(containerConf.getPortList(), containerConf.getContainerFlowSpecs());
+ }
+
+ /*
+ * If any port is shared with an existing container, need flowSpec to be
+ * configured. If no flowSpec for this or other container, or if containers have any
+ * overlapping flowspec in common, then let the caller know this
+ * configuration has to be rejected.
+ */
+ private Status validatePortSharingInternal(List<NodeConnector> portList, List<ContainerFlow> flowSpecList) {
+ for (NodeConnector port : portList) {
+ List<String> slist = nodeConnectorToContainers.get(port);
+ if (slist != null && !slist.isEmpty()) {
+ for (String otherContainerName : slist) {
+ String msg = null;
+ ContainerData other = containerData.get(otherContainerName);
+ if (flowSpecList.isEmpty()) {
+ msg = String.format("Port %s is shared and flow spec is emtpy for this container", port);
+ } else if (other.isFlowSpecEmpty()) {
+ msg = String.format("Port %s is shared and flow spec is emtpy for the other container", port);
+ } else if (!checkCommonContainerFlow(flowSpecList, other.getContainerFlowSpecs()).isSuccess()) {
+ msg = String.format("Port %s is shared and other container has common flow spec", port);
+ }
+ if (msg != null) {
+ logger.debug(msg);
+ return new Status(StatusCode.BADREQUEST, msg);
+ }
+ }
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ /**
+ * Utility function to check if two lists of container flows share any same
+ * or overlapping container flows.
+ *
+ * @param oneFlowList
+ * One of the two lists of container flows to test
+ * @param twoFlowList
+ * One of the two lists of container flows to test
+ * @return The status of the check. Either SUCCESS or CONFLICT. In case of
+ * conflict, the Status will contain the description for the failed
+ * check.
+ */
+ private Status checkCommonContainerFlow(List<ContainerFlow> oneFlowList, List<ContainerFlow> twoFlowList) {
+ for (ContainerFlow oneFlow : oneFlowList) {
+ for (ContainerFlow twoFlow : twoFlowList) {
+ if (oneFlow.getMatch().intersetcs(twoFlow.getMatch())) {
+ return new Status(StatusCode.CONFLICT, String.format("Flow Specs overlap: %s %s",
+ oneFlow.getMatch(), twoFlow.getMatch()));
+ }
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
- logger.info("ContainerManager Shutdown....");
+ /**
+ * Return the ContainerData object for the passed container name. Given this is a
+ * backend database, the lower case version of the passed name is used while
+ * searching for the corresponding ContainerData object.
+ *
+ * @param name
+ * The container name in any case
+ * @return The corresponding ContainerData object
+ */
+ private ContainerData getContainerByName(String name) {
+ return containerData.get(name.toLowerCase(Locale.ENGLISH));
+ }
+
+ /**
+ * Add a ContainerData object for the given container name.
+ *
+ * @param name
+ * The container name in any case
+ * @param sData
+ * The container data object
+ */
+ private void putContainerDataByName(String name, ContainerData sData) {
+ containerData.put(name.toLowerCase(Locale.ENGLISH), sData);
+ }
+
+ /**
+ * Removes the ContainerData object for the given container name.
+ *
+ * @param name
+ * The container name in any case
+ */
+ private void removeContainerDataByName(String name) {
+ containerData.remove(name.toLowerCase(Locale.ENGLISH));
}
@Override
- public List<String> getContainerNames() {
+ public List<ContainerConfig> getContainerConfigList() {
+ return new ArrayList<ContainerConfig>(containerConfigs.values());
+ }
+
+ @Override
+ public ContainerConfig getContainerConfig(String containerName) {
+ ContainerConfig target = containerConfigs.get(containerName);
+ return (target == null) ? null : new ContainerConfig(target);
+ }
+
+ @Override
+ public List<String> getContainerNameList() {
/*
* Return container names as they were configured by user (case sensitive)
* along with the default container
*/
List<String> containerNameList = new ArrayList<String>();
containerNameList.add(GlobalConstants.DEFAULT.toString());
+ containerNameList.addAll(containerConfigs.keySet());
return containerNameList;
}
@Override
- public boolean hasNonDefaultContainer() {
- return false;
+ public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
+ Map<String, List<ContainerFlowConfig>> flowSpecConfig = new HashMap<String, List<ContainerFlowConfig>>();
+ for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+ List<ContainerFlowConfig> set = entry.getValue().getContainerFlowConfigs();
+ flowSpecConfig.put(entry.getKey(), set);
+ }
+ return flowSpecConfig;
+ }
+
+ private void loadConfigurations() {
+ /*
+ * Read containers, container flows and finally containers' entries from file
+ * and program the database accordingly
+ */
+ if (containerConfigs.isEmpty()) {
+ loadContainerConfig();
+ }
+ }
+
+ private Status saveContainerConfig() {
+ return saveContainerConfigLocal();
+ }
+
+ public Status saveContainerConfigLocal() {
+ ObjectWriter objWriter = new ObjectWriter();
+
+ Status status = objWriter.write(new ConcurrentHashMap<String, ContainerConfig>(containerConfigs), containersFileName);
+ if (!status.isSuccess()) {
+ return new Status(StatusCode.INTERNALERROR, "Failed to save container configurations: "
+ + status.getDescription());
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ private void removeComponentsStartUpfiles(String containerName) {
+ String startupLocation = String.format("./%s", GlobalConstants.STARTUPHOME.toString());
+ String containerPrint = String.format("_%s.", containerName.toLowerCase(Locale.ENGLISH));
+
+ File directory = new File(startupLocation);
+ String[] fileList = directory.list();
+
+ logger.trace("Deleteing startup configuration files for container {}", containerName);
+ if (fileList != null) {
+ for (String fileName : fileList) {
+ if (fileName.contains(containerPrint)) {
+ String fullPath = String.format("%s/%s", startupLocation, fileName);
+ File file = new File(fullPath);
+ boolean done = file.delete();
+ logger.trace("{} {}", (done ? "Deleted: " : "Failed to delete: "), fileName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Create and initialize default all resource group and create association
+ * with default well known users and profiles, if not already learnt from
+ * another cluster node
+ */
+ private void createDefaultAuthorizationGroups() {
+ allResourcesGroupName = ContainerManager.allContainersGroup;
+
+ // Add the default container to the all containers group if needed
+ String defaultContainer = GlobalConstants.DEFAULT.toString();
+ Set<String> allContainers = (resourceGroups.containsKey(allResourcesGroupName)) ? resourceGroups
+ .get(allResourcesGroupName) : new HashSet<String>();
+ if (!allContainers.contains(defaultContainer)) {
+ // Add Default container
+ allContainers.add(defaultContainer);
+ // Update cluster
+ resourceGroups.put(allResourcesGroupName, allContainers);
+ }
+
+ // Add the controller well known roles, if not known already
+ if (!roles.containsKey(UserLevel.SYSTEMADMIN.toString())) {
+ roles.put(UserLevel.SYSTEMADMIN.toString(), AppRoleLevel.APPADMIN);
+ }
+ if (!roles.containsKey(UserLevel.NETWORKADMIN.toString())) {
+ roles.put(UserLevel.NETWORKADMIN.toString(), AppRoleLevel.APPADMIN);
+ }
+ if (!roles.containsKey(UserLevel.NETWORKOPERATOR.toString())) {
+ roles.put(UserLevel.NETWORKOPERATOR.toString(), AppRoleLevel.APPOPERATOR);
+ }
+
+ /*
+ * Create and add the all containers user groups and associate them to the
+ * default well known user roles, if not present already
+ */
+ if (!groupsAuthorizations.containsKey(UserLevel.NETWORKADMIN.toString())) {
+ Set<ResourceGroup> writeProfile = new HashSet<ResourceGroup>(1);
+ Set<ResourceGroup> readProfile = new HashSet<ResourceGroup>(1);
+ writeProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.WRITE));
+ readProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.READ));
+ groupsAuthorizations.put(UserLevel.SYSTEMADMIN.toString(), writeProfile);
+ groupsAuthorizations.put(UserLevel.NETWORKADMIN.toString(), writeProfile);
+ groupsAuthorizations.put(UserLevel.NETWORKOPERATOR.toString(), readProfile);
+ }
+ }
+
+ /**
+ * Until manual configuration is not available, automatically maintain the
+ * well known resource groups
+ *
+ * @param containerName
+ * @param delete
+ */
+ private void updateResourceGroups(String containerName, boolean delete) {
+ // Container Roles and Container Resource Group
+ String groupName = "Container-" + containerName;
+ String containerAdminRole = "Container-" + containerName + "-Admin";
+ String containerOperatorRole = "Container-" + containerName + "-Operator";
+ Set<String> allContainerSet = resourceGroups.get(allResourcesGroupName);
+ if (delete) {
+ resourceGroups.remove(groupName);
+ groupsAuthorizations.remove(containerAdminRole);
+ groupsAuthorizations.remove(containerOperatorRole);
+ roles.remove(containerAdminRole);
+ roles.remove(containerOperatorRole);
+ // Update the all container group
+ allContainerSet.remove(containerName);
+ } else {
+ Set<String> resources = new HashSet<String>(1);
+ resources.add(containerName);
+ resourceGroups.put(groupName, resources);
+ Set<ResourceGroup> adminGroups = new HashSet<ResourceGroup>(1);
+ Set<ResourceGroup> operatorGroups = new HashSet<ResourceGroup>(1);
+ adminGroups.add(new ResourceGroup(groupName, Privilege.WRITE));
+ operatorGroups.add(new ResourceGroup(groupName, Privilege.READ));
+ groupsAuthorizations.put(containerAdminRole, adminGroups);
+ groupsAuthorizations.put(containerOperatorRole, operatorGroups);
+ roles.put(containerAdminRole, AppRoleLevel.APPADMIN);
+ roles.put(containerOperatorRole, AppRoleLevel.APPOPERATOR);
+ // Update the all containers resource group
+ allContainerSet.add(containerName);
+ }
+ // Update resource groups in cluster
+ resourceGroups.put(allResourcesGroupName, allContainerSet);
+ }
+
+ /**
+ * Notify ContainerAware listeners of the creation/deletion of the container
+ *
+ * @param containerName
+ * @param delete
+ * true is container was removed, false otherwise
+ */
+ private void notifyContainerAwareListeners(String containerName, boolean delete) {
+ // Back-end World: container name forced to lower case
+ String name = containerName.toLowerCase(Locale.ENGLISH);
+
+ synchronized (this.iContainerAware) {
+ for (IContainerAware i : this.iContainerAware) {
+ if (delete) {
+ i.containerDestroy(name);
+ } else {
+ i.containerCreate(name);
+ }
+ }
+ }
+ }
+
+ /**
+ * Notify the ContainerListener listeners in case the container mode has changed
+ * following a container configuration operation Note: this call must happen
+ * after the configuration db has been updated
+ *
+ * @param lastActionDelete
+ * true if the last container configuration operation was a container
+ * delete operation
+ */
+ private void notifyContainerModeChange(boolean lastActionDelete) {
+ if (lastActionDelete == false && containerConfigs.size() == 1) {
+ logger.info("First container Creation. Inform listeners");
+ synchronized (this.iContainerListener) {
+ for (IContainerListener i : this.iContainerListener) {
+ i.containerModeUpdated(UpdateType.ADDED);
+ }
+ }
+ } else if (lastActionDelete == true && containerConfigs.isEmpty()) {
+ logger.info("Last container Deletion. Inform listeners");
+ synchronized (this.iContainerListener) {
+ for (IContainerListener i : this.iContainerListener) {
+ i.containerModeUpdated(UpdateType.REMOVED);
+ }
+ }
+ }
+ }
+
+ private Status addRemoveContainerEntries(String containerName, List<String> nodeConnectorsString, boolean delete) {
+ // Construct action message
+ String action = String.format("Node conenctor(s) %s container %s: %s", delete ? "removal from" : "addition to",
+ containerName, nodeConnectorsString);
+
+ // Validity Check
+ if (nodeConnectorsString == null || nodeConnectorsString.isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Node connector list is null or empty");
+ }
+
+ // Presence check
+ ContainerConfig entryConf = containerConfigs.get(containerName);
+ if (entryConf == null) {
+ String msg = String.format("Container not found: %s", containerName);
+ String error = String.format("Failed to apply %s: (%s)", action, msg);
+ logger.warn(error);
+ return new Status(StatusCode.NOTFOUND, msg);
+ }
+
+ // Validation check
+ Status status = ContainerConfig.validateNodeConnectors(nodeConnectorsString);
+ if (!status.isSuccess()) {
+ String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
+ logger.warn(error);
+ return status;
+ }
+
+ List<NodeConnector> nodeConnectors = ContainerConfig.nodeConnectorsFromString(nodeConnectorsString);
+
+ // Port sharing check
+ if (!delete) {
+ /*
+ * Check if the ports being added to this container already belong to
+ * other containers. If so check whether the the appropriate flow specs
+ * are configured on this container
+ */
+ status = validatePortSharing(containerName, nodeConnectors);
+ if (!status.isSuccess()) {
+ String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
+ logger.warn(error);
+ return status;
+ }
+ }
+
+ // Update Database
+ status = updateContainerEntryDatabase(containerName, nodeConnectors, delete);
+ if (!status.isSuccess()) {
+ String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
+ logger.warn(error);
+ return status;
+ }
+
+ // Update Configuration
+ status = (delete) ? entryConf.removeNodeConnectors(nodeConnectorsString) : entryConf
+ .addNodeConnectors(nodeConnectorsString);
+ if (!status.isSuccess()) {
+ String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
+ logger.warn(error);
+ // Revert backend changes
+ Status statusRevert = updateContainerEntryDatabase(containerName, nodeConnectors, !delete);
+ if (!statusRevert.isSuccess()) {
+ // Unlikely
+ logger.error("Failed to revert changes in database (CRITICAL)");
+ }
+ return status;
+ }
+
+ // Update cluster Configuration cache
+ containerConfigs.put(containerName, entryConf);
+
+ // Notify
+ UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
+ notifyContainerEntryChangeInternal(containerName, nodeConnectors, update);
+ // Trigger cluster notification
+ containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(nodeConnectors, update));
+
+ return status;
+ }
+
+ private void notifyContainerChangeInternal(ContainerConfig conf, UpdateType update) {
+ String containerName = conf.getContainerName();
+ logger.trace("Notifying listeners on {} for container {}", update, containerName);
+ // Back-end World: container name forced to lower case
+ String container = containerName.toLowerCase(Locale.ENGLISH);
+ boolean delete = (update == UpdateType.REMOVED);
+ // Check if a container mode change notification is needed
+ notifyContainerModeChange(delete);
+ // Notify listeners
+ notifyContainerAwareListeners(container, delete);
+ }
+
+ private void notifyContainerEntryChangeInternal(String containerName, List<NodeConnector> ncList, UpdateType update) {
+ logger.trace("Notifying listeners on {} for ports {} in container {}", update, ncList, containerName);
+ // Back-end World: container name forced to lower case
+ String container = containerName.toLowerCase(Locale.ENGLISH);
+ for (NodeConnector nodeConnector : ncList) {
+ // Now signal that the port has been added/removed
+ synchronized (this.iContainerListener) {
+ for (IContainerListener i : this.iContainerListener) {
+ i.nodeConnectorUpdated(container, nodeConnector, update);
+ }
+ }
+ }
+ }
+
+ private void notifyCFlowChangeInternal(String containerName, List<ContainerFlowConfig> confList, UpdateType update) {
+ logger.trace("Notifying listeners on {} for flow specs {} in container {}", update, confList, containerName);
+ // Back-end World: container name forced to lower case
+ String container = containerName.toLowerCase(Locale.ENGLISH);
+ synchronized (this.iContainerListener) {
+ for (ContainerFlowConfig conf : confList) {
+ for (Match match : conf.getMatches()) {
+ ContainerFlow cFlow = new ContainerFlow(match);
+ for (IContainerListener i : this.iContainerListener) {
+ i.containerFlowUpdated(container, cFlow, cFlow, update);
+ }
+ }
+ }
+ }
+ }
+
+ private Status addRemoveContainerFlow(String containerName, List<ContainerFlowConfig> cFlowConfList, boolean delete) {
+ // Construct action message
+ String action = String.format("Flow spec(s) %s container %s: %s", delete ? "removal from" : "addition to",
+ containerName, cFlowConfList);
+
+ // Presence check
+ ContainerConfig containerConfig = this.containerConfigs.get(containerName);
+ if (containerConfig == null) {
+ String msg = String.format("Container not found: %s", containerName);
+ String error = String.format("Failed to apply %s: (%s)", action, msg);
+ logger.warn(error);
+ return new Status(StatusCode.NOTFOUND, "Container not present");
+ }
+
+ // Validity check, check for overlaps on current container configuration
+ Status status = containerConfig.validateContainerFlowModify(cFlowConfList, delete);
+ if (!status.isSuccess()) {
+ String msg = status.getDescription();
+ String error = String.format("Failed to apply %s: (%s)", action, msg);
+ logger.warn(error);
+ return new Status(StatusCode.BADREQUEST, msg);
+ }
+
+ // Validate the operation in terms to the port sharing with other containers
+ for (ContainerFlowConfig conf : cFlowConfList) {
+ for (Match match : conf.getMatches()) {
+ ContainerFlow cFlow = new ContainerFlow(match);
+ status = validateContainerFlowAddRemoval(containerName, cFlow, delete);
+ if (!status.isSuccess()) {
+ String msg = "Validation failed: " + status.getDescription();
+ String error = String.format("Failed to apply %s: (%s)", action, msg);
+ logger.warn(error);
+ return new Status(StatusCode.BADREQUEST, msg);
+ }
+ }
+ }
+
+ // Update Database
+ status = updateContainerFlow(containerName, cFlowConfList, delete);
+ if (!status.isSuccess()) {
+ String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
+ logger.error(error);
+ return status;
+ }
+
+ // Update Configuration
+ status = (delete) ? containerConfig.removeContainerFlows(cFlowConfList) : containerConfig
+ .addContainerFlows(cFlowConfList);
+ if (!status.isSuccess()) {
+ String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
+ logger.error(error);
+ // Revert backend changes
+ Status statusRevert = updateContainerFlow(containerName, cFlowConfList, !delete);
+ if (!statusRevert.isSuccess()) {
+ // Unlikely
+ logger.error("Failed to revert changes in database (CRITICAL)");
+ }
+ return status;
+ }
+ // Update cluster cache
+ this.containerConfigs.put(containerName, containerConfig);
+
+ // Notify listeners
+ UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
+ notifyCFlowChangeInternal(containerName, cFlowConfList, update);
+ // Trigger cluster notification
+ containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(cFlowConfList, update));
+
+ return status;
+ }
+
+ private Status addRemoveContainer(ContainerConfig containerConf, boolean delete) {
+ // Construct action message
+ String action = String.format("Container %s", delete ? "removal" : "creation");
+
+ // Valid configuration check
+ Status status = null;
+ String error = (containerConfigs == null) ? String.format("Invalid %s configuration: (null config object)", action)
+ : (!(status = containerConf.validate()).isSuccess()) ? String.format("Invalid %s configuration: (%s)",
+ action, status.getDescription()) : null;
+ if (error != null) {
+ logger.warn(error);
+ return new Status(StatusCode.BADREQUEST, error);
+ }
+
+ // Configuration presence check
+ String containerName = containerConf.getContainerName();
+ if (delete) {
+ if (!containerConfigs.containsKey(containerName)) {
+ String msg = String.format("%s Failed: (Container does not exist: %s)", action, containerName);
+ logger.warn(msg);
+ return new Status(StatusCode.NOTFOUND, msg);
+ }
+ } else {
+ if (containerConfigs.containsKey(containerName)) {
+ String msg = String.format("%s Failed: (Container already exist: %s)", action, containerName);
+ logger.warn(msg);
+ return new Status(StatusCode.CONFLICT, msg);
+ }
+ }
+
+ /*
+ * The proposed container configuration could be a complex one containing
+ * both ports and flow spec. If so, check if it has shared ports with
+ * other existing containers. If that is the case verify flow spec isolation
+ * is in place. No need to check on flow spec validation first. This
+ * would take care of both
+ */
+ if (!delete) {
+ status = validatePortSharing(containerConf);
+ if (!status.isSuccess()) {
+ error = String.format("%s Failed: (%s)", action, status.getDescription());
+ logger.error(error);
+ return status;
+ }
+ }
+
+ // Update Database
+ status = updateContainerDatabase(containerConf, delete);
+
+ // Abort and exit here if back-end database update failed
+ if (!status.isSuccess()) {
+ return status;
+ }
+
+ /*
+ * This is a quick fix until configuration service becomes the
+ * centralized configuration management place. Here container manager will
+ * remove the startup files for all the bundles that are present in the
+ * container being deleted. Do the cleanup here in Container manger as do not
+ * want to put this temporary code in Configuration manager yet which is
+ * ODL.
+ */
+ if (delete) {
+ // TODO: remove when Config Mgr takes over
+ removeComponentsStartUpfiles(containerName);
+ }
+
+ /*
+ * Update Configuration: This will trigger the notifications on cache
+ * update callback locally and on the other cluster nodes
+ */
+ if (delete) {
+ this.containerConfigs.remove(containerName);
+ } else {
+ this.containerConfigs.put(containerName, containerConf);
+ }
+
+ // Automatically create and populate user and resource groups
+ updateResourceGroups(containerName, delete);
+
+ // Notify listeners
+ UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
+ notifyContainerChangeInternal(containerConf, update);
+
+ // Trigger cluster notification
+ containerChangeEvents.put(containerName, new ContainerChangeEvent(containerConf, update));
+
+ if (update == UpdateType.ADDED) {
+ if (containerConf.hasFlowSpecs()) {
+ List<ContainerFlowConfig> specList = containerConf.getContainerFlowConfigs();
+ // Notify flow spec addition
+ notifyCFlowChangeInternal(containerName, specList, update);
+
+ // Trigger cluster notification
+ containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(specList, update));
+ }
+
+ if (containerConf.hasNodeConnectors()) {
+ List<NodeConnector> ncList = containerConf.getPortList();
+ // Notify port(s) addition
+ notifyContainerEntryChangeInternal(containerName, ncList, update);
+ // Trigger cluster notification
+ containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(ncList, update));
+ }
+ }
+
+ return status;
+ }
+
+ @Override
+ public Status addContainer(ContainerConfig containerConf) {
+ return addRemoveContainer(containerConf, false);
+ }
+
+ @Override
+ public Status removeContainer(ContainerConfig containerConf) {
+ return addRemoveContainer(containerConf, true);
+ }
+
+ @Override
+ public Status removeContainer(String containerName) {
+ // Construct action message
+ String action = String.format("Container removal: %s", containerName);
+
+ ContainerConfig containerConf = containerConfigs.get(containerName);
+ if (containerConf == null) {
+ String msg = String.format("Container not found");
+ String error = String.format("Failed to apply %s: (%s)", action, msg);
+ logger.warn(error);
+ return new Status(StatusCode.NOTFOUND, msg);
+ }
+ return addRemoveContainer(containerConf, true);
+ }
+
+ @Override
+ public Status addContainerEntry(String containerName, List<String> nodeConnectors) {
+ return addRemoveContainerEntries(containerName, nodeConnectors, false);
+ }
+
+ @Override
+ public Status removeContainerEntry(String containerName, List<String> nodeConnectors) {
+ return addRemoveContainerEntries(containerName, nodeConnectors, true);
+ }
+
+ @Override
+ public Status addContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
+ return addRemoveContainerFlow(containerName, fSpecConf, false);
+ }
+
+ @Override
+ public Status removeContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
+ return addRemoveContainerFlow(containerName, fSpecConf, true);
+ }
+
+ @Override
+ public Status removeContainerFlows(String containerName, Set<String> names) {
+ // Construct action message
+ String action = String.format("Flow spec(s) removal from container %s: %s", containerName, names);
+
+ // Presence check
+ ContainerConfig sc = containerConfigs.get(containerName);
+ if (sc == null) {
+ String msg = String.format("Container not found: %s", containerName);
+ String error = String.format("Failed to apply %s: (%s)", action, msg);
+ logger.warn(error);
+ return new Status(StatusCode.NOTFOUND, msg);
+ }
+ List<ContainerFlowConfig> list = sc.getContainerFlowConfigs(names);
+ if (list.isEmpty() || list.size() != names.size()) {
+ String msg = String.format("Cannot find all the specified flow specs");
+ String error = String.format("Failed to apply %s: (%s)", action, msg);
+ logger.warn(error);
+ return new Status(StatusCode.BADREQUEST, msg);
+ }
+ return addRemoveContainerFlow(containerName, list, true);
+ }
+
+ @Override
+ public List<ContainerFlowConfig> getContainerFlows(String containerName) {
+ ContainerConfig sc = containerConfigs.get(containerName);
+ return (sc == null) ? new ArrayList<ContainerFlowConfig>(0) : sc.getContainerFlowConfigs();
+ }
+
+ @Override
+ public List<String> getContainerFlowNameList(String containerName) {
+ ContainerConfig sc = containerConfigs.get(containerName);
+ return (sc == null) ? new ArrayList<String>(0) : sc.getContainerFlowConfigsNames();
+ }
+
+ @Override
+ public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
+ // Perform the class deserialization locally, from inside the package
+ // where the class is defined
+ return ois.readObject();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void loadContainerConfig() {
+ ObjectReader objReader = new ObjectReader();
+ ConcurrentMap<String, ContainerConfig> configMap = (ConcurrentMap<String, ContainerConfig>) objReader.read(this,
+ containersFileName);
+
+ if (configMap == null) {
+ return;
+ }
+
+ for (Map.Entry<String, ContainerConfig> configEntry : configMap.entrySet()) {
+ addContainer(configEntry.getValue());
+ }
+ }
+
+ public void _psc(CommandInterpreter ci) {
+ for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+ ContainerConfig sc = entry.getValue();
+ ci.println(String.format("%s: %s", sc.getContainerName(), sc.toString()));
+ }
+ ci.println("Total number of containers: " + containerConfigs.entrySet().size());
+ }
+
+ public void _pfc(CommandInterpreter ci) {
+ for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+ ContainerConfig sc = entry.getValue();
+ ci.println(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
+ }
+ }
+
+ public void _psd(CommandInterpreter ci) {
+ for (String containerName : containerData.keySet()) {
+ ContainerData sd = containerData.get(containerName);
+ for (Node sid : sd.getSwPorts().keySet()) {
+ Set<NodeConnector> s = sd.getSwPorts().get(sid);
+ ci.println("\t" + sid + " : " + s);
+ }
+
+ for (ContainerFlow s : sd.getContainerFlowSpecs()) {
+ ci.println("\t" + s.toString());
+ }
+ }
+ }
+
+ public void _psp(CommandInterpreter ci) {
+ for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
+ ci.println(nodeConnectorToContainers.get(sp));
+ }
+ }
+
+ public void _psm(CommandInterpreter ci) {
+ for (Node sp : nodeToContainers.keySet()) {
+ ci.println(nodeToContainers.get(sp));
+ }
+ }
+
+ public void _addContainer(CommandInterpreter ci) {
+ String containerName = ci.nextArgument();
+ if (containerName == null) {
+ ci.print("Container Name not specified");
+ return;
+ }
+ String staticVlan = ci.nextArgument();
+ if (staticVlan == null) {
+ ci.print("Static Vlan not specified");
+ return;
+ }
+ ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
+ ci.println(this.addRemoveContainer(containerConfig, false));
+ }
+
+ public void _createContainer(CommandInterpreter ci) {
+ String containerName = ci.nextArgument();
+ if (containerName == null) {
+ ci.print("Container Name not specified");
+ return;
+ }
+ String staticVlan = ci.nextArgument();
+ if (staticVlan == null) {
+ ci.print("Static Vlan not specified");
+ return;
+ }
+ List<String> ports = new ArrayList<String>();
+ for (long l = 1L; l < 10L; l++) {
+ ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
+ }
+ List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
+ cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
+ ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
+ ci.println(this.addRemoveContainer(containerConfig, false));
+ }
+
+ public void _removeContainer(CommandInterpreter ci) {
+ String containerName = ci.nextArgument();
+ if (containerName == null) {
+ ci.print("Container Name not specified");
+ return;
+ }
+ ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
+ ci.println(this.addRemoveContainer(containerConfig, true));
+ }
+
+ public void _addContainerEntry(CommandInterpreter ci) {
+ String containerName = ci.nextArgument();
+ if (containerName == null) {
+ ci.print("Container Name not specified");
+ return;
+ }
+ String nodeId = ci.nextArgument();
+ if (nodeId == null) {
+ ci.print("Node Id not specified");
+ return;
+ }
+ String portId = ci.nextArgument();
+ if (portId == null) {
+ ci.print("Port not specified");
+ return;
+ }
+ Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
+ Short port = Short.valueOf(portId);
+ NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
+ List<String> portList = new ArrayList<String>(1);
+ portList.add(nc.toString());
+ ci.println(this.addRemoveContainerEntries(containerName, portList, false));
+ }
+
+ public void _removeContainerEntry(CommandInterpreter ci) {
+ String containerName = ci.nextArgument();
+ if (containerName == null) {
+ ci.print("Container Name not specified");
+ return;
+ }
+ String nodeId = ci.nextArgument();
+ if (nodeId == null) {
+ ci.print("Node Id not specified");
+ return;
+ }
+ String portId = ci.nextArgument();
+ if (portId == null) {
+ ci.print("Port not specified");
+ return;
+ }
+ Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
+ Short port = Short.valueOf(portId);
+ NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
+ List<String> portList = new ArrayList<String>(1);
+ portList.add(nc.toString());
+ ci.println(this.addRemoveContainerEntries(containerName, portList, true));
+ }
+
+ private ContainerFlowConfig createSampleContainerFlowConfig(String cflowName, boolean boolUnidirectional) {
+ ContainerFlowConfig cfg = new ContainerFlowConfig(cflowName, "9.9.1.0/24", "19.9.1.2", "TCP", "1234", "25");
+ return cfg;
+ }
+
+ public void _addContainerFlow(CommandInterpreter ci) {
+ String containerName = ci.nextArgument();
+ if (containerName == null) {
+ ci.print("Container Name not specified");
+ return;
+ }
+ String cflowName = ci.nextArgument();
+ if (cflowName == null) {
+ ci.print("cflowName not specified");
+ return;
+ }
+ String unidirectional = ci.nextArgument();
+ boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
+ List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
+ list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
+ ci.println(this.addRemoveContainerFlow(containerName, list, false));
+ }
+
+ public void _removeContainerFlow(CommandInterpreter ci) {
+ String containerName = ci.nextArgument();
+ if (containerName == null) {
+ ci.print("Container Name not specified");
+ return;
+ }
+ String cflowName = ci.nextArgument();
+ if (cflowName == null) {
+ ci.print("cflowName not specified");
+ return;
+ }
+ Set<String> set = new HashSet<String>(1);
+ set.add(cflowName);
+ ci.println(this.removeContainerFlows(containerName, set));
+ }
+
+ @Override
+ public String getHelp() {
+ StringBuffer help = new StringBuffer();
+ help.append("---ContainerManager Testing---\n");
+ help.append("\tpsc - Print ContainerConfigs\n");
+ help.append("\tpfc - Print FlowSpecConfigs\n");
+ help.append("\tpsd - Print ContainerData\n");
+ help.append("\tpsp - Print nodeConnectorToContainers\n");
+ help.append("\tpsm - Print nodeToContainers\n");
+ help.append("\t addContainer <containerName> <staticVlan> \n");
+ help.append("\t removeContainer <containerName> \n");
+ help.append("\t addContainerEntry <containerName> <nodeId> <port> \n");
+ help.append("\t removeContainerEntry <containerName> <nodeId> <port> \n");
+ help.append("\t addContainerFlow <containerName> <cflowName> <unidirectional true/false>\n");
+ help.append("\t removeContainerFlow <containerName> <cflowName> \n");
+ return help.toString();
+ }
+
+ @Override
+ public boolean doesContainerExist(String containerName) {
+ // Test for default container
+ if (GlobalConstants.DEFAULT.toString().equalsIgnoreCase(containerName)) {
+ return true;
+ }
+ // Test for non-default one
+ return (getContainerByName(containerName) != null);
+ }
+
+ @Override
+ public ContainerData getContainerData(String containerName) {
+ return (getContainerByName(containerName));
+ }
+
+ @Override
+ public Status saveConfiguration() {
+ return saveContainerConfig();
+ }
+
+ public void _containermgrGetRoles(CommandInterpreter ci) {
+ ci.println("Configured roles for Container Mgr:");
+ List<String> list = this.getRoles();
+ for (String role : list) {
+ ci.println(role + "\t" + roles.get(role));
+ }
+ }
+
+ public void _containermgrGetAuthorizedGroups(CommandInterpreter ci) {
+ String roleName = ci.nextArgument();
+ if (roleName == null || roleName.trim().isEmpty()) {
+ ci.println("Invalid argument");
+ ci.println("mmGetAuthorizedGroups <role_name>");
+ return;
+ }
+ ci.println("Resource Groups associated to role " + roleName + ":");
+ List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
+ for (ResourceGroup group : list) {
+ ci.println(group.toString());
+ }
+ }
+
+ public void _containermgrGetAuthorizedResources(CommandInterpreter ci) {
+ String roleName = ci.nextArgument();
+ if (roleName == null || roleName.trim().isEmpty()) {
+ ci.println("Invalid argument");
+ ci.println("mmGetAuthorizedResources <role_name>");
+ return;
+ }
+ ci.println("Resource associated to role " + roleName + ":");
+ List<Resource> list = this.getAuthorizedResources(roleName);
+ for (Resource resource : list) {
+ ci.println(resource.toString());
+ }
+ }
+
+ public void _containermgrGetResourcesForGroup(CommandInterpreter ci) {
+ String groupName = ci.nextArgument();
+ if (groupName == null || groupName.trim().isEmpty()) {
+ ci.println("Invalid argument");
+ ci.println("containermgrResourcesForGroup <group_name>");
+ return;
+ }
+ ci.println("Group " + groupName + " contains the following resources:");
+ List<Object> resources = this.getResources(groupName);
+ for (Object resource : resources) {
+ ci.println(resource.toString());
+ }
+ }
+
+ public void _containermgrGetUserLevel(CommandInterpreter ci) {
+ String userName = ci.nextArgument();
+ if (userName == null || userName.trim().isEmpty()) {
+ ci.println("Invalid argument");
+ ci.println("containermgrGetUserLevel <user_name>");
+ return;
+ }
+ ci.println("User " + userName + " has level: " + this.getUserLevel(userName));
+ }
+
+ public void _containermgrGetUserResources(CommandInterpreter ci) {
+ String userName = ci.nextArgument();
+ if (userName == null || userName.trim().isEmpty()) {
+ ci.println("Invalid argument");
+ ci.println("containermgrGetUserResources <user_name>");
+ return;
+ }
+ ci.println("User " + userName + " owns the following resources: ");
+ Set<Resource> resources = this.getAllResourcesforUser(userName);
+ for (Resource resource : resources) {
+ ci.println(resource.toString());
+ }
+ }
+
+ /*
+ * For scalability testing where as of now controller gui is unresponsive
+ * providing here an osgi hook to trigger the save config so that DT do not
+ * have to reaply the scalable configuration each time they restart the
+ * controller
+ */
+ // TODO: remove when no longer needed
+ public void _saveConfig(CommandInterpreter ci) {
+ Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
+
+ IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
+ IConfigurationService.class, this);
+ if (configService != null) {
+ status = configService.saveConfigurations();
+ }
+ ci.println(status.toString());
}
@Override
- public Status saveContainerConfig() {
- return null;
+ public List<String> getContainerNames() {
+ return getContainerNameList();
}
+ @Override
+ public boolean hasNonDefaultContainer() {
+ return !containerConfigs.keySet().isEmpty();
+ }
}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager.internal;
+
+import org.opendaylight.controller.containermanager.ContainerData;
+
+/**
+ * @file IContainerInternal.java
+ *
+ * @brief Interface to export internal container manager data to friend classes
+ *
+ * Interface to export internal container manager data to friend classes
+ */
+
+interface IContainerInternal {
+ /**
+ * Return a reference to containerData if available so a friend class
+ * can extract all the data and cook them up.
+ *
+ * @param containerName ContainerName for which we want to export the data
+ *
+ * @return null if containerName doesn't exist or a reference to
+ * ContainerData if exists
+ */
+ ContainerData getContainerData(String containerName);
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+ </scm>
+
+ <artifactId>containermanager.it.implementation</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.containermanager,
+ org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.sal.packet,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.action,
+ org.opendaylight.controller.sal.flowprogrammer,
+ org.opendaylight.controller.sal.match,
+ org.opendaylight.controller.sal.reader,
+ org.eclipse.osgi.framework.console,
+ org.osgi.framework,
+ org.slf4j,
+ org.apache.felix.dm
+ </Import-Package>
+ <Export-Package>
+ </Export-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.containermanager.internal.Activator
+ </Bundle-Activator>
+ </instructions>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager.internal;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.sal.core.IContainerAware;
+import org.opendaylight.controller.sal.core.IContainer;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.apache.felix.dm.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+
+public class Activator extends ComponentActivatorAbstractBase {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+
+ /**
+ * Function called when the activator starts just after some
+ * initializations are done by the
+ * ComponentActivatorAbstractBase.
+ *
+ */
+ public void init() {
+ }
+
+ /**
+ * Function called when the activator stops just before the
+ * cleanup done by ComponentActivatorAbstractBase
+ *
+ */
+ public void destroy() {
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the
+ * list of known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ public Object[] getImplementations() {
+ Object[] res = { ContainerImpl.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies
+ * is required.
+ *
+ * @param c dependency manager Component object, used for
+ * configuring the dependencies exported and imported
+ * @param imp Implementation class that is being configured,
+ * needed as long as the same routine can configure multiple
+ * implementations
+ * @param containerName The containerName being configured, this allow
+ * also optional per-container different behavior if needed, usually
+ * should not be the case though.
+ */
+ public void configureInstance(Component c, Object imp, String containerName) {
+ if (imp.equals(ContainerImpl.class)) {
+ // export the service
+ c.setInterface(new String[] { IContainer.class.getName() }, null);
+ }
+ }
+
+ /**
+ * Method which tells how many Global implementations are
+ * supported by the bundle. This way we can tune the number of
+ * components created. This components will be created ONLY at the
+ * time of bundle startup and will be destroyed only at time of
+ * bundle destruction, this is the major difference with the
+ * implementation retrieved via getImplementations where all of
+ * them are assumed to be in a container!
+ *
+ *
+ * @return The list of implementations the bundle will support,
+ * in Global version
+ */
+ protected Object[] getGlobalImplementations() {
+ Object[] res = { ContainerManager.class };
+ return res;
+ }
+
+ /**
+ * Configure the dependency for a given instance Global
+ *
+ * @param c Component assigned for this instance, this will be
+ * what will be used for configuration
+ * @param imp implementation to be configured
+ * @param containerName container on which the configuration happens
+ */
+ protected void configureGlobalInstance(Component c, Object imp) {
+ if (imp.equals(ContainerManager.class)) {
+
+ // export the service
+ c.setInterface(new String[] { IContainerManager.class.getName() },
+ null);
+
+ c.add(createServiceDependency().setService(
+ IClusterGlobalServices.class).setCallbacks(
+ "setClusterServices", "unsetClusterServices").setRequired(
+ true));
+
+ // Key kick-starter for container creation in each component
+ c.add(createServiceDependency().setService(IContainerAware.class)
+ .setCallbacks("setIContainerAware", "unsetIContainerAware")
+ .setRequired(false));
+
+ // Optional interface expected to be exported by the
+ // protocol plugins to setup proper filtering based on
+ // slicing events
+ c.add(createServiceDependency()
+ .setService(IContainerListener.class).setCallbacks(
+ "setIContainerListener", "unsetIContainerListener")
+ .setRequired(false));
+ }
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+/**
+ * @file ContainerImpl.java
+ *
+ * @brief Class that instantiated per-container implements the
+ * interface IContainer
+ *
+ *
+ */
+package org.opendaylight.controller.containermanager.internal;
+
+import java.util.Dictionary;
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import java.util.Set;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import java.util.List;
+import org.opendaylight.controller.sal.core.IContainer;
+
+public class ContainerImpl implements IContainer {
+ private String containerName = null;
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init(Component c) {
+ Dictionary<?, ?> props = c.getServiceProperties();
+ if (props != null) {
+ this.containerName = (String) props.get("containerName");
+ }
+ }
+
+ @Override
+ public String getName() {
+ return this.containerName;
+ }
+
+ @Override
+ public List<ContainerFlow> getContainerFlows() {
+ return null;
+ }
+
+ @Override
+ public short getTag(Node n) {
+ return (short) 0;
+ }
+
+ @Override
+ public Set<NodeConnector> getNodeConnectors() {
+ return null;
+ }
+
+ @Override
+ public Set<Node> getNodes() {
+ return null;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+/**
+ * @file ContainerManager.java
+ *
+ * @brief Manage one or many Containers
+ *
+ *
+ */
+package org.opendaylight.controller.containermanager.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.containermanager.ContainerConfig;
+import org.opendaylight.controller.containermanager.ContainerFlowConfig;
+import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.sal.core.IContainerAware;
+import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.Status;
+
+public class ContainerManager implements IContainerManager {
+ private static final Logger logger = LoggerFactory
+ .getLogger(ContainerManager.class);
+ private IClusterGlobalServices clusterServices;
+ /*
+ * Collection containing the configuration objects.
+ * This is configuration world: container names (also the map key)
+ * are maintained as they were configured by user, same case
+ */
+ private Set<IContainerAware> iContainerAware = (Set<IContainerAware>) Collections
+ .synchronizedSet(new HashSet<IContainerAware>());
+ private Set<IContainerListener> iContainerListener = Collections
+ .synchronizedSet(new HashSet<IContainerListener>());
+
+ void setIContainerListener(IContainerListener s) {
+ if (this.iContainerListener != null) {
+ this.iContainerListener.add(s);
+ }
+ }
+
+ void unsetIContainerListener(IContainerListener s) {
+ if (this.iContainerListener != null) {
+ this.iContainerListener.remove(s);
+ }
+ }
+
+ public void setIContainerAware(IContainerAware iContainerAware) {
+ if (!this.iContainerAware.contains(iContainerAware)) {
+ this.iContainerAware.add(iContainerAware);
+ // Now call the container creation for all the known containers so
+ // far
+ List<String> containerDB = getContainerNames();
+ if (containerDB != null) {
+ for (int i = 0; i < containerDB.size(); i++) {
+ iContainerAware.containerCreate(containerDB.get(i));
+ }
+ }
+ }
+ }
+
+ public void unsetIContainerAware(IContainerAware iContainerAware) {
+ this.iContainerAware.remove(iContainerAware);
+ // There is no need to do cleanup of the component when
+ // unregister because it will be taken care by the Containerd
+ // component itself
+ }
+
+ public void setClusterServices(IClusterGlobalServices i) {
+ this.clusterServices = i;
+ logger.debug("IClusterServices set");
+ }
+
+ public void unsetClusterServices(IClusterGlobalServices i) {
+ if (this.clusterServices == i) {
+ this.clusterServices = null;
+ logger.debug("IClusterServices Unset");
+ }
+ }
+
+ public void init() {
+ logger.info("ContainerManager startup....");
+ }
+
+ public void destroy() {
+ // Clear local states
+ this.iContainerAware.clear();
+ this.iContainerListener.clear();
+
+ logger.info("ContainerManager Shutdown....");
+ }
+
+ @Override
+ public List<String> getContainerNames() {
+ /*
+ * Return container names as they were configured by user (case sensitive)
+ * along with the default container
+ */
+ List<String> containerNameList = new ArrayList<String>();
+ containerNameList.add(GlobalConstants.DEFAULT.toString());
+ return containerNameList;
+ }
+
+ @Override
+ public boolean hasNonDefaultContainer() {
+ return false;
+ }
+
+ @Override
+ public Status addContainer(ContainerConfig configObject) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Status removeContainer(ContainerConfig configObject) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Status removeContainer(String containerName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Status addContainerEntry(String containerName, List<String> portList) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Status removeContainerEntry(String containerName,
+ List<String> portList) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Status addContainerFlows(String containerName,
+ List<ContainerFlowConfig> configObject) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Status removeContainerFlows(String containerName,
+ List<ContainerFlowConfig> configObject) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Status removeContainerFlows(String containerName, Set<String> name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public List<ContainerConfig> getContainerConfigList() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ContainerConfig getContainerConfig(String containerName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public List<String> getContainerNameList() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean doesContainerExist(String ContainerId) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public List<ContainerFlowConfig> getContainerFlows(String containerName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public List<String> getContainerFlowNameList(String containerName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
assertEquals(GlobalConstants.DEFAULT.toString(), names.get(0));\r
\r
assertFalse(cm.hasNonDefaultContainer());\r
- assertNull(cm.saveContainerConfig());\r
\r
cm.destroy();\r
\r
<module>../../hosttracker_new/implementation</module>
<module>../../containermanager/api</module>
<module>../../containermanager/implementation</module>
+ <module>../../containermanager/it.implementation</module>
+ <module>../../appauth</module>
<module>../../switchmanager/api</module>
<module>../../switchmanager/implementation</module>
<module>../../switchmanager/integrationtest</module>
<module>../../northbound/hosttracker</module>
<module>../../northbound/subnets</module>
<module>../../northbound/switchmanager</module>
+ <module>../../northbound/containermanager</module>
<module>../../northbound/networkconfiguration/bridgedomain</module>
<!-- Northbound integration tests -->
<exclude>org.opendaylight.controller:logging.bridge</exclude>
<exclude>org.opendaylight.controller:protocol_plugins.stub</exclude>
<exclude>org.opendaylight.controller:*.integrationtest</exclude>
+ <exclude>org.opendaylight.controller:containermanager.it.implementation</exclude>
<exclude>org.opendaylight.controller:hosttracker_new</exclude>
<exclude>org.opendaylight.controller:hosttracker_new.implementation</exclude>
<exclude>org.opendaylight.controller:checkstyle</exclude>
* @param ipAddress (InetAddress) the IP address
* @return StaticRoute
*/
- public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress);
+ StaticRoute getBestMatchStaticRoute(InetAddress ipAddress);
/**
* Returns all the StaticRouteConfig
* @return all the StaticRouteConfig
*/
- public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs();
+ ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs();
/**
* Adds a StaticRouteConfig
* @return a text string indicating the result of the operation..
* If the operation is successful, the return string will be "SUCCESS"
*/
- public Status addStaticRoute(StaticRouteConfig config);
+ Status addStaticRoute(StaticRouteConfig config);
/**
* Removes the named StaticRouteConfig
* @return a text string indicating the result of the operation.
* If the operation is successful, the return string will be "SUCCESS"
*/
- public Status removeStaticRoute(String name);
+ Status removeStaticRoute(String name);
}
* @param s: StaticRoute
* @param added: boolean true if the static route is added,
*/
- public void staticRouteUpdate(StaticRoute s, boolean added);
+ void staticRouteUpdate(StaticRoute s, boolean added);
}
private static Logger log = LoggerFactory
.getLogger(StaticRoutingImplementation.class);
private static String ROOT = GlobalConstants.STARTUPHOME.toString();
- private static final String SAVE = "Save";
ConcurrentMap<String, StaticRoute> staticRoutes;
ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs;
private IfIptoHost hostTracker;
* New PortGroupConfig object created by user Configuration.
* @return true if successful. false otherwise.
*/
- public boolean createPortGroupConfig(PortGroupConfig config);
+ boolean createPortGroupConfig(PortGroupConfig config);
/**
* This method is invoked by the Controller towards the Provider when an
* Existing Port Group Configuration deleted by the user.
* @return true if successful. false otherwise.
*/
- public boolean deletePortGroupConfig(PortGroupConfig config);
+ boolean deletePortGroupConfig(PortGroupConfig config);
/**
* Returns the complete mapping database corresponds to a PortGroup
* @return Database of Switch-Id to PortGroup mapping that corresponds to
* the Port Group User Configuration.
*/
- public Map<Node, PortGroup> getPortGroupData(PortGroupConfig config);
+ Map<Node, PortGroup> getPortGroupData(PortGroupConfig config);
/**
* Returns PortGroup data for a given Switch and user Configuration. Its the
* @return PortGroup data for a given Openflow switch.
* @see PortGroup
*/
- public PortGroup getPortGroupData(PortGroupConfig config, long matrixSwitchId);
+ PortGroup getPortGroupData(PortGroupConfig config, long matrixSwitchId);
/**
* Registers a Listener for Port Group membership changes based on Custom
* A Controller module that listens to events from the Custom
* Port Grouping Application.
*/
- public void registerPortGroupChange(PortGroupChangeListener listener);
+ void registerPortGroupChange(PortGroupChangeListener listener);
/**
* Application returns an Usage string for the Match Criteria User
*
* @return Usage string.
*/
- public String getApplicationDrivenMatchCriteriaUsage();
+ String getApplicationDrivenMatchCriteriaUsage();
/**
* Returns the name of the Custom Application that implements
*
* @return Provider Name
*/
- public String getProviderName();
+ String getProviderName();
/**
* Controller uses this method to check with the Provider supports the
* @return true if the Provider supports the matchCriteria String. false
* otherwise.
*/
- public boolean isMatchCriteriaSupported(String matchCriteria);
+ boolean isMatchCriteriaSupported(String matchCriteria);
}
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.implementation</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <artifactId>containermanager.it.implementation</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
mavenBundle("org.opendaylight.controller", "containermanager")
.versionAsInProject(),
mavenBundle("org.opendaylight.controller",
- "containermanager.implementation").versionAsInProject(),
+ "containermanager.it.implementation").versionAsInProject(),
mavenBundle("org.opendaylight.controller",
"forwardingrulesmanager").versionAsInProject(),
<dependency>\r
<groupId>org.opendaylight.controller</groupId>\r
<artifactId>containermanager</artifactId>\r
- <version>0.4.0-SNAPSHOT</version>\r
+ <version>0.5.0-SNAPSHOT</version>\r
</dependency>\r
\r
<dependency>\r
<groupId>org.opendaylight.controller</groupId>\r
- <artifactId>containermanager.implementation</artifactId>\r
- <version>0.4.0-SNAPSHOT</version>\r
+ <artifactId>containermanager.it.implementation</artifactId>\r
+ <version>0.5.0-SNAPSHOT</version>\r
</dependency>\r
\r
<dependency>\r
// needed by statisticsmanager
mavenBundle("org.opendaylight.controller", "containermanager").versionAsInProject(),
- mavenBundle("org.opendaylight.controller", "containermanager.implementation").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller", "containermanager.it.implementation").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "clustering.services").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "clustering.stub").versionAsInProject(),
<instructions>
<Export-Package>
org.opendaylight.controller.northbound.commons.exception,
+ org.opendaylight.controller.northbound.commons.types,
org.opendaylight.controller.northbound.commons.utils,
org.opendaylight.controller.northbound.commons
</Export-Package>
<Import-Package>
javax.ws.rs,
javax.ws.rs.core,
+ javax.xml.bind.annotation,
org.opendaylight.controller.sal.utils,
org.opendaylight.controller.sal.authorization,
org.opendaylight.controller.containermanager,
--- /dev/null
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.northbound.commons.types;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Represents a list of string values
+ */
+@XmlRootElement(name = "list")
+@XmlAccessorType(XmlAccessType.NONE)
+public class StringList {
+ @XmlElement(name = "item")
+ private List<String> list;
+
+ public StringList() {
+ }
+
+ public StringList(List<String> list) {
+ this.list = list;
+ }
+
+ public List<String> getList() {
+ return list;
+ }
+
+}
--- /dev/null
+<?xml version="1.0"?>
+<enunciate label="full" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd">
+ <services>
+ <rest defaultRestSubcontext="/controller/nb/v2/containermanager"/>
+ </services>
+
+ <modules>
+ <docs docsDir="rest" title="Container Manager REST API" includeExampleXml="true" includeExampleJson="true"/>
+ </modules>
+</enunciate>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager.northbound</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.enunciate</groupId>
+ <artifactId>maven-enunciate-plugin</artifactId>
+ <version>${enunciate.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ </Export-Package>
+ <Import-Package>
+ org.opendaylight.controller.appauth.authorization,
+ org.opendaylight.controller.sal.authorization,
+ org.opendaylight.controller.usermanager,
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.containermanager,
+ org.opendaylight.controller.northbound.commons,
+ org.opendaylight.controller.northbound.commons.exception,
+ org.opendaylight.controller.northbound.commons.utils,
+ com.sun.jersey.spi.container.servlet,
+ javax.ws.rs,
+ javax.ws.rs.core,
+ javax.xml.bind.annotation,
+ javax.xml.bind,
+ org.slf4j,
+ org.codehaus.jackson.jaxrs,
+ !org.codehaus.enunciate.jaxrs
+ </Import-Package>
+ <Web-ContextPath>/controller/nb/v2/containermanager</Web-ContextPath>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>appauth</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>web</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.enunciate</groupId>
+ <artifactId>enunciate-core-annotations</artifactId>
+ <version>${enunciate.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.northbound</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>com.sun.jersey.jersey-servlet</artifactId>
+ <version>1.17-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+
+package org.opendaylight.controller.containermanager.northbound;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.containermanager.ContainerConfig;
+
+
+@XmlRootElement(name = "container-config-list")
+@XmlAccessorType(XmlAccessType.NONE)
+public class ContainerConfigs {
+ @XmlElement(name = "container-config")
+ List<ContainerConfig> containerConfig;
+
+ //To satisfy JAXB
+ @SuppressWarnings("unused")
+ private ContainerConfigs() {
+
+ }
+
+
+ public ContainerConfigs(List<ContainerConfig> containerconfig) {
+ this.containerConfig = containerconfig;
+ }
+
+
+ public List<ContainerConfig> getcontainerConfig() {
+ return containerConfig;
+ }
+
+ public void setcontainerConfig(List<ContainerConfig> containerConfig) {
+ this.containerConfig = containerConfig;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager.northbound;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import javax.xml.bind.JAXBElement;
+
+import org.codehaus.enunciate.jaxrs.ResponseCode;
+import org.codehaus.enunciate.jaxrs.StatusCodes;
+import org.codehaus.enunciate.jaxrs.TypeHint;
+import org.opendaylight.controller.containermanager.IContainerAuthorization;
+import org.opendaylight.controller.northbound.commons.RestMessages;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceForbiddenException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
+import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
+import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
+import org.opendaylight.controller.sal.authorization.Privilege;
+import org.opendaylight.controller.sal.authorization.UserLevel;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.usermanager.IUserManager;
+
+import org.opendaylight.controller.containermanager.ContainerFlowConfig;
+import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.containermanager.ContainerConfig;
+
+/**
+ * Container Manager Northbound API
+ *
+ * <br>
+ * <br>
+ * Authentication scheme : <b>HTTP Basic</b><br>
+ * Authentication realm : <b>opendaylight</b><br>
+ * Transport : <b>HTTP and HTTPS</b><br>
+ * <br>
+ * HTTPS Authentication is disabled by default. Administrator can enable it in
+ * tomcat-server.xml after adding a proper keystore / SSL certificate from a
+ * trusted authority.<br>
+ * More info :
+ * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ *
+ */
+@Path("/")
+public class ContainerManagerNorthbound {
+ private String username;
+
+ @Context
+ public void setSecurityContext(SecurityContext context) {
+ Principal principal;
+ principal = context.getUserPrincipal();
+ username = principal.getName();
+ }
+
+ protected String getUserName() {
+ return username;
+ }
+
+ private IContainerManager getContainerManager() {
+ IContainerManager containerMgr = (IContainerManager) ServiceHelper.getGlobalInstance(IContainerManager.class, this);
+ if (containerMgr == null) {
+ throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
+ }
+ return containerMgr;
+ }
+
+ private void handleNameMismatch(String name, String nameinURL) {
+ if (name == null || nameinURL == null) {
+ throw new BadRequestException(RestMessages.INVALIDJSON.toString());
+ }
+
+ if (name.equalsIgnoreCase(nameinURL)) {
+ return;
+ }
+ throw new BadRequestException(RestMessages.INVALIDJSON.toString());
+ }
+
+
+
+ /**
+ * Get all the containers configured in the system
+ *
+ * @return a List of all {@link org.opendaylight.controller.containermanager.ContainerConfig}
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/all
+ *
+ * Response Payload in XML:
+ * <container-config-list>
+ *    <container-config>
+ *       <container>black</container>
+ *       <staticVlan>10</staticVlan>
+ *       <nodeConnectors>OF|1@OF|00:00:00:00:00:00:00:01</nodeConnectors>
+ *       <nodeConnectors>OF|23@OF|00:00:00:00:00:00:20:21</nodeConnectors>
+ *       <flowSpecs>
+ *        <name>tcp</name>
+ *        <protocol>TCP</protocol>
+ *       </flowSpecs>
+ *     </container-config>
+ *     <container-config>
+ *       <container>red</container>
+ *       <staticVlan>20</staticVlan>
+ *       <nodeConnectors>OF|1@OF|00:00:00:00:00:00:00:01</nodeConnectors>
+ *       <nodeConnectors>OF|23@OF|00:00:00:00:00:00:20:21</nodeConnectors>
+ *       <flowSpecs>
+ *        <name>udp</name>
+ *        <protocol>UDP</protocol>
+ *       </flowSpecs>
+ *     </container-config>
+ * </container-config-list>
+ *
+ * Response Payload in JSON:
+ * { "container-config" : [ { "name" : "black", "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|23@OF|00:00:00:00:00:00:20:21"], "staticVlan" : "10", "flowSpecs : [{ "name": "udp", "protocol": "UDP" }] } ] }
+ * { "container-config" : [ { "name" : "red", "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|23@OF|00:00:00:00:00:00:20:21"], "staticVlan" : "20", "flowSpecs": [{ "name": "tcp", "protocol": "TCP" }] } ] }
+ *
+ * </pre>
+ */
+ @Path("/all")
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @TypeHint(ContainerConfigs.class)
+ @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User is not authorized to perform this operation"),
+ @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
+ public ContainerConfigs viewAllContainers() {
+
+ handleNetworkAuthorization(getUserName());
+
+ IContainerManager containerManager = getContainerManager();
+
+ return new ContainerConfigs(containerManager.getContainerConfigList());
+ }
+
+ /**
+ * Get the container configuration for container name requested
+ *
+ * @param container
+ * name of the Container (eg. blue)
+ * @return a List of {@link org.opendaylight.controller.containermanager.ContainerConfig}
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/container/blue
+ *
+ * Response Payload in XML:
+ * <container-config>
+ *     <container>blue</container>
+ *     <staticVlan>10</staticVlan>
+ *     <nodeConnectors>OF|1@OF|00:00:00:00:00:00:00:01</nodeConnectors>
+ *     <nodeConnectors>OF|23@OF|00:00:00:00:00:00:20:21</nodeConnectors>
+ * </container-config>
+ *
+ * Response Payload in JSON:
+ * { "container" : "blue", "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|23@OF|00:00:00:00:00:00:20:21"], "staticVlan" : "10" }
+ *
+ * </pre>
+ */
+ @Path("/container/{container}")
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @TypeHint(ContainerConfig.class)
+ @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User is not authorized to perform this operation"),
+ @ResponseCode(code = 403, condition = "Operation forbidden on default"),
+ @ResponseCode(code = 404, condition = "The container is not found") })
+ public ContainerConfigs viewContainer(@PathParam(value = "container") String container) {
+
+ handleContainerAuthorization(container, getUserName());
+ handleForbiddenOnDefault(container);
+
+ handleContainerNotExists(container);
+
+ IContainerManager containerManager = getContainerManager();
+ List<ContainerConfig> containerConfigs = new ArrayList<ContainerConfig>();
+ containerConfigs.add(containerManager.getContainerConfig(container));
+ return new ContainerConfigs(containerConfigs);
+ }
+
+ /**
+ * Create a container
+ *
+ * @param uriInfo
+ * @param container
+ * name of the Container (eg. yellow)
+ * @param containerConfig
+ * details of the container as specified by:
+ * {@link org.opendaylight.controller.containermanager.ContainerConfig}
+ * @return Response as dictated by the HTTP Response Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/container/yellow
+ *
+ * Request Payload in XML:
+ * <container-config>
+ *     <container>yellow</container>
+ *     <staticVlan>10</staticVlan>
+ *     <nodeConnectors></nodeConnectors>
+ * </container-config>
+ *
+ * Request Payload in JSON:
+ * { "container" : "yellow", "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|23@OF|00:00:00:00:00:00:20:21"], "staticVlan" : "10"}
+ *
+ * </pre>
+ */
+ @Path("/container/{container}")
+ @POST
+ @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @StatusCodes({ @ResponseCode(code = 201, condition = "Container created successfully"),
+ @ResponseCode(code = 400, condition = "Invalid Container configuration."),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+ @ResponseCode(code = 403, condition = "Operation forbidden on default"),
+ @ResponseCode(code = 404, condition = "Container Name is not found"),
+ @ResponseCode(code = 409, condition = "Failed to create Container due to Conflicting Name"),
+ @ResponseCode(code = 500, condition = "Failure Reason included in HTTP Error response") })
+ public Response createContainer(@Context UriInfo uriInfo,
+ @PathParam(value = "container") String container,
+ @TypeHint(ContainerConfig.class) ContainerConfig containerConfig) {
+
+ handleAdminAuthorization(getUserName());
+ handleContainerExists(container);
+
+ handleNameMismatch(containerConfig.getContainerName(), container);
+ handleForbiddenOnDefault(container);
+
+ IContainerManager containerManager = getContainerManager();
+ Status status = containerManager.addContainer(containerConfig);
+ if (status.isSuccess()) {
+ NorthboundUtils.auditlog("Container", username, "added", container);
+ return Response.created(uriInfo.getRequestUri()).build();
+ }
+ return NorthboundUtils.getResponse(status);
+ }
+
+ /**
+ * Delete a container
+ *
+ * @param container
+ * name of the Container (eg. green)
+ * @return Response as dictated by the HTTP Response code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/container/green
+ *
+ * </pre>
+ */
+ @Path("/container/{container}")
+ @DELETE
+ @StatusCodes({
+ @ResponseCode(code = 204, condition = "Container deleted successfully"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+ @ResponseCode(code = 403, condition = "Operation forbidden on default"),
+ @ResponseCode(code = 404, condition = "The container is not found") })
+ public Response removeContainer(@PathParam(value = "container") String container) {
+
+ handleAdminAuthorization(getUserName());
+ handleForbiddenOnDefault(container);
+ handleContainerNotExists(container);
+ IContainerManager containerManager = getContainerManager();
+ Status status = containerManager.removeContainer(container);
+ if (status.isSuccess()) {
+ NorthboundUtils.auditlog("Container", username, "removed", container);
+ return Response.noContent().build();
+ }
+ return NorthboundUtils.getResponse(status);
+ }
+
+ /**
+ * Get flowspec within a given container
+ *
+ * @param container
+ * name of the Container (eg. green)
+ * @param name
+ * name of the flowspec (eg. ssh)
+ * @return flowspec detail as specified by:
+ * {@link org.opendaylight.controller.containermanager.ContainerFlowConfig}
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/container/green/flowspec/ssh
+ *
+ * Response Payload in XML:
+ * <container-flowconfig>
+ *     <name>ssh</name>
+ *     <nwSrc>10.0.0.101</nwSrc>
+ *     <nwDst>10.0.0.102</nwDst>
+ *     <protocol>IPv4</protocol>
+ *     <tpSrc>80</tpSrc>
+ *     <tpDst>100</tpDst>
+ * </container-flowconfig>
+ *
+ * Response Payload in JSON:
+ * { "protocol" : "IPv4", "nwDst" : "10.0.0.102", "name" : "ssh", "nwSrc" : "10.0.0.101", "tpSrc" : "80", "tpDst" : "100" }
+ *
+ * </pre>
+ */
+ @Path("/container/{container}/flowspec/{flowspec}")
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @TypeHint(ContainerFlowConfig.class)
+ @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+ @ResponseCode(code = 404, condition = "The container is not found"),
+ @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
+ public ContainerFlowConfig viewContainerFlowSpec(@PathParam(value = "container") String container,
+ @PathParam(value = "flowspec") String flowspec) {
+
+ handleContainerAuthorization(container, getUserName());
+ handleForbiddenOnDefault(container);
+
+ handleContainerNotExists(container);
+ IContainerManager containerManager = getContainerManager();
+ List<ContainerFlowConfig> flowSpecs = containerManager.getContainerFlows(container);
+
+ for (ContainerFlowConfig containerFlowConfig : flowSpecs) {
+ if (containerFlowConfig.equalsByName(flowspec)) {
+ return containerFlowConfig;
+ }
+ }
+ throw new ResourceNotFoundException("Flow Spec not found");
+ }
+
+ /**
+ * Get all the flowspec in a given container
+ *
+ * @param container
+ * name of the Container (eg. red)
+ * @return list of all flowspec configured for a container. Flowspec as
+ * specified by:
+ * {@link org.opendaylight.controller.containermanager.ContainerFlowConfig}
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/container/red/flowspec
+ *
+ * Response Payload in XML:
+ * <container-flowconfigs>
+ *     <container-flowconfig>
+ *       <name>ssh</name>
+ *       <nwSrc>10.0.0.101</nwSrc>
+ *       <nwDst>10.0.0.102</nwDst>
+ *       <protocol>IPv4</protocol>
+ *       <tpSrc>23</tpSrc>
+ *       <tpDst>100</tpDst>
+ *     </container-flowconfig>
+ *     <container-flowconfig>
+ *       <name>http2</name>
+ *       <nwSrc>10.0.0.201</nwSrc>
+ *       <nwDst>10.0.0.202</nwDst>
+ *       <protocol></protocol>
+ *       <tpSrc>80</tpSrc>
+ *       <tpDst>100</tpDst>
+ *     </container-flowconfig>
+ * </container-flowconfigs>
+ *
+ * Response Payload in JSON:
+ * { "protocol" : "IPv4", "nwDst" : "10.0.0.102", "name" : "ssh" , "nwSrc" : "10.0.0.101", "tpSrc" : "23", "tpDst" : "100" }
+ * { "protocol" : "", "nwDst" : "10.0.0.202", "name" : "http" , "nwSrc" : "10.0.0.201", "tpSrc" : "80", "tpDst" : "100" }
+ *
+ * </pre>
+ */
+ @Path("/container/{container}/flowspec")
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @TypeHint(FlowSpecConfigs.class)
+ @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 404, condition = "The container is not found"),
+ @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
+ public FlowSpecConfigs viewContainerFlowSpecs(@PathParam(value = "container") String container) {
+
+ handleContainerAuthorization(container, getUserName());
+ handleForbiddenOnDefault(container);
+
+ handleContainerNotExists(container);
+
+ IContainerManager containerManager = getContainerManager();
+
+ return new FlowSpecConfigs(containerManager.getContainerFlows(container));
+ }
+
+ /**
+ * Add flowspec to a container
+ *
+ * @param container
+ * name of the container (eg. purple)
+ * @param name
+ * name of the flowspec (eg. http)
+ * @param flowspec
+ * configuration as specified by:
+ * {@link org.opendaylight.controller.containermanager.ContainerFlowConfig}
+ *
+ * @return Response as dictated by the HTTP Response code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/container/purple/flowspec/http
+ *
+ * Request Payload in XML:
+ * <container-flowconfig>
+ *     <name>http</name>
+ *     <nwSrc>10.0.0.101</nwSrc>
+ *     <nwDst>10.0.0.102</nwDst>
+ *     <protocol></protocol>
+ *     <tpSrc>80</tpSrc>
+ *     <tpDst>100</tpDst>
+ * </container-flowconfig>
+ *
+ * Request Payload in JSON:
+ * { "protocol" : "", "nwDst" : "10.0.0.102", "name" : "http", "nwSrc" : "10.0.0.101", "tpSrc" : "80", "tpDst" : "100" }
+ *
+ * </pre>
+ */
+ @Path("/container/{container}/flowspec/{flowspec}")
+ @PUT
+ @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @StatusCodes({
+ @ResponseCode(code = 201, condition = "FlowSpec created successfully"),
+ @ResponseCode(code = 400, condition = "Invalid flowspec configuration"),
+ @ResponseCode(code = 404, condition = "The container is not found"),
+ @ResponseCode(code = 409, condition = "Container Entry already exists"),
+ @ResponseCode(code = 500, condition = "Failed to create Flow specifications. Failure Reason included in HTTP Error response") })
+ public Response createFlowSpec(@Context UriInfo uriInfo,
+ @PathParam(value = "container") String container,
+ @PathParam(value = "flowspec") String flowspec,
+ @TypeHint(ContainerFlowConfig.class) ContainerFlowConfig containerFlowConfig) {
+
+ handleAdminAuthorization(getUserName());
+ handleForbiddenOnDefault(container);
+
+ handleContainerNotExists(container);
+ handleNameMismatch(containerFlowConfig.getName(), flowspec);
+
+ IContainerManager containerManager = getContainerManager();
+ List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
+ list.add(containerFlowConfig);
+ Status status = containerManager.addContainerFlows(container, list);
+ if (status.isSuccess()) {
+ NorthboundUtils.auditlog("Flow Spec", username, "added", containerFlowConfig.getName());
+ return Response.created(uriInfo.getRequestUri()).build();
+ }
+ return NorthboundUtils.getResponse(status);
+ }
+
+ /**
+ * Remove flowspec from a container
+ *
+ * @param name
+ * name of the flowspec (eg. telnet)
+ * @param container
+ * name of the Container (eg. black)
+ * @return Response as dictated by the HTTP Response code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/container/black/flowspec/telnet
+ *
+ * </pre>
+ */
+ @Path("/container/{container}/flowspec/{flowspec}")
+ @DELETE
+ @StatusCodes({
+ @ResponseCode(code = 204, condition = "Flow Spec deleted successfully"),
+ @ResponseCode(code = 400, condition = "Invalid flowspec configuration"),
+ @ResponseCode(code = 404, condition = "Container or Container Entry not found"),
+ @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
+ @ResponseCode(code = 500, condition = "Failed to delete Flowspec. Failure Reason included in HTTP Error response"),
+ @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
+ public Response removeFlowSpec(@PathParam(value = "container") String container,
+ @PathParam(value = "flowspec") String flowspec) {
+
+ handleAdminAuthorization(getUserName());
+ handleForbiddenOnDefault(container);
+
+ handleContainerNotExists(container);
+
+ IContainerManager containerManager = getContainerManager();
+ Set<String> set = new HashSet<String>();
+ set.add(flowspec);
+ Status status = containerManager.removeContainerFlows(container, set);
+ if (status.isSuccess()) {
+ NorthboundUtils.auditlog("Flow Spec", username, "added", flowspec);
+ return Response.noContent().build();
+ }
+ return NorthboundUtils.getResponse(status);
+ }
+
+ /**
+ * Add node connectors to a container
+ *
+ * @param container
+ * name of the container (eg. green)
+ * @param list
+ * The list of strings each representing a node connector in the form "<Port Type>|<Port id>@<Node Type>|<Node id>", as "OF|1@OF|00:00:00:ab:00:00:00:01"
+ * @return response as dictated by the HTTP Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/container/green/nodeconnector
+ *
+ * Request Payload in XML:
+ * <list>
+ * <nodeConnectors>OF|1@OF|00:00:00:00:00:00:00:01</nodeConnectors>
+ * <nodeConnectors>OF|2@OF|00:00:00:00:00:00:00:01</nodeConnectors>
+ * <nodeConnectors>OF|3@OF|00:00:00:00:00:00:00:22</nodeConnectors>
+ * <nodeConnectors>OF|4@OF|00:00:00:00:00:00:00:22</nodeConnectors>
+ * </list>
+ *
+ * Request Payload in JSON:
+ * { "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|2@OF|00:00:00:00:00:00:00:01", "OF|3@OF|00:00:00:00:00:00:00:22", "OF|4@OF|00:00:00:00:00:00:00:22" }
+ *
+ * </pre>
+ */
+ @Path("/container/{container}/nodeconnector/")
+ @PUT
+ @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @TypeHint(Response.class)
+ @StatusCodes({
+ @ResponseCode(code = 200, condition = "NodeConnectors added successfully"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+ @ResponseCode(code = 403, condition = "Operation forbidden on default"),
+ @ResponseCode(code = 404, condition = "The Container is not found"),
+ @ResponseCode(code = 409, condition = "Container Entry already exists"),
+ @ResponseCode(code = 500, condition = "Failed to create nodeconnectors. Failure Reason included in HTTP Error response") })
+ public Response addNodeConnectors(@PathParam(value = "container") String container,
+ @TypeHint(StringList.class) StringList list) {
+
+ handleAdminAuthorization(getUserName());
+ handleForbiddenOnDefault(container);
+ handleContainerNotExists(container);
+
+ IContainerManager containerManager = getContainerManager();
+ Status status = containerManager.addContainerEntry(container, list.getList());
+ if (status.isSuccess()) {
+ NorthboundUtils.auditlog("Node ", username, "added", " Ports:" + list.getList());
+ }
+ return NorthboundUtils.getResponse(status);
+ }
+
+ /**
+ * Remove node connectors from a container
+ *
+ * @param container
+ * name of the container (eg. red)
+ * @param list
+ * The list of strings each representing a node connector in the form "<Port Type>|<Port id>@<Node Type>|<Node id>", as "OF|1@OF|00:00:00:ab:00:00:00:01"
+ * @return response as dictated by the HTTP Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://localhost:8080/controller/nb/v2/containermanager/container/red/nodeconnector
+ *
+ * Request Payload in XML:
+ * <list>
+ * <nodeConnectors>OF|1@OF|00:00:00:00:00:00:00:01</nodeConnectors>
+ * <nodeConnectors>OF|2@OF|00:00:00:00:00:00:00:01</nodeConnectors>
+ * <nodeConnectors>OF|3@OF|00:00:00:00:00:00:00:22</nodeConnectors>
+ * <nodeConnectors>OF|4@OF|00:00:00:00:00:00:00:22</nodeConnectors>
+ * </list>
+ *
+ * Request Payload in JSON:
+ * { "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|2@OF|00:00:00:00:00:00:00:01", "OF|3@OF|00:00:00:00:00:00:00:22", "OF|4@OF|00:00:00:00:00:00:00:22" }
+ *
+ * </pre>
+ */
+ @Path("/container/{container}/nodeconnector/")
+ @DELETE
+ @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @StatusCodes({
+ @ResponseCode(code = 204, condition = "Container Entry deleted successfully"),
+ @ResponseCode(code = 400, condition = "Invalid Container Entry configuration"),
+ @ResponseCode(code = 404, condition = "The Container is not found"),
+ @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
+ @ResponseCode(code = 500, condition = "Failed to delete node connector. Failure Reason included in HTTP Error response") })
+ public Response removeNodeConnectors(@PathParam(value = "container") String container,
+ @TypeHint(StringList.class) StringList portList) {
+
+ handleAdminAuthorization(getUserName());
+ handleForbiddenOnDefault(container);
+ handleContainerNotExists(container);
+
+ IContainerManager containerManager = getContainerManager();
+ Status status = containerManager.removeContainerEntry(container, portList.getList());
+ if (status.isSuccess()) {
+ NorthboundUtils.auditlog("Node", username, "removed", " Ports:" + portList.getList());
+ return Response.noContent().build();
+ }
+ return NorthboundUtils.getResponse(status);
+ }
+
+ /*
+ * Check If the function is not allowed on default container, Throw a
+ * ResourceForbiddenException exception if forbidden
+ */
+ private void handleForbiddenOnDefault(String container) {
+ if (container.equalsIgnoreCase(GlobalConstants.DEFAULT.toString())) {
+ throw new ResourceForbiddenException(RestMessages.NODEFAULT.toString() + ": " + container);
+ }
+ }
+
+ /*
+ * Check if container exists, Throw a ResourceNotFoundException exception if it
+ * does not exist
+ */
+ private void handleContainerNotExists(String container) {
+ IContainerManager containerManager = getContainerManager();
+ if (!containerManager.doesContainerExist(container)) {
+ throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString() + ": " + container);
+ }
+ }
+
+ private void handleContainerExists(String container) {
+ IContainerManager containerManager = getContainerManager();
+ if (containerManager.doesContainerExist(container)) {
+ throw new ResourceConflictException(RestMessages.RESOURCECONFLICT.toString() + ": " + container);
+ }
+ }
+
+ private void handleAdminAuthorization(String userName) {
+ IUserManager usrMgr = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
+
+ UserLevel level = usrMgr.getUserLevel(userName);
+ if (level.ordinal() <= UserLevel.NETWORKADMIN.ordinal()) {
+ return;
+ }
+
+ throw new UnauthorizedException("User is not authorized to perform this operation");
+ }
+
+ private void handleNetworkAuthorization(String userName) {
+ IUserManager usrMgr = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
+
+ UserLevel level = usrMgr.getUserLevel(userName);
+ if (level.ordinal() <= UserLevel.NETWORKOPERATOR.ordinal()) {
+ return;
+ }
+ throw new UnauthorizedException("User is not authorized to perform this operation");
+ }
+
+ private void handleContainerAuthorization(String container, String userName) {
+ IContainerAuthorization auth = (IContainerAuthorization) ServiceHelper.getGlobalInstance(
+ IContainerAuthorization.class, this);
+
+ UserLevel level = auth.getUserLevel(userName);
+ if (level.ordinal() <= UserLevel.NETWORKOPERATOR.ordinal()) {
+ return;
+ }
+
+ Privilege current = (auth == null) ? Privilege.NONE : auth.getResourcePrivilege(userName, container);
+
+ if (current.ordinal() > Privilege.NONE.ordinal()) {
+ return;
+ }
+ throw new UnauthorizedException("User is not authorized to perform this operation");
+ }
+
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager.northbound;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.core.Application;
+
+import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
+
+/**
+ * Instance of javax.ws.rs.core.Application used to return the classes
+ * that will be instantiated for JAXRS processing, this is necessary
+ * because the package scanning in jersey doesn't yet work in OSGi
+ * environment.
+ *
+ */
+public class ContainerManagerNorthboundRSApplication extends Application {
+ @Override
+ public Set<Class<?>> getClasses() {
+ Set<Class<?>> classes = new HashSet<Class<?>>();
+ classes.add(ContainerManagerNorthbound.class);
+ classes.add(JacksonJaxbJsonProvider.class);
+ return classes;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager.northbound;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.containermanager.ContainerFlowConfig;
+
+
+@XmlRootElement(name = "flow-spec-configs")
+@XmlAccessorType(XmlAccessType.NONE)
+public class FlowSpecConfigs {
+ @XmlElement(name = "flow-spec-config")
+ List<ContainerFlowConfig> containerFlowConfig;
+
+ // To satisfy JAXB
+ @SuppressWarnings("unused")
+ private FlowSpecConfigs() {
+
+ }
+
+ public FlowSpecConfigs(List<ContainerFlowConfig> containerFlowConfig) {
+ this.containerFlowConfig = containerFlowConfig;
+ }
+
+ public List<ContainerFlowConfig> getContainerFlowConfig() {
+ return containerFlowConfig;
+ }
+
+ public void setContainerFlowConfig(List<ContainerFlowConfig> containerFlowConfig) {
+ this.containerFlowConfig = containerFlowConfig;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.containermanager.northbound;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+//TODO: Remove this once StringList is provided by commons.northboud
+
+@XmlRootElement(name = "nodeConnectors")
+@XmlAccessorType(XmlAccessType.NONE)
+public class StringList {
+ @XmlElement(name = "nodeConnectors")
+ private List<String> list;
+
+ public StringList() {
+ }
+
+ public StringList(List<String> list) {
+ this.list = list;
+ }
+
+ public List<String> getList() {
+ return list;
+ }
+
+}
--- /dev/null
+org.springframework.beans.BeanInfoFactory=org.springframework.beans.ExtendedBeanInfoFactory
--- /dev/null
+http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
+http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
+http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
+http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
+http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
+http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
+http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
+http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
+http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
+
--- /dev/null
+http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
+http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
+http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
+http\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd
+http\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd
+http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd
+http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
+http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
+http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
+http\://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd
+http\://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd
+http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd
+http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
+http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
+http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
+http\://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd
+http\://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsd
+http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsd
+http\://www.springframework.org/schema/context/spring-context-2.5.xsd=org/springframework/context/config/spring-context-2.5.xsd
+http\://www.springframework.org/schema/context/spring-context-3.0.xsd=org/springframework/context/config/spring-context-3.0.xsd
+http\://www.springframework.org/schema/context/spring-context-3.1.xsd=org/springframework/context/config/spring-context-3.1.xsd
+http\://www.springframework.org/schema/context/spring-context-3.2.xsd=org/springframework/context/config/spring-context-3.2.xsd
+http\://www.springframework.org/schema/context/spring-context.xsd=org/springframework/context/config/spring-context-3.2.xsd
+http\://www.springframework.org/schema/jee/spring-jee-2.0.xsd=org/springframework/ejb/config/spring-jee-2.0.xsd
+http\://www.springframework.org/schema/jee/spring-jee-2.5.xsd=org/springframework/ejb/config/spring-jee-2.5.xsd
+http\://www.springframework.org/schema/jee/spring-jee-3.0.xsd=org/springframework/ejb/config/spring-jee-3.0.xsd
+http\://www.springframework.org/schema/jee/spring-jee-3.1.xsd=org/springframework/ejb/config/spring-jee-3.1.xsd
+http\://www.springframework.org/schema/jee/spring-jee-3.2.xsd=org/springframework/ejb/config/spring-jee-3.2.xsd
+http\://www.springframework.org/schema/jee/spring-jee.xsd=org/springframework/ejb/config/spring-jee-3.2.xsd
+http\://www.springframework.org/schema/lang/spring-lang-2.0.xsd=org/springframework/scripting/config/spring-lang-2.0.xsd
+http\://www.springframework.org/schema/lang/spring-lang-2.5.xsd=org/springframework/scripting/config/spring-lang-2.5.xsd
+http\://www.springframework.org/schema/lang/spring-lang-3.0.xsd=org/springframework/scripting/config/spring-lang-3.0.xsd
+http\://www.springframework.org/schema/lang/spring-lang-3.1.xsd=org/springframework/scripting/config/spring-lang-3.1.xsd
+http\://www.springframework.org/schema/lang/spring-lang-3.2.xsd=org/springframework/scripting/config/spring-lang-3.2.xsd
+http\://www.springframework.org/schema/lang/spring-lang.xsd=org/springframework/scripting/config/spring-lang-3.2.xsd
+http\://www.springframework.org/schema/task/spring-task-3.0.xsd=org/springframework/scheduling/config/spring-task-3.0.xsd
+http\://www.springframework.org/schema/task/spring-task-3.1.xsd=org/springframework/scheduling/config/spring-task-3.1.xsd
+http\://www.springframework.org/schema/task/spring-task-3.2.xsd=org/springframework/scheduling/config/spring-task-3.2.xsd
+http\://www.springframework.org/schema/task/spring-task.xsd=org/springframework/scheduling/config/spring-task-3.2.xsd
+http\://www.springframework.org/schema/cache/spring-cache-3.1.xsd=org/springframework/cache/config/spring-cache-3.1.xsd
+http\://www.springframework.org/schema/cache/spring-cache-3.2.xsd=org/springframework/cache/config/spring-cache-3.2.xsd
+http\://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache-3.2.xsd
+http\://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd=org/springframework/web/servlet/config/spring-mvc-3.0.xsd
+http\://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd=org/springframework/web/servlet/config/spring-mvc-3.1.xsd
+http\://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd=org/springframework/web/servlet/config/spring-mvc-3.2.xsd
+http\://www.springframework.org/schema/mvc/spring-mvc.xsd=org/springframework/web/servlet/config/spring-mvc-3.2.xsd
--- /dev/null
+# Tooling related information for the beans namespace
+http\://www.springframework.org/schema/beans@name=beans Namespace
+http\://www.springframework.org/schema/beans@prefix=beans
+http\://www.springframework.org/schema/beans@icon=org/springframework/beans/factory/xml/spring-beans.gif
+
+# Tooling related information for the util namespace
+http\://www.springframework.org/schema/util@name=util Namespace
+http\://www.springframework.org/schema/util@prefix=util
+http\://www.springframework.org/schema/util@icon=org/springframework/beans/factory/xml/spring-util.gif
+
+# Tooling related information for the context namespace
+http\://www.springframework.org/schema/context@name=context Namespace
+http\://www.springframework.org/schema/context@prefix=context
+http\://www.springframework.org/schema/context@icon=org/springframework/context/config/spring-context.gif
+
+# Tooling related information for the jee namespace
+http\://www.springframework.org/schema/jee@name=jee Namespace
+http\://www.springframework.org/schema/jee@prefix=jee
+http\://www.springframework.org/schema/jee@icon=org/springframework/ejb/config/spring-jee.gif
+
+# Tooling related information for the scheduling namespace
+http\://www.springframework.org/schema/task@name=task Namespace
+http\://www.springframework.org/schema/task@prefix=task
+http\://www.springframework.org/schema/task@icon=org/springframework/scheduling/config/spring-task.gif
+
+# Tooling related information for the lang namespace
+http\://www.springframework.org/schema/lang@name=lang Namespace
+http\://www.springframework.org/schema/lang@prefix=lang
+http\://www.springframework.org/schema/lang@icon=org/springframework/scripting/config/spring-lang.gif
+
+# Tooling related information for the cache namespace
+http\://www.springframework.org/schema/cache@name=cache Namespace
+http\://www.springframework.org/schema/cache@prefix=cache
+http\://www.springframework.org/schema/cache@icon=org/springframework/cache/config/spring-cache.gif
+
+# Tooling related information for the mvc namespace
+http\://www.springframework.org/schema/mvc@name=mvc Namespace
+http\://www.springframework.org/schema/mvc@prefix=mvc
+http\://www.springframework.org/schema/mvc@icon=org/springframework/web/servlet/config/spring-mvc.gif
--- /dev/null
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
+http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+ version="2.4">
+<servlet>
+ <servlet-name>JAXRSContainerManager</servlet-name>
+ <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>javax.ws.rs.Application</param-name>
+ <param-value>org.opendaylight.controller.containermanager.northbound.ContainerManagerNorthboundRSApplication</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+</servlet>
+ <servlet-mapping>
+ <servlet-name>JAXRSContainerManager</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>NB api</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>System-Admin</role-name>
+ <role-name>Network-Admin</role-name>
+ <role-name>Network-Operator</role-name>
+ <role-name>Container-User</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <security-role>
+ <role-name>System-Admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>Network-Admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>Network-Operator</role-name>
+ </security-role>
+ <security-role>
+ <role-name>Container-User</role-name>
+ </security-role>
+
+ <login-config>
+ <auth-method>BASIC</auth-method>
+ <realm-name>opendaylight</realm-name>
+ </login-config>
+</web-app>
xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd">
<services>
- <rest defaultRestSubcontext="/controller/nb/v2/flow"/>
+ <rest defaultRestSubcontext="/controller/nb/v2/flowprogrammer"/>
</services>
<modules>
</Import-Package>
<Export-Package>
</Export-Package>
- <Web-ContextPath>/controller/nb/v2/flow</Web-ContextPath>
+ <Web-ContextPath>/controller/nb/v2/flowprogrammer</Web-ContextPath>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
import org.opendaylight.controller.switchmanager.ISwitchManager;
/**
- * Flow Configuration Northbound API
+ * Flow Configuration Northbound API provides capabilities to program flows.
*
* <br>
* <br>
* Authentication realm : <b>opendaylight</b><br>
* Transport : <b>HTTP and HTTPS</b><br>
* <br>
- * HTTPS Authentication is disabled by default. Administrator can enable it in
- * tomcat-server.xml after adding a proper keystore / SSL certificate from a
- * trusted authority.<br>
- * More info :
- * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ * HTTPS Authentication is disabled by default.
*
*/
@Path("/")
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/flow/default
+ * http://localhost:8080/controller/nb/v2/flowprogrammer/default
*
* Response in XML:
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01
+ * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01
*
* Response in XML:
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+ * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
*
* Response in XML:
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
*
* </pre>
*/
- @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+ @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(FlowConfig.class)
}
/**
- * Add a flow configuration
+ * Add a flow configuration. If a flow by the given name already
+ * exists, this method will respond with a non-successful status response.
*
* @param containerName
* Name of the Container (Eg. 'default')
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+ * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
*
* Request in XML:
* <flowConfig>
* </pre>
*/
- @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+ @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
@PUT
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+ * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
*
* </pre>
*/
- @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+ @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
@DELETE
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
- @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
+ @ResponseCode(code = 204, condition = "Flow Config deleted successfully"),
@ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
@ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
Status status = frm.removeStaticFlow(name, node);
if (status.isSuccess()) {
NorthboundUtils.auditlog("Flow", username, "removed", name, containerName);
+ return Response.noContent().build();
}
return NorthboundUtils.getResponse(status);
}
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+ * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
*
* </pre>
*/
- @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+ @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
@POST
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
org.codehaus.jackson.jaxrs,
!org.codehaus.enunciate.jaxrs
</Import-Package>
- <Web-ContextPath>/controller/nb/v2/host</Web-ContextPath>
+ <Web-ContextPath>/controller/nb/v2/hosttracker</Web-ContextPath>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
* Authentication realm : <b>opendaylight</b><br>
* Transport : <b>HTTP and HTTPS</b><br>
* <br>
- * HTTPS Authentication is disabled by default. Administrator can enable it in
- * tomcat-server.xml after adding a proper keystore / SSL certificate from a
- * trusted authority.<br>
- * More info :
- * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ * HTTPS Authentication is disabled by default.
*
*/
*
* RequestURL:
*
- * http://localhost:8080/controller/nb/v2/host/default
+ * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/active
*
* Response in XML
*
* }
* </pre>
*/
- @Path("/{containerName}")
+ @Path("/{containerName}/hosts/active")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(Hosts.class)
*
* RequestURL:
*
- * http://localhost:8080/controller/nb/v2/host/default/inactive
+ * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/inactive
*
* Response in XML
*
* }
* </pre>
*/
- @Path("/{containerName}/inactive")
+ @Path("/{containerName}/hosts/inactive")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(Hosts.class)
*
* RequestURL:
*
- * http://localhost:8080/controller/nb/v2/host/default/1.1.1.1
+ * http://localhost:8080/controller/nb/v2/hosttracker/default/address/1.1.1.1
*
* Response in XML
*
* }
* </pre>
*/
- @Path("/{containerName}/{networkAddress}")
+ @Path("/{containerName}/address/{networkAddress}")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(HostConfig.class)
}
/**
- * Add a Static Host configuration
+ * Add a Static Host configuration. If a host by the given address already
+ * exists, this method will respond with a non-successful status response.
*
* @param containerName
* Name of the Container. The Container name for the base
*
* RequestURL:
*
- * http://localhost:8080/controller/nb/v2/host/default/1.1.1.1
+ * http://localhost:8080/controller/nb/v2/hosttracker/default/address/1.1.1.1
*
* Request in XML
*
* </pre>
*/
- @Path("/{containerName}/{networkAddress}")
+ @Path("/{containerName}/address/{networkAddress}")
@PUT
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
* @return Response as dictated by the HTTP Response code.
*/
- @Path("/{containerName}/{networkAddress}")
+ @Path("/{containerName}/address/{networkAddress}")
@DELETE
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.implementation</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <artifactId>containermanager.it.implementation</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
@Test
public void testSubnetsNorthbound() throws JSONException {
System.out.println("Starting Subnets JAXB client.");
- String baseURL = "http://127.0.0.1:8080/controller/nb/v2/subnet/";
+ String baseURL = "http://127.0.0.1:8080/controller/nb/v2/subnetservice/";
String name1 = "testSubnet1";
String subnet1 = "1.1.1.1/24";
nodePortsJson3_1.append(nodePortsJson3).append(",").append(nodePortsJson2);
// Test GET subnets in default container
- String result = getJsonResult(baseURL + "default/subnet/all");
+ String result = getJsonResult(baseURL + "default/subnets");
JSONTokener jt = new JSONTokener(result);
JSONObject json = new JSONObject(jt);
JSONArray subnetConfigs = json.getJSONArray("subnetConfig");
// Test POST subnet1
JSONObject jo = new JSONObject().put("name", name1).put("subnet", subnet1);
// execute HTTP request and verify response code
- result = getJsonResult(baseURL + "default/subnet/" + name1, "POST", jo.toString());
+ result = getJsonResult(baseURL + "default/subnet/" + name1, "PUT", jo.toString());
Assert.assertTrue(httpResponseCode == 201);
// Test GET subnet1
// Test POST subnet2
JSONObject jo2 = new JSONObject().put("name", name2).put("subnet", subnet2);
// execute HTTP request and verify response code
- result = getJsonResult(baseURL + "default/subnet/" + name2, "POST", jo2.toString());
+ result = getJsonResult(baseURL + "default/subnet/" + name2, "PUT", jo2.toString());
Assert.assertEquals(201, httpResponseCode.intValue());
// Test POST nodePorts
jo2.append("nodePorts", nodePortsJson2);
// execute HTTP request and verify response code
- result = getJsonResult(baseURL + "default/subnet/" + name2 + "/node-ports", "POST", jo2.toString());
+ result = getJsonResult(baseURL + "default/subnet/" + name2 + "/nodePorts", "PUT", jo2.toString());
Assert.assertEquals(200, httpResponseCode.intValue());
// Test POST subnet3
JSONObject jo3 = new JSONObject().put("name", name3).put("subnet", subnet3);
// execute HTTP request and verify response code
- result = getJsonResult(baseURL + "default/subnet/" + name3, "POST", jo3.toString());
+ result = getJsonResult(baseURL + "default/subnet/" + name3, "PUT", jo3.toString());
Assert.assertEquals(201, httpResponseCode.intValue());
// Test POST nodePorts
jo3.append("nodePorts", nodePortsJson3);
// execute HTTP request and verify response code
- result = getJsonResult(baseURL + "default/subnet/" + name3 + "/node-ports", "POST", jo3.toString());
+ result = getJsonResult(baseURL + "default/subnet/" + name3 + "/nodePorts", "PUT", jo3.toString());
Assert.assertEquals(200, httpResponseCode.intValue());
// Test PUT nodePorts
jo3.remove("nodePorts");
jo3.append("nodePorts", nodePortsJson3_1);
- result = getJsonResult(baseURL + "default/subnet/" + name3 + "/node-ports", "PUT", jo3.toString());
+ result = getJsonResult(baseURL + "default/subnet/" + name3 + "/nodePorts", "POST", jo3.toString());
Assert.assertEquals(200, httpResponseCode.intValue());
// Test GET all subnets in default container
- result = getJsonResult(baseURL + "default/subnet/all");
+ result = getJsonResult(baseURL + "default/subnets");
jt = new JSONTokener(result);
json = new JSONObject(jt);
JSONArray subnetConfigArray = json.getJSONArray("subnetConfig");
String nextHop2 = "1.1.1.1";
// Test GET static routes in default container, expecting no results
- String result = getJsonResult(baseURL + "default");
+ String result = getJsonResult(baseURL + "default/routes");
JSONTokener jt = new JSONTokener(result);
JSONObject json = new JSONObject(jt);
JSONArray staticRoutes = json.getJSONArray("staticRoute");
// Test insert static route
String requestBody = "{\"name\":\"" + name1 + "\", \"prefix\":\"" + prefix1 + "\", \"nextHop\":\"" + nextHop1
+ "\"}";
- result = getJsonResult(baseURL + "default/route/" + name1, "POST", requestBody);
+ result = getJsonResult(baseURL + "default/route/" + name1, "PUT", requestBody);
Assert.assertEquals(201, httpResponseCode.intValue());
requestBody = "{\"name\":\"" + name2 + "\", \"prefix\":\"" + prefix2 + "\", \"nextHop\":\"" + nextHop2 + "\"}";
- result = getJsonResult(baseURL + "default/route/" + name2, "POST", requestBody);
+ result = getJsonResult(baseURL + "default/route/" + name2, "PUT", requestBody);
Assert.assertEquals(201, httpResponseCode.intValue());
// Test Get all static routes
- result = getJsonResult(baseURL + "default");
+ result = getJsonResult(baseURL + "default/routes");
jt = new JSONTokener(result);
json = new JSONObject(jt);
JSONArray staticRouteArray = json.getJSONArray("staticRoute");
// Test delete static route
result = getJsonResult(baseURL + "default/route/" + name1, "DELETE");
- Assert.assertEquals(200, httpResponseCode.intValue());
+ Assert.assertEquals(204, httpResponseCode.intValue());
- result = getJsonResult(baseURL + "default");
+ result = getJsonResult(baseURL + "default/routes");
jt = new JSONTokener(result);
json = new JSONObject(jt);
@Test
public void testSwitchManager() throws JSONException {
System.out.println("Starting SwitchManager JAXB client.");
- String baseURL = "http://127.0.0.1:8080/controller/nb/v2/switch/default/";
+ String baseURL = "http://127.0.0.1:8080/controller/nb/v2/switchmanager/default/";
// define Node/NodeConnector attributes for test
int nodeId_1 = 51966;
// Delete state property of nodeconnector1
result = getJsonResult(baseURL + "nodeconnector/STUB/" + nodeId_1 + "/STUB/" + nodeConnectorId_1
+ "/property/state", "DELETE");
- Assert.assertEquals(200, httpResponseCode.intValue());
+ Assert.assertEquals(204, httpResponseCode.intValue());
result = getJsonResult(baseURL + "node/STUB/" + nodeId_1);
jt = new JSONTokener(result);
// Delete capabilities property of nodeconnector2
result = getJsonResult(baseURL + "nodeconnector/STUB/" + nodeId_2 + "/STUB/" + nodeConnectorId_2
+ "/property/capabilities", "DELETE");
- Assert.assertEquals(200, httpResponseCode.intValue());
+ Assert.assertEquals(204, httpResponseCode.intValue());
result = getJsonResult(baseURL + "node/STUB/" + nodeId_2);
jt = new JSONTokener(result);
@Test
public void testFlowProgrammer() throws JSONException {
System.out.println("Starting FlowProgrammer JAXB client.");
- String baseURL = "http://127.0.0.1:8080/controller/nb/v2/flow/default/";
+ String baseURL = "http://127.0.0.1:8080/controller/nb/v2/flowprogrammer/default/";
// Attempt to get a flow that doesn't exit. Should return 404
// status.
- String result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "GET");
+ String result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test1", "GET");
Assert.assertTrue(result.equals("404"));
// test add flow1
String fc = "{\"name\":\"test1\", \"node\":{\"id\":\"51966\",\"type\":\"STUB\"}, \"actions\":[\"DROP\"]}";
- result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "PUT", fc);
+ result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test1", "PUT", fc);
Assert.assertTrue(httpResponseCode == 201);
// test get returns flow that was added.
- result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "GET");
+ result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test1", "GET");
// check that result came out fine.
Assert.assertTrue(httpResponseCode == 200);
JSONTokener jt = new JSONTokener(result);
Assert.assertEquals(node.getString("id"), "51966");
// test adding same flow again fails due to repeat name..return 409
// code
- result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "PUT", fc);
+ result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test1", "PUT", fc);
Assert.assertTrue(result.equals("409"));
fc = "{\"name\":\"test2\", \"node\":{\"id\":\"51966\",\"type\":\"STUB\"}, \"actions\":[\"DROP\"]}";
- result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "PUT", fc);
+ result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test2", "PUT", fc);
// test should return 409 for error due to same flow being added.
Assert.assertTrue(result.equals("409"));
// add second flow that's different
fc = "{\"name\":\"test2\", \"nwSrc\":\"1.1.1.1\", \"node\":{\"id\":\"51966\",\"type\":\"STUB\"}, \"actions\":[\"DROP\"]}";
- result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "PUT", fc);
+ result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test2", "PUT", fc);
Assert.assertTrue(httpResponseCode == 201);
// check that request returns both flows given node.
Assert.assertTrue(count == 2);
// delete a flow, check that it's no longer in list.
- result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "DELETE");
- Assert.assertTrue(httpResponseCode == 200);
+ result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test2", "DELETE");
+ Assert.assertTrue(httpResponseCode == 204);
- result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "GET");
+ result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test2", "GET");
Assert.assertTrue(result.equals("404"));
}
Integer nodeConnectorId_2 = 34;
String vlan_2 = "123";
- String baseURL = "http://127.0.0.1:8080/controller/nb/v2/host/default";
+ String baseURL = "http://127.0.0.1:8080/controller/nb/v2/hosttracker/default";
// test PUT method: addHost()
JSONObject fc_json = new JSONObject();
fc_json.put("staticHost", "true");
fc_json.put("networkAddress", networkAddress_1);
- String result = getJsonResult(baseURL + "/" + networkAddress_1, "PUT", fc_json.toString());
+ String result = getJsonResult(baseURL + "/address/" + networkAddress_1, "PUT", fc_json.toString());
Assert.assertTrue(httpResponseCode == 201);
fc_json = new JSONObject();
fc_json.put("staticHost", "true");
fc_json.put("networkAddress", networkAddress_2);
- result = getJsonResult(baseURL + "/" + networkAddress_2 , "PUT", fc_json.toString());
+ result = getJsonResult(baseURL + "/address/" + networkAddress_2 , "PUT", fc_json.toString());
Assert.assertTrue(httpResponseCode == 201);
// define variables for decoding returned strings
// the two hosts should be in inactive host DB
// test GET method: getInactiveHosts()
- result = getJsonResult(baseURL + "/inactive", "GET");
+ result = getJsonResult(baseURL + "/hosts/inactive", "GET");
Assert.assertTrue(httpResponseCode == 200);
JSONTokener jt = new JSONTokener(result);
}
// test GET method: getActiveHosts() - no host expected
- result = getJsonResult(baseURL, "GET");
+ result = getJsonResult(baseURL + "/hosts/active", "GET");
Assert.assertTrue(httpResponseCode == 200);
jt = new JSONTokener(result);
// verify the host shows up in active host DB
- result = getJsonResult(baseURL, "GET");
+ result = getJsonResult(baseURL + "/hosts/active", "GET");
Assert.assertTrue(httpResponseCode == 200);
jt = new JSONTokener(result);
// test GET method for getHostDetails()
- result = getJsonResult(baseURL + "/" + networkAddress_1, "GET");
+ result = getJsonResult(baseURL + "/address/" + networkAddress_1, "GET");
Assert.assertTrue(httpResponseCode == 200);
jt = new JSONTokener(result);
// test DELETE method for deleteFlow()
- result = getJsonResult(baseURL + "/" + networkAddress_1, "DELETE");
+ result = getJsonResult(baseURL + "/address/" + networkAddress_1, "DELETE");
Assert.assertTrue(httpResponseCode == 204);
// verify host_1 removed from active host DB
// test GET method: getActiveHosts() - no host expected
- result = getJsonResult(baseURL, "GET");
+ result = getJsonResult(baseURL + "/hosts/active", "GET");
Assert.assertTrue(httpResponseCode == 200);
jt = new JSONTokener(result);
.put("dstNodeConnector",
nodeConnectorType_2 + "|" + nodeConnectorId_2 + "@" + nodeType_2 + "|" + nodeId_2);
// execute HTTP request and verify response code
- result = getJsonResult(baseURL + "/user-link", "PUT", jo.toString());
+ result = getJsonResult(baseURL + "/userLink/userLink_1", "PUT", jo.toString());
Assert.assertTrue(httpResponseCode == 201);
// === test GET method for getUserLinks()
- result = getJsonResult(baseURL + "/user-link", "GET");
+ result = getJsonResult(baseURL + "/userLinks", "GET");
Assert.assertTrue(httpResponseCode == 200);
if (debugMsg) {
System.out.println("result:" + result);
// === test DELETE method for deleteUserLink()
String userName = "userLink_1";
- result = getJsonResult(baseURL + "/user-link/" + userName, "DELETE");
- Assert.assertTrue(httpResponseCode == 200);
+ result = getJsonResult(baseURL + "/userLink/" + userName, "DELETE");
+ Assert.assertTrue(httpResponseCode == 204);
// execute another getUserLinks() request to verify that userLink_1 is
// removed
- result = getJsonResult(baseURL + "/user-link", "GET");
+ result = getJsonResult(baseURL + "/userLinks", "GET");
Assert.assertTrue(httpResponseCode == 200);
if (debugMsg) {
System.out.println("result:" + result);
mavenBundle("org.opendaylight.controller", "configuration").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "configuration.implementation").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "containermanager").versionAsInProject(),
- mavenBundle("org.opendaylight.controller", "containermanager.implementation").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller", "containermanager.it.implementation").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "clustering.services").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "clustering.services-implementation").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "security").versionAsInProject().noStart(),
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.codehaus.enunciate</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
* Example:
*
* Request URL:
- * GET http://localhost:8080/controller/nb/v2/staticroute/default
+ * GET http://localhost:8080/controller/nb/v2/staticroute/default/routes
*
* Response in XML:
* <list>
*
* </pre>
*/
- @Path("/{containerName}")
+ @Path("/{containerName}/routes")
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(StaticRoutes.class)
/**
*
- * Add a new Static Route
+ * Add a new Static Route. If a route by the given name already exists, this
+ * method will return a non-successful status response.
*
* @param containerName Name of the Container. The Container name for the base controller is "default".
* @param route Name of the Static Route configuration
* Example:
*
* Request URL:
- * POST http://localhost:8080/controller/nb/v2/staticroute/default/route/route-1
+ * PUT http://localhost:8080/controller/nb/v2/staticroute/default/route/route-1
*
* Request payload in JSON:
* {"name":"route-1","prefix":"10.10.1.0/24","nextHop":"1.1.1.1"}
* </pre>
*/
@Path("/{containerName}/route/{route}")
- @POST
+ @PUT
@Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes( {
@ResponseCode(code = 201, condition = "Created Static Route successfully"),
@Path("/{containerName}/route/{route}")
@DELETE
@StatusCodes( {
- @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 204, condition = "Static route removed successfully"),
@ResponseCode(code = 404, condition = "Container Name or Configuration Name not found"),
@ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active") })
public Response removeStaticRoute(
Status status = staticRouting.removeStaticRoute(route);
if (status.isSuccess()) {
NorthboundUtils.auditlog("Static Route", username, "removed", route, containerName);
- return Response.ok().build();
+ return Response.noContent().build();
}
- throw new ResourceNotFoundException(status.getDescription());
+ return NorthboundUtils.getResponse(status);
}
private void handleDefaultDisabled(String containerName) {
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
* Authentication realm : <b>opendaylight</b><br>
* Transport : <b>HTTP and HTTPS</b><br>
* <br>
- * HTTPS Authentication is disabled by default. Administrator can enable it in
- * tomcat-server.xml after adding a proper keystore / SSL certificate from a
- * trusted authority.<br>
- * More info :
- * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ * HTTPS Authentication is disabled by default.
*
*/
@Path("/")
xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd">
<services>
- <rest defaultRestSubcontext="/controller/nb/v2/subnet"/>
+ <rest defaultRestSubcontext="/controller/nb/v2/subnetservice"/>
</services>
<modules>
- <docs docsDir="rest" title="REST API" includeExampleXml="true" includeExampleJson="true"/>
+ <docs docsDir="rest" title="Subnet Service REST API" includeExampleXml="true" includeExampleJson="true"/>
</modules>
</enunciate>
</Import-Package>
<Export-Package>
</Export-Package>
- <Web-ContextPath>/controller/nb/v2/subnet</Web-ContextPath>
+ <Web-ContextPath>/controller/nb/v2/subnetservice</Web-ContextPath>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
package org.opendaylight.controller.subnets.northbound;
import java.util.HashSet;
+
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * This class provides REST APIs to manage subnets.
+ *
+ * <br>
+ * <br>
+ * Authentication scheme : <b>HTTP Basic</b><br>
+ * Authentication realm : <b>opendaylight</b><br>
+ * Transport : <b>HTTP and HTTPS</b><br>
+ * <br>
+ * HTTPS Authentication is disabled by default.
+ *
+ */
+
@Path("/")
public class SubnetsNorthbound {
protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthbound.class);
* <pre>
* Example:
*
- * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/all
+ * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnets
*
* Response in XML:
* <subnetConfig>
* }
* </pre>
*/
- @Path("/{containerName}/subnet/all")
+ @Path("/{containerName}/subnets")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
* <pre>
* Example:
*
- * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
+ * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1
*
* Response in XML:
* <subnetConfig>
}
/**
- * Add a subnet to a container
+ * Add a subnet to a container. If a subnet by the given name already exists
+ * this method will return a non-successful response.
*
* @param containerName
* name of the container to which subnet needs to be added
* <pre>
* Example:
*
- * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
+ * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1
*
* Request XML:
* <subnetConfig>
*/
@Path("/{containerName}/subnet/{subnetName}")
- @POST
+ @PUT
@StatusCodes({ @ResponseCode(code = 201, condition = "Subnet created successfully"),
@ResponseCode(code = 400, condition = "Invalid data passed"),
@ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
*
* <pre>
* Example:
- * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
+ * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1
*
* </pre>
*/
* <pre>
* Example:
*
- * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
+ * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1/nodePorts
*
* Request in XML:
* <subnetConfig>
* }
* </pre>
*/
- @Path("/{containerName}/subnet/{subnetName}/node-ports")
- @PUT
+ @Path("/{containerName}/subnet/{subnetName}/nodePorts")
+ @POST
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
@ResponseCode(code = 200, condition = "Ports replaced successfully"),
}
/**
- * Add ports to a subnet in the container
+ * Add ports to a subnet in the container.
*
* @param containerName
* name of the container that has the subnet to which node ports
*
* <pre>
* Example:
- * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
+ * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1/nodePorts
*
* Request XML:
* <subnetConfig>
*
* </pre>
*/
- @Path("/{containerName}/subnet/{subnetName}/node-ports")
- @POST
+ @Path("/{containerName}/subnet/{subnetName}/nodePorts")
+ @PUT
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
@ResponseCode(code = 200, condition = "Added node ports to subnet successfully"),
*
* <pre>
* Example:
- * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
+ * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1/nodePorts
*
* Request XML:
* <subnetConfig>
*
* </pre>
*/
- @Path("/{containerName}/subnet/{subnetName}/node-ports")
+ @Path("/{containerName}/subnet/{subnetName}/nodePorts")
@DELETE
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
}
}
if (successful) {
- return Response.status(Response.Status.NO_CONTENT).build();
+ return Response.noContent().build();
}
throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
}
xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd">
<services>
- <rest defaultRestSubcontext="/controller/nb/v2/switch"/>
+ <rest defaultRestSubcontext="/controller/nb/v2/switchmanager"/>
</services>
<modules>
org.codehaus.jackson.jaxrs,
!org.codehaus.enunciate.jaxrs
</Import-Package>
- <Web-ContextPath>/controller/nb/v2/switch</Web-ContextPath>
+ <Web-ContextPath>/controller/nb/v2/switchmanager</Web-ContextPath>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/switch/default/nodes
+ * http://localhost:8080/controller/nb/v2/switchmanager/default/nodes
*
* Response in XML:
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
List<NodeProperties> res = new ArrayList<NodeProperties>();
Set<Node> nodes = switchManager.getNodes();
if (nodes == null) {
- return null;
+ return new Nodes(res);
}
for (Node node : nodes) {
}
/**
- * Add a Description, Tier and Forwarding mode property to a node.
+ * Add a Description, Tier and Forwarding mode property to a node. This
+ * method returns a non-successful response if a node by that name already
+ * exists.
*
* @param containerName
* Name of the Container (Eg. 'default')
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:00:00:03/property/description/Switch3
+ * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:03/property/description/Switch3
*
* </pre>
*/
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:00:00:03/property/forwarding
+ * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:03/property/forwarding
*
* </pre>
*/
@DELETE
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
- @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 204, condition = "Property removed successfully"),
@ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
@ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
SwitchConfig newSwitchConfig = new SwitchConfig(node.toString(), nodeProperties);
status = switchManager.updateNodeConfig(newSwitchConfig);
if(status.isSuccess()){
- NorthboundUtils.auditlog("Static Route", username, "updated", nodeId, containerName);
+ NorthboundUtils.auditlog("Node Property", username, "removed", propertyName + " from " + nodeId, containerName);
+ return Response.noContent().build();
}
}
}
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:00:00:01
+ * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:01
*
* Response in XML:
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
}
/**
- * Add Bandwidth property to a node connector
+ * Add node-connector property to a node connector. This method returns a
+ * non-successful response if a node connector by the given name already
+ * exists.
*
* @param containerName
* Name of the Container (Eg. 'default')
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/switch/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth/1
+ * http://localhost:8080/controller/nb/v2/switchmanager/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth/1
*
* </pre>
*/
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/switch/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth
+ * http://localhost:8080/controller/nb/v2/switchmanager/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth
*
* </pre>
*/
@DELETE
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
- @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 204, condition = "Property removed successfully"),
@ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
@ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
Status ret = switchManager.removeNodeConnectorProp(nc, propertyName);
if (ret.isSuccess()) {
NorthboundUtils.auditlog("Node Connector Property", username, "removed", nc + " from " + nodeConnectorId, containerName);
- return Response.ok().build();
+ return Response.noContent().build();
}
throw new ResourceNotFoundException(ret.getDescription());
}
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/switch/default/switch-config
+ * http://localhost:8080/controller/nb/v2/switchmanager/default/save
*
* </pre>
*/
- @Path("/{containerName}/switch-config")
+ @Path("/{containerName}/save")
@POST
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
</services>
<modules>
- <docs docsDir="rest" title="REST API" includeExampleXml="true" includeExampleJson="true"/>
+ <docs docsDir="rest" title="Topology REST API" includeExampleXml="true" includeExampleJson="true"/>
</modules>
</enunciate>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
* Authentication realm : <b>opendaylight</b><br>
* Transport : <b>HTTP and HTTPS</b><br>
* <br>
- * HTTPS Authentication is disabled by default. Administrator can enable it in
- * tomcat-server.xml after adding a proper keystore / SSL certificate from a
- * trusted authority.<br>
- * More info :
- * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ * HTTPS Authentication is disabled by default.
*/
@Path("/")
* The container for which we want to retrieve the topology (Eg.
* 'default')
*
- * @return A List of EdgeProps each EdgeProp represent an Edge of the grap
+ * @return A List of EdgeProps each EdgeProp represent an Edge of the graph
* with the corresponding properties attached to it.
*
* <pre>
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/topology/default/user-link
+ * http://localhost:8080/controller/nb/v2/topology/default/userLinks
*
* Response in XML:
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
*
* </pre>
*/
- @Path("/{containerName}/user-link")
+ @Path("/{containerName}/userLinks")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(TopologyUserLinks.class)
*
* @param containerName
* Name of the Container (Eg. 'default')
+ * @param name
+ * Name of the user link
* @param TopologyUserLinkConfig
* in JSON or XML format
* @return Response as dictated by the HTTP Response Status code
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/topology/default/user-link
+ * http://localhost:8080/controller/nb/v2/topology/default/userLink/link1
*
* Request in XML:
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
*
* </pre>
*/
- @Path("/{containerName}/user-link")
+ @Path("/{containerName}/userLink/{name}")
@PUT
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
public Response addUserLink(
@PathParam(value = "containerName") String containerName,
+ @PathParam(value = "name") String name,
@TypeHint(TopologyUserLinkConfig.class) TopologyUserLinkConfig userLinkConfig) {
if (!NorthboundUtils.isAuthorized(
* Example:
*
* RequestURL:
- * http://localhost:8080/controller/nb/v2/topology/default/user-link/config1
+ * http://localhost:8080/controller/nb/v2/topology/default/userLink/config1
*
* </pre>
*/
- @Path("/{containerName}/user-link/{name}")
+ @Path("/{containerName}/userLink/{name}")
@DELETE
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
- @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 204, condition = "User link removed successfully"),
@ResponseCode(code = 404, condition = "The Container Name or Link Configuration Name was not found"),
@ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
public Response deleteUserLink(
Status ret = topologyManager.deleteUserLink(name);
if (ret.isSuccess()) {
NorthboundUtils.auditlog("User Link", username, "removed", name, containerName);
- return Response.ok().build();
+ return Response.noContent().build();
}
- throw new ResourceNotFoundException(ret.getDescription());
+ return NorthboundUtils.getResponse(ret);
}
}
import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
-import org.openflow.protocol.OFPhysicalPort;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
import org.opendaylight.controller.sal.core.Config;
import org.opendaylight.controller.sal.core.ConstructionException;
-import org.opendaylight.controller.sal.core.Edge;
import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.Edge;
import org.opendaylight.controller.sal.core.IContainerListener;
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.controller.sal.utils.NetUtils;
import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.openflow.protocol.OFPhysicalPort;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* The class describes neighbor discovery service for an OpenFlow network.
private void addDiscovery(Node node) {
Map<Long, ISwitch> switches = controller.getSwitches();
ISwitch sw = switches.get(node.getID());
- List<OFPhysicalPort> ports = sw.getEnabledPorts();
- if (ports == null) {
+ if (sw == null) {
+ //switch could be removed by now, stop propagation
return;
}
+ List<OFPhysicalPort> ports = sw.getEnabledPorts();
for (OFPhysicalPort port : ports) {
NodeConnector nodeConnector = NodeConnectorCreator.createOFNodeConnector(port.getPortNumber(), node);
if (!readyListHi.contains(nodeConnector)) {
Set<Property> props = new HashSet<Property>();
Long sid = (Long) node.getID();
- Date connectedSince = controller.getSwitches().get(sid)
- .getConnectedDate();
+ Date connectedSince = sw.getConnectedDate();
Long connectedSinceTime = (connectedSince == null) ? 0 : connectedSince
.getTime();
props.add(new TimeStamp(connectedSinceTime, "connectedSince"));
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.implementation</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <artifactId>containermanager.it.implementation</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
// needed by statisticsmanager
mavenBundle("org.opendaylight.controller", "containermanager")
.versionAsInProject(),
- mavenBundle("org.opendaylight.controller", "containermanager.implementation")
+ mavenBundle("org.opendaylight.controller", "containermanager.it.implementation")
.versionAsInProject(),
mavenBundle("org.opendaylight.controller", "clustering.services")
.versionAsInProject(),
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.implementation</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <artifactId>containermanager.it.implementation</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
mavenBundle("org.opendaylight.controller", "containermanager")
.versionAsInProject(),
mavenBundle("org.opendaylight.controller",
- "containermanager.implementation").versionAsInProject(),
+ "containermanager.it.implementation").versionAsInProject(),
mavenBundle("org.opendaylight.controller",
"clustering.services").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "sal")
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
private final String name;
private final Boolean me;
private final Boolean coordinator;
+ private final Integer numConnectedNodes;
public static class Builder {
// required params
// optional params
private Boolean me = null;
private Boolean coordinator = null;
+ private Integer numConnectedNodes = null;
public Builder(InetAddress address) {
this.address = address.getAddress();
return this;
}
+ public Builder nodesConnected(int numNodes) {
+ this.numConnectedNodes = numNodes;
+ return this;
+ }
+
public ClusterNodeBean build() {
return new ClusterNodeBean(this);
}
this.name = builder.name;
this.me = builder.me;
this.coordinator = builder.coordinator;
+ this.numConnectedNodes = builder.numConnectedNodes;
}
}
\ No newline at end of file
if (clusterServices == null) {
return null;
}
+ IConnectionManager connectionManager = (IConnectionManager) ServiceHelper.getGlobalInstance(
+ IConnectionManager.class, this);
+ if (connectionManager == null) {
+ return null;
+ }
List<ClusterNodeBean> clusterNodes = new ArrayList<ClusterNodeBean>();
List<InetAddress> controllers = clusterServices.getClusteredControllers();
for (InetAddress controller : controllers) {
ClusterNodeBean.Builder clusterBeanBuilder = new ClusterNodeBean.Builder(controller);
+
+ //get number of connected nodes
+ Set<Node> connectedNodes = connectionManager.getNodes(controller);
+ int numNodes = connectedNodes == null ? 0 : connectedNodes.size();
+ clusterBeanBuilder.nodesConnected(numNodes);
+
+ //determine if this is the executing controller
if (controller.equals(clusterServices.getMyAddress())) {
clusterBeanBuilder.highlightMe();
}
+
+ //determine whether this is coordinator
if (clusterServices.getCoordinatorAddress().equals(controller)) {
clusterBeanBuilder.iAmCoordinator();
}
one.main = {};
one.main.constants = {
- address : {
- menu : "/web.json",
- prefix : "/controller/web",
- save : "/save"
- }
+ address : {
+ menu : "/web.json",
+ prefix : "/controller/web",
+ save : "/save"
+ }
}
one.main.menu = {
- load : function() {
- one.main.menu.ajax(function(data) {
- // reparse the ajax data
- var result = one.main.menu.data.menu(data);
- // transform into list to append to menu
- var $div = one.main.menu.menu(result);
- // append to menu
- $("#menu .nav").append($div.children());
- // binding all menu items
- var $menu = $("#menu .nav a");
- $menu.click(function() {
- var href = $(this).attr('href').substring(1);
- one.main.page.load(href);
- var $li = $(this).parent();
- // reset all other active
- $menu.each(function(index, value) {
- $(value).parent().removeClass('active');
- });
- $li.addClass('active');
- });
- // reset or go to first menu item by default
- var currentLocation = location.hash;
- if (data[currentLocation.substring(1)] == undefined) {
- $($menu[0]).click();
- } else {
- $menu.each(function(index, value) {
- var menuLocation = $(value).attr('href');
- if (currentLocation == menuLocation) {
- $($menu[index]).click();
- return;
- }
- });
- }
- });
- },
- menu : function(result) {
- var $div = $(document.createElement('div'));
- $(result).each(function(index, value) {
- if (value != undefined) {
- var $li = $(document.createElement('li'));
- var $a = $(document.createElement('a'));
- $a.text(value['name']);
- $a.attr('href', '#' + value['id']);
- $li.append($a);
- $div.append($li);
- }
+ registry : {
+ load : false
+ },
+ load : function() {
+ one.main.menu.ajax(function(data) {
+ // reparse the ajax data
+ var result = one.main.menu.data.menu(data);
+ // transform into list to append to menu
+ var $div = one.main.menu.menu(result);
+ // append to menu
+ $("#menu .nav").append($div.children());
+ // binding all menu items
+ var $menu = $("#menu .nav a");
+ $menu.click(function() {
+ if (one.main.menu.registry.load === true) {
+ return false;
+ }
+ one.main.menu.registry.load = true;
+ var href = $(this).attr('href').substring(1);
+ one.main.page.load(href);
+ var $li = $(this).parent();
+ // reset all other active
+ $menu.each(function(index, value) {
+ $(value).parent().removeClass('active');
});
- return $div;
- },
- ajax : function(successCallback) {
- $.getJSON(one.main.constants.address.menu, function(data) {
- successCallback(data);
+ $li.addClass('active');
+ });
+ // reset or go to first menu item by default
+ var currentLocation = location.hash;
+ if (data[currentLocation.substring(1)] == undefined) {
+ $($menu[0]).click();
+ } else {
+ $menu.each(function(index, value) {
+ var menuLocation = $(value).attr('href');
+ if (currentLocation == menuLocation) {
+ $($menu[index]).click();
+ return;
+ }
});
- },
- data : {
- menu : function(data) {
- var result = [];
- $.each(data, function(key, value) {
- var order = value['order'];
- if (order >= 0) {
- var name = value['name'];
- var entry = {
- 'name' : name,
- 'id' : key
- };
- result[order] = entry;
- }
- });
- return result;
+ }
+ });
+ },
+ menu : function(result) {
+ var $div = $(document.createElement('div'));
+ $(result).each(function(index, value) {
+ if (value != undefined) {
+ var $li = $(document.createElement('li'));
+ var $a = $(document.createElement('a'));
+ $a.text(value['name']);
+ $a.attr('href', '#' + value['id']);
+ $li.append($a);
+ $div.append($li);
+ }
+ });
+ return $div;
+ },
+ ajax : function(successCallback) {
+ $.getJSON(one.main.constants.address.menu, function(data) {
+ successCallback(data);
+ });
+ },
+ data : {
+ menu : function(data) {
+ var result = [];
+ $.each(data, function(key, value) {
+ var order = value['order'];
+ if (order >= 0) {
+ var name = value['name'];
+ var entry = {
+ 'name' : name,
+ 'id' : key
+ };
+ result[order] = entry;
}
+ });
+ return result;
}
+ }
}
one.main.page = {
- load : function(page) {
- if (one.f !== undefined && one.f.cleanUp !== undefined) {
- one.f.cleanUp();
- }
- // clear page related
- delete one.f;
- $('.dashlet', '#main').empty();
- $('.nav', '#main').empty();
- // fetch page's js
- $.getScript(one.main.constants.address.prefix + "/" + page
- + "/js/page.js");
-
- $.ajaxSetup({
- data : {
- 'x-page-url' : page
- }
- });
- },
- dashlet : function($nav, dashlet) {
- var $li = $(document.createElement('li'));
- var $a = $(document.createElement('a'));
- $a.text(dashlet.name);
- $a.attr('id', dashlet.id);
- $a.attr('href', '#');
- $li.append($a);
- $nav.append($li);
+ load : function(page) {
+ if (one.f !== undefined && one.f.cleanUp !== undefined) {
+ one.f.cleanUp();
}
+ // clear page related
+ delete one.f;
+ $('.dashlet', '#main').empty();
+ $('.nav', '#main').empty();
+ // fetch page's js
+ $.getScript(one.main.constants.address.prefix+"/"+page+"/js/page.js")
+ .success(function() {
+ one.main.menu.registry.load = false;
+ });
+
+ $.ajaxSetup({
+ data : {
+ 'x-page-url' : page
+ }
+ });
+ },
+ dashlet : function($nav, dashlet) {
+ var $li = $(document.createElement('li'));
+ var $a = $(document.createElement('a'));
+ $a.text(dashlet.name);
+ $a.attr('id', dashlet.id);
+ $a.attr('href', '#');
+ $li.append($a);
+ $nav.append($li);
+ }
}
one.main.admin = {
- id : {
- modal : {
- main : "one_main_admin_id_modal_main",
- close : "one_main_admin_id_modal_close",
- user : "one_main_admin_id_modal_user",
- add : {
- user : "one_main_admin_id_modal_add_user",
- close : "one_main_admin_id_modal_add_close",
- form : {
- name : "one_main_admin_id_modal_add_form_name",
- role : "one_main_admin_id_modal_add_form_role",
- password : "one_main_admin_id_modal_add_form_password",
- verify : "one_main_admin_id_modal_add_form_verify"
- }
- },
- remove : {
- user : "one_main_admin_id_modal_remove_user",
- close : "one_main_admin_id_modal_remove_close",
- password : 'one_main_admin_id_modal_remove_password'
- },
- password : {
- modal : 'one_main_admin_id_modal_password_modal',
- submit : 'one_main_admin_id_modal_password_submit',
- cancel : 'one_main_admin_id_modal_password_cancel',
- form : {
- old : 'one_main_admin_id_modal_password_form_old',
- set : 'one_main_admin_id_modal_password_form_new',
- verify : 'one_main_admin_id_modal_password_form_verify'
- }
- }
- },
- add : {
- user : "one_main_admin_id_add_user"
+ id : {
+ modal : {
+ main : "one_main_admin_id_modal_main",
+ close : "one_main_admin_id_modal_close",
+ user : "one_main_admin_id_modal_user",
+ add : {
+ user : "one_main_admin_id_modal_add_user",
+ close : "one_main_admin_id_modal_add_close",
+ form : {
+ name : "one_main_admin_id_modal_add_form_name",
+ role : "one_main_admin_id_modal_add_form_role",
+ password : "one_main_admin_id_modal_add_form_password",
+ verify : "one_main_admin_id_modal_add_form_verify"
+ }
+ },
+ remove : {
+ user : "one_main_admin_id_modal_remove_user",
+ close : "one_main_admin_id_modal_remove_close",
+ password : 'one_main_admin_id_modal_remove_password'
+ },
+ password : {
+ modal : 'one_main_admin_id_modal_password_modal',
+ submit : 'one_main_admin_id_modal_password_submit',
+ cancel : 'one_main_admin_id_modal_password_cancel',
+ form : {
+ old : 'one_main_admin_id_modal_password_form_old',
+ set : 'one_main_admin_id_modal_password_form_new',
+ verify : 'one_main_admin_id_modal_password_form_verify'
}
+ }
},
- address : {
- root : "/admin",
- users : "/users",
- password : '/admin/users/password/'
+ add : {
+ user : "one_main_admin_id_add_user"
+ }
+ },
+ address : {
+ root : "/admin",
+ users : "/users",
+ password : '/admin/users/password/'
+ },
+ modal : {
+ initialize : function(callback) {
+ var h3 = "Welcome " + $('#admin').text();
+ var footer = one.main.admin.modal.footer();
+ var $modal = one.lib.modal.spawn(one.main.admin.id.modal.main, h3,
+ '', footer);
+
+ // close binding
+ $('#' + one.main.admin.id.modal.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+
+ // body inject
+ one.main.admin.ajax.users(function($body) {
+ one.lib.modal.inject.body($modal, $body);
+ });
+
+ // modal show callback
+ callback($modal);
},
- modal : {
- initialize : function(callback) {
- var h3 = "Welcome " + $('#admin').text();
- var footer = one.main.admin.modal.footer();
- var $modal = one.lib.modal.spawn(one.main.admin.id.modal.main, h3,
- '', footer);
-
- // close binding
- $('#' + one.main.admin.id.modal.close, $modal).click(function() {
- $modal.modal('hide');
- });
+ footer : function() {
+ var footer = [];
+ var closeButton = one.lib.dashlet.button.single('Close', one.main.admin.id.modal.close, '', '');
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
+ return footer;
+ }
+ },
+ ajax : {
+ users : function(callback) {
+ $.getJSON(one.main.admin.address.root
+ + one.main.admin.address.users, function(data) {
+ var body = one.main.admin.data.users(data);
+ var $body = one.main.admin.body.users(body);
+ callback($body);
+ });
+ }
+ },
+ data : {
+ users : function(data) {
+ var body = [];
+ $(data).each(function(index, value) {
+ var tr = {};
+ var entry = [];
+ entry.push(value['user']);
+ entry.push(value['roles']);
+ tr['entry'] = entry;
+ tr['id'] = value['user'];
+ body.push(tr);
+ });
+ return body;
+ }
+ },
+ body : {
+ users : function(body) {
+ var $div = $(document.createElement('div'));
+ var $h5 = $(document.createElement('h5'));
+ $h5.append("Manage Users");
+ var attributes = [ "table-striped", "table-bordered",
+ "table-hover", "table-cursor" ];
+ var $table = one.lib.dashlet.table.table(attributes);
+ var headers = [ "User", "Role" ];
+ var $thead = one.lib.dashlet.table.header(headers);
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($thead).append($tbody);
+
+ // bind table
+ if (one.role < 2) {
+ $table.find('tr').click(function() {
+ var id = $(this).data('id');
+ one.main.admin.remove.modal.initialize(id);
+ });
+ }
- // body inject
- one.main.admin.ajax.users(function($body) {
- one.lib.modal.inject.body($modal, $body);
- });
+ // append to div
+ $div.append($h5).append($table);
- // modal show callback
- callback($modal);
- },
- footer : function() {
- var footer = [];
- var closeButton = one.lib.dashlet.button.single('Close', one.main.admin.id.modal.close, '', '');
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
- return footer;
- }
+ if (one.role < 2) {
+ var addUserButton = one.lib.dashlet.button.single("Add User",
+ one.main.admin.id.add.user, "btn-primary", "btn-mini");
+ var $addUserButton = one.lib.dashlet.button
+ .button(addUserButton);
+ $div.append($addUserButton);
+
+ // add user binding
+ $addUserButton.click(function() {
+ one.main.admin.add.modal.initialize();
+ });
+ }
+
+ return $div;
+ }
+ },
+ remove : {
+ modal : {
+ initialize : function(id) {
+ var h3 = "Edit User";
+ var footer = one.main.admin.remove.footer();
+ var $body = one.main.admin.remove.body();
+ var $modal = one.lib.modal.spawn(one.main.admin.id.modal.user,
+ h3, $body, footer);
+ // close binding
+ $('#'+one.main.admin.id.modal.remove.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+ // remove binding
+ $('#' + one.main.admin.id.modal.remove.user, $modal).click(function() {
+ one.main.admin.remove.modal.ajax(id, function(result) {
+ if (result == 'Success') {
+ $modal.modal('hide');
+ // body inject
+ var $admin = $('#'+one.main.admin.id.modal.main);
+ one.main.admin.ajax.users(function($body) {
+ one.lib.modal.inject.body($admin, $body);
+ });
+ } else {
+ alert("Failed to remove user: " + result);
+ }
+ });
+ });
+ // change password binding
+ $('#' + one.main.admin.id.modal.remove.password, $modal).click(function() {
+ one.main.admin.password.initialize(id, function() {
+ $modal.modal('hide');
+ });
+ });
+ $modal.modal();
+ },
+ ajax : function(id, callback) {
+ $.post(one.main.admin.address.root + one.main.admin.address.users + '/' + id, function(data) {
+ callback(data);
+ });
+ },
},
- ajax : {
- users : function(callback) {
- $.getJSON(one.main.admin.address.root
- + one.main.admin.address.users, function(data) {
- var body = one.main.admin.data.users(data);
- var $body = one.main.admin.body.users(body);
- callback($body);
- });
- }
+ footer : function() {
+ var footer = [];
+ var removeButton = one.lib.dashlet.button.single("Remove User",
+ one.main.admin.id.modal.remove.user, "btn-danger", "");
+ var $removeButton = one.lib.dashlet.button.button(removeButton);
+ footer.push($removeButton);
+ var change = one.lib.dashlet.button.single('Change Password',
+ one.main.admin.id.modal.remove.password, 'btn-success', '');
+ var $change = one.lib.dashlet.button.button(change);
+ footer.push($change);
+ var closeButton = one.lib.dashlet.button.single("Close",
+ one.main.admin.id.modal.remove.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
+ return footer;
},
- data : {
- users : function(data) {
- var body = [];
- $(data).each(function(index, value) {
- var tr = {};
- var entry = [];
- entry.push(value['user']);
- entry.push(value['roles']);
- tr['entry'] = entry;
- tr['id'] = value['user'];
- body.push(tr);
- });
- return body;
- }
+ body : function() {
+ var $p = $(document.createElement('p'));
+ $p.append('Select an action');
+ return $p;
},
- body : {
- users : function(body) {
- var $div = $(document.createElement('div'));
- var $h5 = $(document.createElement('h5'));
- $h5.append("Manage Users");
- var attributes = [ "table-striped", "table-bordered",
- "table-hover", "table-cursor" ];
- var $table = one.lib.dashlet.table.table(attributes);
- var headers = [ "User", "Role" ];
- var $thead = one.lib.dashlet.table.header(headers);
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($thead).append($tbody);
-
- // bind table
- if (one.role < 2) {
- $table.find('tr').click(function() {
- var id = $(this).data('id');
- one.main.admin.remove.modal.initialize(id);
- });
+ },
+ add : {
+ modal : {
+ initialize : function() {
+ var h3 = "Add User";
+ var footer = one.main.admin.add.footer();
+ var $body = one.main.admin.add.body();
+ var $modal = one.lib.modal.spawn(one.main.admin.id.modal.user,
+ h3, $body, footer);
+ // close binding
+ $('#' + one.main.admin.id.modal.add.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+ // add binding
+ $('#' + one.main.admin.id.modal.add.user, $modal).click(function() {
+ one.main.admin.add.modal.add($modal, function(result) {
+ if (result == 'Success') {
+ $modal.modal('hide');
+ // body inject
+ var $admin = $('#'+one.main.admin.id.modal.main);
+ one.main.admin.ajax.users(function($body) {
+ one.lib.modal.inject.body($admin, $body);
+ });
+ } else {
+ alert("Failed to add user: "+result);
}
+ });
+ });
+ $modal.modal();
+ },
+ add : function($modal, callback) {
+ var user = {};
+ user['user'] = $modal.find(
+ '#' + one.main.admin.id.modal.add.form.name).val();
+ user['password'] = $modal.find(
+ '#' + one.main.admin.id.modal.add.form.password).val();
+ roles = new Array();
+ roles[0] = $modal.find(
+ '#' + one.main.admin.id.modal.add.form.role).find(
+ 'option:selected').attr('value');
+ user['roles'] = roles;
+
+ // password check
+ var verify = $('#'+one.main.admin.id.modal.add.form.verify).val();
+ if (user.password != verify) {
+ alert('Passwords do not match');
+ return false;
+ }
- // append to div
- $div.append($h5).append($table);
+ var resource = {};
+ resource['json'] = JSON.stringify(user);
+ resource['action'] = 'add'
- if (one.role < 2) {
- var addUserButton = one.lib.dashlet.button.single("Add User",
- one.main.admin.id.add.user, "btn-primary", "btn-mini");
- var $addUserButton = one.lib.dashlet.button
- .button(addUserButton);
- $div.append($addUserButton);
+ one.main.admin.add.modal.ajax(resource, callback);
+ },
+ ajax : function(data, callback) {
+ $.post(one.main.admin.address.root
+ + one.main.admin.address.users, data, function(data) {
+ callback(data);
+ });
+ }
+ },
+ body : function() {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ // user
+ var $label = one.lib.form.label('Username');
+ var $input = one.lib.form.input('Username');
+ $input.attr('id', one.main.admin.id.modal.add.form.name);
+ $fieldset.append($label).append($input);
+ // password
+ var $label = one.lib.form.label('Password');
+ var $input = one.lib.form.input('Password');
+ $input.attr('id', one.main.admin.id.modal.add.form.password);
+ $input.attr('type', 'password');
+ $fieldset.append($label).append($input);
+ // password verify
+ var $label = one.lib.form.label('Verify Password');
+ var $input = one.lib.form.input('Verify Password');
+ $input.attr('id', one.main.admin.id.modal.add.form.verify);
+ $input.attr('type', 'password');
+ $fieldset.append($label).append($input);
+ // roles
+ var $label = one.lib.form.label('Roles');
+ var options = {
+ "Network-Admin" : "Network Administrator",
+ "Network-Operator" : "Network Operator"
+ };
+ var $select = one.lib.form.select.create(options);
+ $select.attr('id', one.main.admin.id.modal.add.form.role);
+ $fieldset.append($label).append($select);
+ $form.append($fieldset);
+ return $form;
+ },
+ footer : function() {
+ var footer = [];
- // add user binding
- $addUserButton.click(function() {
- one.main.admin.add.modal.initialize();
- });
- }
+ var addButton = one.lib.dashlet.button.single("Add User",
+ one.main.admin.id.modal.add.user, "btn-primary", "");
+ var $addButton = one.lib.dashlet.button.button(addButton);
+ footer.push($addButton);
- return $div;
- }
+ var closeButton = one.lib.dashlet.button.single("Close",
+ one.main.admin.id.modal.add.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
+
+ return footer;
+ }
+ },
+ password : {
+ initialize : function(id, successCallback) {
+ var h3 = 'Change Password';
+ var footer = one.main.admin.password.footer();
+ var $body = one.main.admin.password.body(id);;
+ var $modal = one.lib.modal.spawn(one.main.admin.id.modal.password.modal,
+ h3, $body, footer);
+
+ // cancel binding
+ $('#'+one.main.admin.id.modal.password.cancel, $modal).click(function() {
+ $modal.modal('hide');
+ });
+
+ // change password binding
+ $('#'+one.main.admin.id.modal.password.submit, $modal).click(function() {
+ one.main.admin.password.submit(id, $modal, function(result) {
+ if (result.code == 'SUCCESS') {
+ $modal.modal('hide');
+ successCallback();
+ } else {
+ alert(result.code+': '+result.description);
+ }
+ });
+ });
+
+ $modal.modal();
},
- remove : {
- modal : {
- initialize : function(id) {
- var h3 = "Edit User";
- var footer = one.main.admin.remove.footer();
- var $body = one.main.admin.remove.body();
- var $modal = one.lib.modal.spawn(one.main.admin.id.modal.user,
- h3, $body, footer);
- // close binding
- $('#'+one.main.admin.id.modal.remove.close, $modal).click(function() {
- $modal.modal('hide');
- });
- // remove binding
- $('#' + one.main.admin.id.modal.remove.user, $modal).click(function() {
- one.main.admin.remove.modal.ajax(id, function(result) {
- if (result == 'Success') {
- $modal.modal('hide');
- // body inject
- var $admin = $('#'+one.main.admin.id.modal.main);
- one.main.admin.ajax.users(function($body) {
- one.lib.modal.inject.body($admin, $body);
- });
- } else {
- alert("Failed to remove user: " + result);
- }
- });
- });
- // change password binding
- $('#' + one.main.admin.id.modal.remove.password, $modal).click(function() {
- one.main.admin.password.initialize(id, function() {
- $modal.modal('hide');
- });
- });
- $modal.modal();
- },
- ajax : function(id, callback) {
- $.post(one.main.admin.address.root + one.main.admin.address.users + '/' + id, function(data) {
- callback(data);
- });
- },
- },
- footer : function() {
- var footer = [];
- var removeButton = one.lib.dashlet.button.single("Remove User",
- one.main.admin.id.modal.remove.user, "btn-danger", "");
- var $removeButton = one.lib.dashlet.button.button(removeButton);
- footer.push($removeButton);
- var change = one.lib.dashlet.button.single('Change Password',
- one.main.admin.id.modal.remove.password, 'btn-success', '');
- var $change = one.lib.dashlet.button.button(change);
- footer.push($change);
- var closeButton = one.lib.dashlet.button.single("Close",
- one.main.admin.id.modal.remove.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
- return footer;
- },
- body : function() {
- var $p = $(document.createElement('p'));
- $p.append('Select an action');
- return $p;
- },
+ submit : function(id, $modal, callback) {
+ var resource = {};
+ resource.newPassword = $('#'+one.main.admin.id.modal.password.form.set, $modal).val();
+
+ // verify password
+ var verify = $('#'+one.main.admin.id.modal.password.form.verify, $modal).val();
+ if (verify != resource.newPassword) {
+ alert('Passwords do not match');
+ return false;
+ }
+
+ resource.currentPassword = $('#'+one.main.admin.id.modal.password.form.old, $modal).val();
+
+ $.post(one.main.admin.address.password+id, resource, function(data) {
+ callback(data);
+ });
},
- add : {
- modal : {
- initialize : function() {
- var h3 = "Add User";
- var footer = one.main.admin.add.footer();
- var $body = one.main.admin.add.body();
- var $modal = one.lib.modal.spawn(one.main.admin.id.modal.user,
- h3, $body, footer);
- // close binding
- $('#' + one.main.admin.id.modal.add.close, $modal).click(function() {
- $modal.modal('hide');
- });
- // add binding
- $('#' + one.main.admin.id.modal.add.user, $modal).click(function() {
- one.main.admin.add.modal.add($modal, function(result) {
- if (result == 'Success') {
- $modal.modal('hide');
- // body inject
- var $admin = $('#'+one.main.admin.id.modal.main);
- one.main.admin.ajax.users(function($body) {
- one.lib.modal.inject.body($admin, $body);
- });
- } else {
- alert("Failed to add user: "+result);
- }
- });
- });
- $modal.modal();
- },
- add : function($modal, callback) {
- var user = {};
- user['user'] = $modal.find(
- '#' + one.main.admin.id.modal.add.form.name).val();
- user['password'] = $modal.find(
- '#' + one.main.admin.id.modal.add.form.password).val();
- roles = new Array();
- roles[0] = $modal.find(
- '#' + one.main.admin.id.modal.add.form.role).find(
- 'option:selected').attr('value');
- user['roles'] = roles;
-
- // password check
- var verify = $('#'+one.main.admin.id.modal.add.form.verify).val();
- if (user.password != verify) {
- alert('Passwords do not match');
- return false;
- }
-
- var resource = {};
- resource['json'] = JSON.stringify(user);
- resource['action'] = 'add'
-
- one.main.admin.add.modal.ajax(resource, callback);
- },
- ajax : function(data, callback) {
- $.post(one.main.admin.address.root
- + one.main.admin.address.users, data, function(data) {
- callback(data);
- });
- }
- },
- body : function() {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- // user
- var $label = one.lib.form.label('Username');
- var $input = one.lib.form.input('Username');
- $input.attr('id', one.main.admin.id.modal.add.form.name);
- $fieldset.append($label).append($input);
- // password
- var $label = one.lib.form.label('Password');
- var $input = one.lib.form.input('Password');
- $input.attr('id', one.main.admin.id.modal.add.form.password);
- $input.attr('type', 'password');
- $fieldset.append($label).append($input);
- // password verify
- var $label = one.lib.form.label('Verify Password');
- var $input = one.lib.form.input('Verify Password');
- $input.attr('id', one.main.admin.id.modal.add.form.verify);
- $input.attr('type', 'password');
- $fieldset.append($label).append($input);
- // roles
- var $label = one.lib.form.label('Roles');
- var options = {
- "Network-Admin" : "Network Administrator",
- "Network-Operator" : "Network Operator"
- };
- var $select = one.lib.form.select.create(options);
- $select.attr('id', one.main.admin.id.modal.add.form.role);
- $fieldset.append($label).append($select);
- $form.append($fieldset);
- return $form;
- },
- footer : function() {
- var footer = [];
-
- var addButton = one.lib.dashlet.button.single("Add User",
- one.main.admin.id.modal.add.user, "btn-primary", "");
- var $addButton = one.lib.dashlet.button.button(addButton);
- footer.push($addButton);
-
- var closeButton = one.lib.dashlet.button.single("Close",
- one.main.admin.id.modal.add.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
-
- return footer;
- }
+ body : function(id) {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ // user
+ var $label = one.lib.form.label('Username');
+ var $input = one.lib.form.input('');
+ $input.attr('disabled', 'disabled');
+ $input.val(id);
+ $fieldset.append($label)
+ .append($input);
+ // old password
+ var $label = one.lib.form.label('Old Password');
+ var $input = one.lib.form.input('Old Password');
+ $input.attr('id', one.main.admin.id.modal.password.form.old);
+ $input.attr('type', 'password');
+ $fieldset.append($label).append($input);
+ // new password
+ var $label = one.lib.form.label('New Password');
+ var $input = one.lib.form.input('New Password');
+ $input.attr('id', one.main.admin.id.modal.password.form.set);
+ $input.attr('type', 'password');
+ $fieldset.append($label).append($input);
+ // verify new password
+ var $label = one.lib.form.label('Verify Password');
+ var $input = one.lib.form.input('Verify Password');
+ $input.attr('id', one.main.admin.id.modal.password.form.verify);
+ $input.attr('type', 'password');
+ $fieldset.append($label).append($input);
+ // return
+ $form.append($fieldset);
+ return $form;
},
- password : {
- initialize : function(id, successCallback) {
- var h3 = 'Change Password';
- var footer = one.main.admin.password.footer();
- var $body = one.main.admin.password.body(id);;
- var $modal = one.lib.modal.spawn(one.main.admin.id.modal.password.modal,
- h3, $body, footer);
-
- // cancel binding
- $('#'+one.main.admin.id.modal.password.cancel, $modal).click(function() {
- $modal.modal('hide');
- });
-
- // change password binding
- $('#'+one.main.admin.id.modal.password.submit, $modal).click(function() {
- one.main.admin.password.submit(id, $modal, function(result) {
- if (result.code == 'SUCCESS') {
- $modal.modal('hide');
- successCallback();
- } else {
- alert(result.code+': '+result.description);
- }
- });
- });
-
- $modal.modal();
- },
- submit : function(id, $modal, callback) {
- var resource = {};
- resource.newPassword = $('#'+one.main.admin.id.modal.password.form.set, $modal).val();
-
- // verify password
- var verify = $('#'+one.main.admin.id.modal.password.form.verify, $modal).val();
- if (verify != resource.newPassword) {
- alert('Passwords do not match');
- return false;
- }
-
- resource.currentPassword = $('#'+one.main.admin.id.modal.password.form.old, $modal).val();
-
- $.post(one.main.admin.address.password+id, resource, function(data) {
- callback(data);
- });
- },
- body : function(id) {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- // user
- var $label = one.lib.form.label('Username');
- var $input = one.lib.form.input('');
- $input.attr('disabled', 'disabled');
- $input.val(id);
- $fieldset.append($label)
- .append($input);
- // old password
- var $label = one.lib.form.label('Old Password');
- var $input = one.lib.form.input('Old Password');
- $input.attr('id', one.main.admin.id.modal.password.form.old);
- $input.attr('type', 'password');
- $fieldset.append($label).append($input);
- // new password
- var $label = one.lib.form.label('New Password');
- var $input = one.lib.form.input('New Password');
- $input.attr('id', one.main.admin.id.modal.password.form.set);
- $input.attr('type', 'password');
- $fieldset.append($label).append($input);
- // verify new password
- var $label = one.lib.form.label('Verify Password');
- var $input = one.lib.form.input('Verify Password');
- $input.attr('id', one.main.admin.id.modal.password.form.verify);
- $input.attr('type', 'password');
- $fieldset.append($label).append($input);
- // return
- $form.append($fieldset);
- return $form;
- },
- footer : function() {
- var footer = [];
- var submit = one.lib.dashlet.button.single('Submit',
- one.main.admin.id.modal.password.submit, 'btn-primary', '');
- var $submit = one.lib.dashlet.button.button(submit);
- footer.push($submit);
- var cancel = one.lib.dashlet.button.single('Cancel',
- one.main.admin.id.modal.password.cancel, '', '');
- var $cancel = one.lib.dashlet.button.button(cancel);
- footer.push($cancel);
- return footer;
- }
- }
+ footer : function() {
+ var footer = [];
+ var submit = one.lib.dashlet.button.single('Submit',
+ one.main.admin.id.modal.password.submit, 'btn-primary', '');
+ var $submit = one.lib.dashlet.button.button(submit);
+ footer.push($submit);
+ var cancel = one.lib.dashlet.button.single('Cancel',
+ one.main.admin.id.modal.password.cancel, '', '');
+ var $cancel = one.lib.dashlet.button.button(cancel);
+ footer.push($cancel);
+ return footer;
+ }
+ }
}
one.main.cluster = {
- id : { // one.main.cluster.id
- modal : 'one-main-cluster-id-modal',
- close : 'one-main-cluster-id-close',
- datagrid : 'one-main-cluster-id-datagrid'
- },
- initialize : function() {
- var h3 = 'Cluster Management';
- var footer = one.main.cluster.footer();
- var $body = '';
- var $modal = one.lib.modal.spawn(one.main.cluster.id.modal, h3, $body, footer);
-
- // close
- $('#'+one.main.cluster.id.close, $modal).click(function() {
- $modal.modal('hide');
- });
-
- // body
- $.getJSON('/admin/cluster', function(data) {
- var $gridHTML = one.lib.dashlet.datagrid.init(one.main.cluster.id.datagrid, {
- searchable: true,
- filterable: false,
- pagination: true,
- flexibleRowsPerPage: true
- }, 'table-striped table-condensed table-cursor');
- var source = one.main.cluster.data(data);
- $gridHTML.datagrid({dataSource : source}).on('loaded', function() {
- $(this).find('tbody tr').click(function() {
- var $tr = $(this);
- if ($tr.find('td:nth-child(1)').attr('colspan') === '1') {
- return false;
- }
- var address = $tr.find('.ux-id').text();
- one.main.cluster.nodes.initialize(address);
- });
- });
- one.lib.modal.inject.body($modal, $gridHTML);
- });
-
- $modal.modal();
- },
- data : function(data) {
- var tdata = [];
- var registry = [];
- $(data).each(function(idx, val) {
- var name = val.name;
- var address = val.address;
- var $registry = $(document.createElement('span'));
- $registry
- .append(JSON.stringify(address))
- .css('display', 'none')
- .addClass('ux-id');
- name = one.lib.dashlet.label(name, null)[0].outerHTML;
- name += $registry[0].outerHTML;
- if (val.me === true) {
- var me = one.lib.dashlet.label('*', 'label-inverse')[0].outerHTML;
- name += ' '+me;
- }
- if (val.coordinator === true) {
- var coord = one.lib.dashlet.label('C')[0].outerHTML;
- name += ' '+coord;
- }
- tdata.push({
- 'controller' : name
- });
- });
- var source = new StaticDataSource({
- columns : [
- {
- property : 'controller',
- label : 'Controller',
- sortable : true
- }
- ],
- data : tdata,
- delay : 0
- });
- return source;
- },
- footer : function() {
- var footer = [];
- var close = one.lib.dashlet.button.single('Close', one.main.cluster.id.close, '', '');
- var $close = one.lib.dashlet.button.button(close);
- footer.push($close);
- return footer;
- }
+ id : { // one.main.cluster.id
+ modal : 'one-main-cluster-id-modal',
+ close : 'one-main-cluster-id-close',
+ datagrid : 'one-main-cluster-id-datagrid'
+ },
+ initialize : function() {
+ var h3 = 'Cluster Management';
+ var footer = one.main.cluster.footer();
+ var $body = '';
+ var $modal = one.lib.modal.spawn(one.main.cluster.id.modal, h3, $body, footer);
+
+ // close
+ $('#'+one.main.cluster.id.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+
+ // body
+ $.getJSON('/admin/cluster', function(data) {
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.main.cluster.id.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, 'table-striped table-condensed table-cursor');
+ var source = one.main.cluster.data(data);
+ $gridHTML.datagrid({dataSource : source}).on('loaded', function() {
+ $(this).find('tbody tr').click(function() {
+ var $tr = $(this);
+ if ($tr.find('td:nth-child(1)').attr('colspan') === '1') {
+ return false;
+ }
+ var address = $tr.find('.ux-id').text();
+ one.main.cluster.nodes.initialize(address);
+ });
+ });
+ one.lib.modal.inject.body($modal, $gridHTML);
+ });
+
+ $modal.modal();
+ },
+ data : function(data) {
+ var tdata = [];
+ var registry = [];
+ $(data).each(function(idx, controller) {
+ var name = controller.name;
+ var address = controller.address;
+ var $registry = $(document.createElement('span'));
+ $registry
+ .append(JSON.stringify(address))
+ .css('display', 'none')
+ .addClass('ux-id');
+ name = one.lib.dashlet.label(name, null)[0].outerHTML;
+ name += $registry[0].outerHTML;
+ if (controller.me === true) {
+ var me = one.lib.dashlet.label('*', 'label-inverse')[0].outerHTML;
+ name += ' '+me;
+ }
+ if (controller.coordinator === true) {
+ var coord = one.lib.dashlet.label('C')[0].outerHTML;
+ name += ' '+coord;
+ }
+ tdata.push({
+ 'controller' : name,
+ 'numNodes' : controller.numConnectedNodes
+ });
+ });
+ var source = new StaticDataSource({
+ columns : [
+ {
+ property : 'controller',
+ label : 'Controller',
+ sortable : true
+ },
+ {
+ property : 'numNodes',
+ label : 'Nodes',
+ sortable : true
+ }
+ ],
+ data : tdata,
+ delay : 0
+ });
+ return source;
+ },
+ footer : function() {
+ var footer = [];
+ var close = one.lib.dashlet.button.single('Close', one.main.cluster.id.close, '', '');
+ var $close = one.lib.dashlet.button.button(close);
+ footer.push($close);
+ return footer;
+ }
}
one.main.cluster.nodes = {
- id : { // one.main.cluster.nodes.id
- modal : 'one-main-cluster-nodes-id-modal',
- close : 'one-main-cluster-nodes-id-close',
- datagrid : 'one-main-cluser-nodes-id-datagrid'
- },
- initialize : function(address) { // one.main.cluster.nodes.initialize
- var h3 = 'Connected Nodes';
- var footer = one.main.cluster.nodes.footer();
- var $body = '';
- var $modal = one.lib.modal.spawn(one.main.cluster.nodes.id.modal, h3, $body, footer);
-
- // close
- $('#'+one.main.cluster.nodes.id.close, $modal).click(function() {
- $modal.modal('hide');
- });
-
- // body
- $.getJSON('/admin/cluster/controller/'+address, function(data) {
- var $gridHTML = one.lib.dashlet.datagrid.init(one.main.cluster.nodes.id.datagrid, {
- searchable: true,
- filterable: false,
- pagination: true,
- flexibleRowsPerPage: true
- }, 'table-striped table-condensed');
- var source = one.main.cluster.nodes.data(data);
- $gridHTML.datagrid({dataSource : source});
- one.lib.modal.inject.body($modal, $gridHTML);
- });
-
- $modal.modal();
- },
- data : function(data) {
- var tdata = [];
- $(data).each(function(idx, val) {
- tdata.push({
- 'node' : val.description
- });
- });
- var source = new StaticDataSource({
- columns : [
- {
- property : 'node',
- label : 'Node',
- sortable : true
- }
- ],
- data : tdata,
- delay : 0
- });
- return source;
- },
- footer : function() { // one.main.cluster.nodes.footer
- var footer = [];
- var close = one.lib.dashlet.button.single('Close', one.main.cluster.nodes.id.close, '', '');
- var $close = one.lib.dashlet.button.button(close);
- footer.push($close);
- return footer;
- }
+ id : { // one.main.cluster.nodes.id
+ modal : 'one-main-cluster-nodes-id-modal',
+ close : 'one-main-cluster-nodes-id-close',
+ datagrid : 'one-main-cluser-nodes-id-datagrid'
+ },
+ initialize : function(address) { // one.main.cluster.nodes.initialize
+ var h3 = 'Connected Nodes';
+ var footer = one.main.cluster.nodes.footer();
+ var $body = '';
+ var $modal = one.lib.modal.spawn(one.main.cluster.nodes.id.modal, h3, $body, footer);
+
+ // close
+ $('#'+one.main.cluster.nodes.id.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+
+ // body
+ $.getJSON('/admin/cluster/controller/'+address, function(data) {
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.main.cluster.nodes.id.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, 'table-striped table-condensed');
+ var source = one.main.cluster.nodes.data(data);
+ $gridHTML.datagrid({dataSource : source});
+ one.lib.modal.inject.body($modal, $gridHTML);
+ });
+
+ $modal.modal();
+ },
+ data : function(data) {
+ var tdata = [];
+ $(data).each(function(idx, val) {
+ tdata.push({
+ 'node' : val.description
+ });
+ });
+ var source = new StaticDataSource({
+ columns : [
+ {
+ property : 'node',
+ label : 'Node',
+ sortable : true
+ }
+ ],
+ data : tdata,
+ delay : 0
+ });
+ return source;
+ },
+ footer : function() { // one.main.cluster.nodes.footer
+ var footer = [];
+ var close = one.lib.dashlet.button.single('Close', one.main.cluster.nodes.id.close, '', '');
+ var $close = one.lib.dashlet.button.button(close);
+ footer.push($close);
+ return footer;
+ }
}
one.main.dashlet = {
- left : {
- top : $("#left-top .dashlet"),
- bottom : $("#left-bottom .dashlet")
- },
- right : {
- bottom : $("#right-bottom .dashlet")
- }
+ left : {
+ top : $("#left-top .dashlet"),
+ bottom : $("#left-bottom .dashlet")
+ },
+ right : {
+ bottom : $("#right-bottom .dashlet")
+ }
}
/** BOOTSTRAP */
$(".modal").on('hidden', function() {
- $(this).remove();
+ $(this).remove();
});
$("#alert .close").click(function() {
- $("#alert").hide();
+ $("#alert").hide();
});
/** INIT */
// user admin
$("#admin").click(function() {
- one.main.admin.modal.initialize(function($modal) {
- $modal.modal();
- });
+ one.main.admin.modal.initialize(function($modal) {
+ $modal.modal();
+ });
});
// cluster
$('#cluster').click(function() {
- one.main.cluster.initialize();
+ one.main.cluster.initialize();
});
// save
$("#save").click(function() {
- $.post(one.main.constants.address.save, function(data) {
- if (data == "Success") {
- one.lib.alert("Configuration Saved");
- } else {
- one.lib.alert("Unable to save configuration: " + data);
- }
- });
+ $.post(one.main.constants.address.save, function(data) {
+ if (data == "Success") {
+ one.lib.alert("Configuration Saved");
+ } else {
+ one.lib.alert("Unable to save configuration: " + data);
+ }
+ });
});
// logout
$("#logout").click(function() {
- location.href = "/logout";
+ location.href = "/logout";
});
$.ajaxSetup({
- complete : function(xhr, textStatus) {
- var mime = xhr.getResponseHeader('Content-Type');
- if (mime.substring(0, 9) == 'text/html') {
- location.href = '/';
- }
+ complete : function(xhr, textStatus) {
+ var mime = xhr.getResponseHeader('Content-Type');
+ if (mime.substring(0, 9) == 'text/html') {
+ location.href = '/';
}
+ }
});
/** MAIN PAGE LOAD */
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.4.0-SNAPSHOT</version>
+ <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>