From: Giovanni Meo Date: Thu, 12 Sep 2013 14:41:27 +0000 (+0000) Subject: Merge "Declare a property for commons-lang version in poms and use it." X-Git-Tag: releasepom-0.1.0~103 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=d059b1faef43031951d426048974fa9b32dceba3;hp=ed55006f54e710851f766cf9640175dfc5df5725 Merge "Declare a property for commons-lang version in poms and use it." --- diff --git a/opendaylight/appauth/pom.xml b/opendaylight/appauth/pom.xml new file mode 100644 index 0000000000..71aa49f0f2 --- /dev/null +++ b/opendaylight/appauth/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.0-SNAPSHOT + ../commons/opendaylight + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main + + appauth + 0.4.0-SNAPSHOT + bundle + + + + + org.apache.felix + maven-bundle-plugin + 2.3.6 + true + + + + 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 + + + org.opendaylight.controller.appauth, + org.opendaylight.controller.appauth.authorization + + + + + + + + + + + + org.opendaylight.controller + sal + 0.5.0-SNAPSHOT + + + org.opendaylight.controller + usermanager + 0.4.0-SNAPSHOT + + + junit + junit + 4.8.1 + test + + + diff --git a/opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/Application.java b/opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/Application.java new file mode 100644 index 0000000000..84a2e919f7 --- /dev/null +++ b/opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/Application.java @@ -0,0 +1,20 @@ + +/* + * 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 extends Authorization { + + public Application() { + super(); + } + +} diff --git a/opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/authorization/Authorization.java b/opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/authorization/Authorization.java new file mode 100644 index 0000000000..acfc7252ef --- /dev/null +++ b/opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/authorization/Authorization.java @@ -0,0 +1,614 @@ + +/* + * 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 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> resourceGroups; + /* + * The configured roles along with their level + */ + protected ConcurrentMap roles; + /* + * The association of groups to roles + */ + protected ConcurrentMap> 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()); + 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 getRoles() { + return new ArrayList(groupsAuthorizations.keySet()); + } + + @SuppressWarnings("unchecked") + @Override + public Status createResourceGroup(String groupName, List 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 toBeAdded = new HashSet(); + 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 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 affectedRoles = new ArrayList(); + Status result; + for (Entry> pairs : groupsAuthorizations + .entrySet()) { + String role = pairs.getKey(); + Set 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 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 getAllResourcesforUser(String userName) { + Set resources = new HashSet(); + IUserManager userManager = (IUserManager) ServiceHelper + .getGlobalInstance(IUserManager.class, this); + if (userManager == null) { + return new HashSet(0); + } + + // Get the roles associated with this user + List roles = userManager.getUserRoles(userName); + if (roles == null) { + return resources; + } + + for (String role : roles) { + // Get our resource groups associated with this role + List groups = this.getAuthorizedGroups(role); + if (groups.isEmpty()) { + continue; + } + for (ResourceGroup group : groups) { + // Get the list of resources in this group + List 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 getAuthorizedResources(String role) { + Set resources = new HashSet(); + + // Get our resource groups associated with this role + List groups = this.getAuthorizedGroups(role); + if (groups.isEmpty()) { + return new ArrayList(0); + } + for (ResourceGroup group : groups) { + // Get the list of resources in this group + List 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(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 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 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 hisResources = getAllResourcesforUser(userName); + for (Resource element : hisResources) { + if (element.getResource().equals(resource)) { + return element.getPrivilege(); + } + } + + return Privilege.NONE; + } + + @Override + public List getResourceGroups() { + return new ArrayList(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 getAuthorizedGroups(String role) { + return (groupsAuthorizations.containsKey(role)) ? new ArrayList( + groupsAuthorizations.get(role)) + : new ArrayList(); + } + + @Override + public List getResources(String groupName) { + return (resourceGroups.containsKey(groupName)) ? new ArrayList( + resourceGroups.get(groupName)) : new ArrayList(); + } + + @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 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 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); + } +} diff --git a/opendaylight/clustering/integrationtest/pom.xml b/opendaylight/clustering/integrationtest/pom.xml index 50d6f5c7d5..0fadb12890 100644 --- a/opendaylight/clustering/integrationtest/pom.xml +++ b/opendaylight/clustering/integrationtest/pom.xml @@ -40,12 +40,12 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller - containermanager.implementation - 0.4.0-SNAPSHOT + containermanager.it.implementation + 0.5.0-SNAPSHOT diff --git a/opendaylight/clustering/integrationtest/src/test/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusteringServicesIT.java b/opendaylight/clustering/integrationtest/src/test/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusteringServicesIT.java index 8cbebe5134..5c8b096b3e 100644 --- a/opendaylight/clustering/integrationtest/src/test/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusteringServicesIT.java +++ b/opendaylight/clustering/integrationtest/src/test/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusteringServicesIT.java @@ -103,7 +103,7 @@ public class ClusteringServicesIT { "sal.implementation").versionAsInProject(), mavenBundle("org.opendaylight.controller", "containermanager").versionAsInProject(), mavenBundle("org.opendaylight.controller", - "containermanager.implementation").versionAsInProject(), + "containermanager.it.implementation").versionAsInProject(), mavenBundle("org.jboss.spec.javax.transaction", "jboss-transaction-api_1.1_spec").versionAsInProject(), mavenBundle("org.apache.commons", "commons-lang3").versionAsInProject(), diff --git a/opendaylight/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/ICoordinatorChangeAware.java b/opendaylight/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/ICoordinatorChangeAware.java index 4a92aca604..5d0edd8d00 100644 --- a/opendaylight/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/ICoordinatorChangeAware.java +++ b/opendaylight/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/ICoordinatorChangeAware.java @@ -28,5 +28,5 @@ public interface ICoordinatorChangeAware { * Function that will be called when there is the event of * coordinator change in the cluster. */ - public void coordinatorChanged(); + void coordinatorChanged(); } diff --git a/opendaylight/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/IListenRoleChange.java b/opendaylight/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/IListenRoleChange.java index e5c7a8839e..b8798e6705 100644 --- a/opendaylight/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/IListenRoleChange.java +++ b/opendaylight/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/IListenRoleChange.java @@ -37,5 +37,5 @@ public interface IListenRoleChange { * active-standby milestone is reached, after will be removed. * */ - public void newActiveAvailable(); + void newActiveAvailable(); } diff --git a/opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClassResolver.java b/opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClassResolver.java new file mode 100644 index 0000000000..521a773966 --- /dev/null +++ b/opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClassResolver.java @@ -0,0 +1,46 @@ +/* + * 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 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(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; + } +} diff --git a/opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusterManager.java b/opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusterManager.java index f5c655a4ea..cd3a295791 100644 --- a/opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusterManager.java +++ b/opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusterManager.java @@ -33,6 +33,9 @@ import javax.transaction.TransactionManager; 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; @@ -247,8 +250,14 @@ public class ClusterManager implements IClusterServices, IContainerAware { } 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(); diff --git a/opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationAwareCommon.java b/opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationAwareCommon.java index 57a648bf69..f06dcb3296 100644 --- a/opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationAwareCommon.java +++ b/opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationAwareCommon.java @@ -22,5 +22,5 @@ public interface IConfigurationAwareCommon { /** * Trigger from configuration component to persist the configuration state. */ - public Status saveConfiguration(); + Status saveConfiguration(); } diff --git a/opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationServiceCommon.java b/opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationServiceCommon.java index fc9c5ac977..c76e377026 100644 --- a/opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationServiceCommon.java +++ b/opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationServiceCommon.java @@ -17,5 +17,5 @@ import org.opendaylight.controller.sal.utils.Status; * */ public interface IConfigurationServiceCommon { - public Status saveConfigurations(); + Status saveConfigurations(); } diff --git a/opendaylight/configuration/integrationtest/pom.xml b/opendaylight/configuration/integrationtest/pom.xml index 51ff2a6703..19a1e66422 100644 --- a/opendaylight/configuration/integrationtest/pom.xml +++ b/opendaylight/configuration/integrationtest/pom.xml @@ -56,12 +56,12 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller - containermanager.implementation - 0.4.0-SNAPSHOT + containermanager.it.implementation + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/configuration/integrationtest/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationIT.java b/opendaylight/configuration/integrationtest/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationIT.java index a2f2297325..b0e351ae16 100644 --- a/opendaylight/configuration/integrationtest/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationIT.java +++ b/opendaylight/configuration/integrationtest/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationIT.java @@ -73,7 +73,7 @@ public class ConfigurationIT { 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(), diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java index 9cbddbb130..7097958126 100644 --- a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java +++ b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java @@ -352,16 +352,21 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene String controller = ci.nextArgument(); if (controller == null) { ci.println("Nodes connected to this controller : "); - if (this.getLocalNodes() == null) ci.println("None"); - else ci.println(this.getLocalNodes().toString()); + if (this.getLocalNodes() == null) { + ci.println("None"); + } else { + ci.println(this.getLocalNodes().toString()); + } return; } try { InetAddress address = InetAddress.getByName(controller); ci.println("Nodes connected to controller "+controller); - if (this.getNodes(address) == null) ci.println("None"); - else ci.println(this.getNodes(address).toString()); - return; + if (this.getNodes(address) == null) { + ci.println("None"); + } else { + ci.println(this.getNodes(address).toString()); + } } catch (UnknownHostException e) { logger.error("An error occured",e); } diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java index f4c7bd2ff7..06b72219f7 100644 --- a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java +++ b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java @@ -10,8 +10,6 @@ import java.util.Set; 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; @@ -128,7 +126,6 @@ public abstract class AbstractScheme { return controllerNodesMap.get(controller); } - @SuppressWarnings("deprecation") public Set getNodes() { return getNodes(clusterServices.getMyAddress()); } @@ -142,7 +139,6 @@ public abstract class AbstractScheme { return nodeConnections; } - @SuppressWarnings("deprecation") public boolean isLocal(Node node) { if (nodeConnections == null) return false; InetAddress myController = clusterServices.getMyAddress(); @@ -150,7 +146,6 @@ public abstract class AbstractScheme { return (controllers != null && controllers.contains(myController)); } - @SuppressWarnings("deprecation") public Status removeNode (Node node) { return removeNodeFromController(node, clusterServices.getMyAddress()); } diff --git a/opendaylight/containermanager/api/pom.xml b/opendaylight/containermanager/api/pom.xml index 65adf68642..2b207aa797 100644 --- a/opendaylight/containermanager/api/pom.xml +++ b/opendaylight/containermanager/api/pom.xml @@ -15,7 +15,7 @@ containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT bundle @@ -28,14 +28,18 @@ - 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 org.opendaylight.controller.containermanager diff --git a/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerChangeEvent.java b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerChangeEvent.java new file mode 100644 index 0000000000..7e040c7852 --- /dev/null +++ b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerChangeEvent.java @@ -0,0 +1,33 @@ + +/* + * 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; + } +} diff --git a/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerConfig.java b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerConfig.java new file mode 100644 index 0000000000..01715cc1bf --- /dev/null +++ b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerConfig.java @@ -0,0 +1,628 @@ + +/* + * 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 ports; + + @XmlElement(name = "flowSpecs") + private List 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 getContainerFlows() { + return containerFlows; + } + + public void setContainerFlows(List containerFlows) { + this.containerFlows = containerFlows; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + public static String getRegexname() { + return regexName; + } + + public void setPorts(List 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(0); + this.containerFlows = new ArrayList(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 portList, List containerFlows) { + this.container = container; + this.staticVlan = vlan; + this.ports = (portList == null) ? new ArrayList(0) : new ArrayList(portList); + this.containerFlows = (containerFlows == null) ? new ArrayList(0) + : new ArrayList(containerFlows); + } + + public ContainerConfig(ContainerConfig config) { + this.container = config.container; + this.staticVlan = config.staticVlan; + this.ports = (config.ports == null) ? new ArrayList(0) : new ArrayList(config.ports); + this.containerFlows = (config.containerFlows == null) ? new ArrayList(0) + : new ArrayList(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 getPorts() { + return new ArrayList(ports); + } + + /** + * Returns the list of container flows configured for this container + * + * @return + */ + public List getContainerFlowConfigs() { + return (containerFlows == null || containerFlows.isEmpty()) ? new ArrayList(0) + : new ArrayList(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 getPortList() { + List portList = new ArrayList(); + 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 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 nodeConnectorsFromString(List nodeConnectorStrings) { + List list = new ArrayList(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 ncList) { + // Syntax check + Status status = ContainerConfig.validateNodeConnectors(ncList); + if (!status.isSuccess()) { + return status; + } + + /* Allow adding ports which are already present + if (!ports.isEmpty()) { + List intersection = new ArrayList(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 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 extra = new ArrayList(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 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 existingNames = this.getContainerFlowConfigsNames(); + List 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 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 getContainerFlowConfigsNames(List confList) { + // Use set to check for duplicates later + Set namesSet = new HashSet(); + if (confList != null) { + for (ContainerFlowConfig conf : confList) { + namesSet.add(conf.getName()); + } + } + return new ArrayList(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 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 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 names) { + // Sanity check + if (names == null || names.isEmpty()) { + return new Status(StatusCode.BADREQUEST, "Invalid flow spec names list"); + } + // Validation check + List present = this.getContainerFlowConfigsNames(); + if (!present.containsAll(names)) { + List notPresent = new ArrayList(names); + notPresent.retainAll(present); + return new Status(StatusCode.BADREQUEST, "Following flow spec(s) are not present: " + notPresent); + } + // Remove + List toDelete = new ArrayList(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 getContainerFlowConfigs(Set names) { + List list = new ArrayList(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 getContainerFlowSpecs() { + List list = new ArrayList(); + if (containerFlows != null && !containerFlows.isEmpty()) { + for (ContainerFlowConfig flowSpec : containerFlows) { + for (Match match : flowSpec.getMatches()) { + list.add(new ContainerFlow(match)); + } + } + } + return list; + } +} diff --git a/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerData.java b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerData.java new file mode 100644 index 0000000000..5a7cdd2058 --- /dev/null +++ b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerData.java @@ -0,0 +1,241 @@ + +/* + * 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> swPorts; + private short staticVlan; + private List cFlowList; + + /** + * Default constructor + + * + * @return constructed ContainerData + */ + public ContainerData() { + name = null; + swPorts = new ConcurrentHashMap>(); + staticVlan = 0; + cFlowList = new ArrayList(); + } + + /** + * 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>(); + cFlowList = new ArrayList(); + 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 getPorts(Node switchId) { + Set 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> 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 portSet = swPorts.get(switchId); + if (portSet == null) { + portSet = new HashSet(); + 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 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 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 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 getNodeConnectors() { + Set set = new HashSet(); + for (Map.Entry> entry : swPorts.entrySet()) { + set.addAll(entry.getValue()); + } + return set; + } +} diff --git a/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowChangeEvent.java b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowChangeEvent.java new file mode 100644 index 0000000000..7405a72e7b --- /dev/null +++ b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowChangeEvent.java @@ -0,0 +1,39 @@ + +/* + * 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 configs; + private UpdateType updateType; + + public ContainerFlowChangeEvent(List configs, UpdateType updateType) { + this.configs = configs; + this.updateType = updateType; + } + + public List getConfigList() { + return configs; + } + + public UpdateType getUpdateType() { + return updateType; + } + + @Override + public String toString() { + return "ContainerFlowChangeEvent [configs: " + configs + " updateType: " + updateType + "]"; + } +} diff --git a/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowConfig.java b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowConfig.java new file mode 100644 index 0000000000..c0b0a65270 --- /dev/null +++ b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowConfig.java @@ -0,0 +1,665 @@ + +/* + * 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 thisMatch = this.getMatches(); + List 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 myMathes = this.getMatches(); + List 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 getMatches() { + List matches = new ArrayList(); + 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 + "}"; + } +} diff --git a/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/IContainerManager.java b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/IContainerManager.java index 9300921be9..5daaf00ece 100644 --- a/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/IContainerManager.java +++ b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/IContainerManager.java @@ -10,15 +10,170 @@ 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 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 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 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 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 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 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 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> 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 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 getContainerFlowNameList(String containerName); + /** * Returns true if there are any non-default Containers present. * @@ -32,14 +187,4 @@ public interface IContainerManager { * @return List of Container name strings. */ public List 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(); } diff --git a/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/NodeConnectorsChangeEvent.java b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/NodeConnectorsChangeEvent.java new file mode 100644 index 0000000000..5f992c76de --- /dev/null +++ b/opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/NodeConnectorsChangeEvent.java @@ -0,0 +1,43 @@ + +/* + * 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 ncList; + private UpdateType updateType; + + public NodeConnectorsChangeEvent(List ncList, UpdateType updateType) { + this.ncList = ncList; + this.updateType = updateType; + } + + public List getNodeConnectors() { + return ncList; + } + + public UpdateType getUpdateType() { + return updateType; + } + + @Override + public String toString() { + return "ContainerConnectorsChangeEvent [ncList: " + ncList + " updateType: " + updateType + "]"; + } +} diff --git a/opendaylight/containermanager/implementation/pom.xml b/opendaylight/containermanager/implementation/pom.xml index f6909ca61b..5af8e2265c 100644 --- a/opendaylight/containermanager/implementation/pom.xml +++ b/opendaylight/containermanager/implementation/pom.xml @@ -15,7 +15,7 @@ containermanager.implementation - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT bundle @@ -29,7 +29,11 @@ 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, @@ -37,6 +41,7 @@ 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, @@ -62,6 +67,21 @@ org.opendaylight.controller containermanager + 0.5.0-SNAPSHOT + + + org.opendaylight.controller + configuration + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + appauth + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + topologymanager 0.4.0-SNAPSHOT @@ -69,5 +89,10 @@ sal 0.5.0-SNAPSHOT + + org.opendaylight.controller + usermanager + 0.4.0-SNAPSHOT + diff --git a/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java b/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java index 9caa62072c..aab07afa9e 100644 --- a/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java +++ b/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java @@ -9,108 +9,141 @@ 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> props = new Hashtable>(); + Set propSet = new HashSet(); + 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) @@ -121,8 +154,8 @@ public class Activator extends ComponentActivatorAbstractBase { // 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)); } } diff --git a/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java b/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java index 15f939d39b..1dfab887af 100644 --- a/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java +++ b/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java @@ -13,21 +13,39 @@ * @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 @@ -48,21 +66,52 @@ public class ContainerImpl implements IContainer { @Override public List getContainerFlows() { - return null; + List list = new ArrayList(); + + 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 getNodeConnectors() { - return null; + Set set = new HashSet(); + + ContainerData d = this.iContainerInternal.getContainerData(this.containerName); + if (d != null) { + ConcurrentMap> m = d.getSwPorts(); + if (m != null) { + for (Map.Entry> entry : m.entrySet()) { + set.addAll(entry.getValue()); + } + } + } + return set; } @Override public Set getNodes() { - return null; + Set set = new HashSet(); + + ContainerData d = this.iContainerInternal.getContainerData(this.containerName); + if (d != null) { + ConcurrentMap> m = d.getSwPorts(); + if (m != null) { + set.addAll(m.keySet()); + } + } + return set; } } diff --git a/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java b/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java index d47aca8135..da301ba8c0 100644 --- a/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java +++ b/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java @@ -7,47 +7,118 @@ * 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 implements IContainerManager, IObjectReader, + CommandProvider, ICacheUpdateAware, 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 = (Set) Collections - .synchronizedSet(new HashSet()); - private Set iContainerListener = Collections + private ConcurrentMap containerConfigs; + private ConcurrentMap containerData; + private ConcurrentMap> nodeConnectorToContainers; + private ConcurrentMap> nodeToContainers; + private ConcurrentMap containerChangeEvents; + private final Set iContainerAware = Collections.synchronizedSet(new HashSet()); + private final Set iContainerListener = Collections .synchronizedSet(new HashSet()); 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> entry : nodeConnectorToContainers + .entrySet()) { + NodeConnector port = entry.getKey(); + for (String container : entry.getValue()) { + s.nodeConnectorUpdated(container, port, UpdateType.ADDED); + } + } + for (Map.Entry container : containerData.entrySet()) { + for (ContainerFlow cFlow : container.getValue().getContainerFlowSpecs()) { + s.containerFlowUpdated(container.getKey(), cFlow, cFlow, UpdateType.ADDED); + } + } } } @@ -60,13 +131,9 @@ public class ContainerManager implements IContainerManager { 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 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)); } } } @@ -90,37 +157,1445 @@ public class ContainerManager implements IContainerManager { } } + 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) clusterServices.getCache("containermgr.containerConfigs"); + + containerChangeEvents = (ConcurrentMap) clusterServices.getCache("containermgr.event.containerChange"); + + containerData = (ConcurrentMap) clusterServices.getCache("containermgr.containerData"); + + nodeConnectorToContainers = (ConcurrentMap>) clusterServices + .getCache("containermgr.nodeConnectorToContainers"); + + nodeToContainers = (ConcurrentMap>) clusterServices.getCache("containermgr.nodeToContainers"); + + resourceGroups = (ConcurrentMap>) clusterServices.getCache("containermgr.containerGroups"); + + groupsAuthorizations = (ConcurrentMap>) clusterServices + .getCache("containermgr.containerAuthorizations"); + + roles = (ConcurrentMap) clusterServices.getCache("containermgr.roles"); + + if (containerConfigs.size() > 0) { + for (Map.Entry 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 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 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>> it = nodeConnectorToContainers.entrySet().iterator(); + String containerName = container.getContainerName(); + for (; it.hasNext();) { + Entry> entry = it.next(); + final NodeConnector nc = entry.getKey(); + final CopyOnWriteArrayList 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> 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 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 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 - 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 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 value = nodeToContainers.get(node); + // Add node->containers mapping + if (value == null) { + value = new HashSet(); + 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 slist = nodeConnectorToContainers.get(port); + if (slist == null) { + slist = new CopyOnWriteArrayList(); + } + 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 thisContainerPorts = container.getNodeConnectors(); + // Go through all the installed containers + for (Map.Entry entry : containerData.entrySet()) { + if (containerName.equalsIgnoreCase(entry.getKey())) { + continue; + } + // Derive the common ports + Set 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 thisContainerPorts = container.getNodeConnectors(); + List proposed = new ArrayList(container.getContainerFlowSpecs()); + proposed.add(cFlow); + for (Map.Entry entry : containerData.entrySet()) { + if (containerName.equalsIgnoreCase(entry.getKey())) { + continue; + } + ContainerData otherContainer = entry.getValue(); + Set 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 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 portList, List flowSpecList) { + for (NodeConnector port : portList) { + List 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 oneFlowList, List 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 getContainerNames() { + public List getContainerConfigList() { + return new ArrayList(containerConfigs.values()); + } + + @Override + public ContainerConfig getContainerConfig(String containerName) { + ContainerConfig target = containerConfigs.get(containerName); + return (target == null) ? null : new ContainerConfig(target); + } + + @Override + public List getContainerNameList() { /* * Return container names as they were configured by user (case sensitive) * along with the default container */ List containerNameList = new ArrayList(); containerNameList.add(GlobalConstants.DEFAULT.toString()); + containerNameList.addAll(containerConfigs.keySet()); return containerNameList; } @Override - public boolean hasNonDefaultContainer() { - return false; + public Map> getContainerFlows() { + Map> flowSpecConfig = new HashMap>(); + for (Map.Entry entry : containerConfigs.entrySet()) { + List 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(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 allContainers = (resourceGroups.containsKey(allResourcesGroupName)) ? resourceGroups + .get(allResourcesGroupName) : new HashSet(); + 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 writeProfile = new HashSet(1); + Set readProfile = new HashSet(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 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 resources = new HashSet(1); + resources.add(containerName); + resourceGroups.put(groupName, resources); + Set adminGroups = new HashSet(1); + Set operatorGroups = new HashSet(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 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 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 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 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 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 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 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 nodeConnectors) { + return addRemoveContainerEntries(containerName, nodeConnectors, false); + } + + @Override + public Status removeContainerEntry(String containerName, List nodeConnectors) { + return addRemoveContainerEntries(containerName, nodeConnectors, true); + } + + @Override + public Status addContainerFlows(String containerName, List fSpecConf) { + return addRemoveContainerFlow(containerName, fSpecConf, false); + } + + @Override + public Status removeContainerFlows(String containerName, List fSpecConf) { + return addRemoveContainerFlow(containerName, fSpecConf, true); + } + + @Override + public Status removeContainerFlows(String containerName, Set 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 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 getContainerFlows(String containerName) { + ContainerConfig sc = containerConfigs.get(containerName); + return (sc == null) ? new ArrayList(0) : sc.getContainerFlowConfigs(); + } + + @Override + public List getContainerFlowNameList(String containerName) { + ContainerConfig sc = containerConfigs.get(containerName); + return (sc == null) ? new ArrayList(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 configMap = (ConcurrentMap) objReader.read(this, + containersFileName); + + if (configMap == null) { + return; + } + + for (Map.Entry configEntry : configMap.entrySet()) { + addContainer(configEntry.getValue()); + } + } + + public void _psc(CommandInterpreter ci) { + for (Map.Entry 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 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 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 ports = new ArrayList(); + for (long l = 1L; l < 10L; l++) { + ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString()); + } + List cFlowList = new ArrayList(); + 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 portList = new ArrayList(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 portList = new ArrayList(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 list = new ArrayList(); + 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 set = new HashSet(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 \n"); + help.append("\t removeContainer \n"); + help.append("\t addContainerEntry \n"); + help.append("\t removeContainerEntry \n"); + help.append("\t addContainerFlow \n"); + help.append("\t removeContainerFlow \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 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 "); + return; + } + ci.println("Resource Groups associated to role " + roleName + ":"); + List 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 "); + return; + } + ci.println("Resource associated to role " + roleName + ":"); + List 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 "); + return; + } + ci.println("Group " + groupName + " contains the following resources:"); + List 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 "); + 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 "); + return; + } + ci.println("User " + userName + " owns the following resources: "); + Set 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 getContainerNames() { + return getContainerNameList(); } + @Override + public boolean hasNonDefaultContainer() { + return !containerConfigs.keySet().isEmpty(); + } } diff --git a/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/IContainerInternal.java b/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/IContainerInternal.java new file mode 100644 index 0000000000..555f37a4a4 --- /dev/null +++ b/opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/IContainerInternal.java @@ -0,0 +1,33 @@ + +/* + * 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); +} diff --git a/opendaylight/containermanager/it.implementation/pom.xml b/opendaylight/containermanager/it.implementation/pom.xml new file mode 100644 index 0000000000..ba71dbfdfe --- /dev/null +++ b/opendaylight/containermanager/it.implementation/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.0-SNAPSHOT + ../../commons/opendaylight + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main + + + containermanager.it.implementation + 0.5.0-SNAPSHOT + bundle + + + + + org.apache.felix + maven-bundle-plugin + 2.3.6 + true + + + + 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 + + + + + org.opendaylight.controller.containermanager.internal.Activator + + + ${project.basedir}/META-INF + + + + + + + org.opendaylight.controller + clustering.services + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + containermanager + 0.5.0-SNAPSHOT + + + org.opendaylight.controller + sal + 0.5.0-SNAPSHOT + + + diff --git a/opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java b/opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java new file mode 100644 index 0000000000..9caa62072c --- /dev/null +++ b/opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java @@ -0,0 +1,129 @@ + +/* + * 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)); + } + } +} diff --git a/opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java b/opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java new file mode 100644 index 0000000000..15f939d39b --- /dev/null +++ b/opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java @@ -0,0 +1,68 @@ + +/* + * 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 getContainerFlows() { + return null; + } + + @Override + public short getTag(Node n) { + return (short) 0; + } + + @Override + public Set getNodeConnectors() { + return null; + } + + @Override + public Set getNodes() { + return null; + } +} diff --git a/opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java b/opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java new file mode 100644 index 0000000000..b5624af6af --- /dev/null +++ b/opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java @@ -0,0 +1,217 @@ + +/* + * 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 = (Set) Collections + .synchronizedSet(new HashSet()); + private Set iContainerListener = Collections + .synchronizedSet(new HashSet()); + + 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 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 getContainerNames() { + /* + * Return container names as they were configured by user (case sensitive) + * along with the default container + */ + List containerNameList = new ArrayList(); + 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 portList) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Status removeContainerEntry(String containerName, + List portList) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Status addContainerFlows(String containerName, + List configObject) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Status removeContainerFlows(String containerName, + List configObject) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Status removeContainerFlows(String containerName, Set name) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getContainerConfigList() { + // TODO Auto-generated method stub + return null; + } + + @Override + public ContainerConfig getContainerConfig(String containerName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getContainerNameList() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean doesContainerExist(String ContainerId) { + // TODO Auto-generated method stub + return false; + } + + @Override + public Map> getContainerFlows() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getContainerFlows(String containerName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getContainerFlowNameList(String containerName) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/opendaylight/containermanager/implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerImplTest.java b/opendaylight/containermanager/it.implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerImplTest.java similarity index 100% rename from opendaylight/containermanager/implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerImplTest.java rename to opendaylight/containermanager/it.implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerImplTest.java diff --git a/opendaylight/containermanager/implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerManagerTest.java b/opendaylight/containermanager/it.implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerManagerTest.java similarity index 91% rename from opendaylight/containermanager/implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerManagerTest.java rename to opendaylight/containermanager/it.implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerManagerTest.java index c7e35a069c..7323a59bdb 100644 --- a/opendaylight/containermanager/implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerManagerTest.java +++ b/opendaylight/containermanager/it.implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerManagerTest.java @@ -29,7 +29,6 @@ public class ContainerManagerTest { assertEquals(GlobalConstants.DEFAULT.toString(), names.get(0)); assertFalse(cm.hasNonDefaultContainer()); - assertNull(cm.saveContainerConfig()); cm.destroy(); diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index d44cfc5c0e..56ca0c951b 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -66,6 +66,8 @@ ../../hosttracker_new/implementation ../../containermanager/api ../../containermanager/implementation + ../../containermanager/it.implementation + ../../appauth ../../switchmanager/api ../../switchmanager/implementation ../../switchmanager/integrationtest @@ -111,6 +113,7 @@ ../../northbound/hosttracker ../../northbound/subnets ../../northbound/switchmanager + ../../northbound/containermanager ../../northbound/networkconfiguration/bridgedomain diff --git a/opendaylight/distribution/opendaylight/src/assemble/bin.xml b/opendaylight/distribution/opendaylight/src/assemble/bin.xml index a85c3f8860..92a718bac3 100644 --- a/opendaylight/distribution/opendaylight/src/assemble/bin.xml +++ b/opendaylight/distribution/opendaylight/src/assemble/bin.xml @@ -15,6 +15,7 @@ org.opendaylight.controller:logging.bridge org.opendaylight.controller:protocol_plugins.stub org.opendaylight.controller:*.integrationtest + org.opendaylight.controller:containermanager.it.implementation org.opendaylight.controller:hosttracker_new org.opendaylight.controller:hosttracker_new.implementation org.opendaylight.controller:checkstyle diff --git a/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IForwardingStaticRouting.java b/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IForwardingStaticRouting.java index 0166d279f6..03d9453126 100644 --- a/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IForwardingStaticRouting.java +++ b/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IForwardingStaticRouting.java @@ -30,13 +30,13 @@ public interface IForwardingStaticRouting { * @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 getStaticRouteConfigs(); + ConcurrentMap getStaticRouteConfigs(); /** * Adds a StaticRouteConfig @@ -44,7 +44,7 @@ public interface IForwardingStaticRouting { * @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 @@ -52,5 +52,5 @@ public interface IForwardingStaticRouting { * @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); } diff --git a/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IStaticRoutingAware.java b/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IStaticRoutingAware.java index f650ee9e77..fb4863b1e0 100644 --- a/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IStaticRoutingAware.java +++ b/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IStaticRoutingAware.java @@ -21,5 +21,5 @@ public interface IStaticRoutingAware { * @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); } diff --git a/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/internal/StaticRoutingImplementation.java b/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/internal/StaticRoutingImplementation.java index 1b2128957e..89d2419245 100644 --- a/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/internal/StaticRoutingImplementation.java +++ b/opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/internal/StaticRoutingImplementation.java @@ -63,7 +63,6 @@ public class StaticRoutingImplementation implements IfNewHostNotify, private static Logger log = LoggerFactory .getLogger(StaticRoutingImplementation.class); private static String ROOT = GlobalConstants.STARTUPHOME.toString(); - private static final String SAVE = "Save"; ConcurrentMap staticRoutes; ConcurrentMap staticRouteConfigs; private IfIptoHost hostTracker; diff --git a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/PortGroupProvider.java b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/PortGroupProvider.java index 7b53eb0b8a..dc4f13f497 100644 --- a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/PortGroupProvider.java +++ b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/PortGroupProvider.java @@ -30,7 +30,7 @@ public interface PortGroupProvider { * 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 @@ -40,7 +40,7 @@ public interface PortGroupProvider { * 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 @@ -54,7 +54,7 @@ public interface PortGroupProvider { * @return Database of Switch-Id to PortGroup mapping that corresponds to * the Port Group User Configuration. */ - public Map getPortGroupData(PortGroupConfig config); + Map getPortGroupData(PortGroupConfig config); /** * Returns PortGroup data for a given Switch and user Configuration. Its the @@ -70,7 +70,7 @@ public interface PortGroupProvider { * @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 @@ -80,7 +80,7 @@ public interface PortGroupProvider { * 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 @@ -91,7 +91,7 @@ public interface PortGroupProvider { * * @return Usage string. */ - public String getApplicationDrivenMatchCriteriaUsage(); + String getApplicationDrivenMatchCriteriaUsage(); /** * Returns the name of the Custom Application that implements @@ -99,7 +99,7 @@ public interface PortGroupProvider { * * @return Provider Name */ - public String getProviderName(); + String getProviderName(); /** * Controller uses this method to check with the Provider supports the @@ -109,5 +109,5 @@ public interface PortGroupProvider { * @return true if the Provider supports the matchCriteria String. false * otherwise. */ - public boolean isMatchCriteriaSupported(String matchCriteria); + boolean isMatchCriteriaSupported(String matchCriteria); } diff --git a/opendaylight/forwardingrulesmanager/integrationtest/pom.xml b/opendaylight/forwardingrulesmanager/integrationtest/pom.xml index b0ffaacec8..7779243599 100644 --- a/opendaylight/forwardingrulesmanager/integrationtest/pom.xml +++ b/opendaylight/forwardingrulesmanager/integrationtest/pom.xml @@ -102,12 +102,12 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller - containermanager.implementation - 0.4.0-SNAPSHOT + containermanager.it.implementation + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/forwardingrulesmanager/integrationtest/src/test/java/org/opendaylight/controller/forwardingrulesmanager/internal/ForwardingRulesManagerIT.java b/opendaylight/forwardingrulesmanager/integrationtest/src/test/java/org/opendaylight/controller/forwardingrulesmanager/internal/ForwardingRulesManagerIT.java index 4fc3afc726..ef2e0fa01d 100644 --- a/opendaylight/forwardingrulesmanager/integrationtest/src/test/java/org/opendaylight/controller/forwardingrulesmanager/internal/ForwardingRulesManagerIT.java +++ b/opendaylight/forwardingrulesmanager/integrationtest/src/test/java/org/opendaylight/controller/forwardingrulesmanager/internal/ForwardingRulesManagerIT.java @@ -90,7 +90,7 @@ public class ForwardingRulesManagerIT { mavenBundle("org.opendaylight.controller", "containermanager") .versionAsInProject(), mavenBundle("org.opendaylight.controller", - "containermanager.implementation").versionAsInProject(), + "containermanager.it.implementation").versionAsInProject(), mavenBundle("org.opendaylight.controller", "forwardingrulesmanager").versionAsInProject(), diff --git a/opendaylight/hosttracker/integrationtest/pom.xml b/opendaylight/hosttracker/integrationtest/pom.xml index 351e31b11e..81551650c3 100644 --- a/opendaylight/hosttracker/integrationtest/pom.xml +++ b/opendaylight/hosttracker/integrationtest/pom.xml @@ -41,13 +41,13 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller - containermanager.implementation - 0.4.0-SNAPSHOT + containermanager.it.implementation + 0.5.0-SNAPSHOT diff --git a/opendaylight/hosttracker/integrationtest/src/test/java/org/opendaylight/controller/hosttracker/internal/HostTrackerIT.java b/opendaylight/hosttracker/integrationtest/src/test/java/org/opendaylight/controller/hosttracker/internal/HostTrackerIT.java index 57e2714ff1..4281d0db16 100644 --- a/opendaylight/hosttracker/integrationtest/src/test/java/org/opendaylight/controller/hosttracker/internal/HostTrackerIT.java +++ b/opendaylight/hosttracker/integrationtest/src/test/java/org/opendaylight/controller/hosttracker/internal/HostTrackerIT.java @@ -92,7 +92,7 @@ public class HostTrackerIT { // 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(), diff --git a/opendaylight/hosttracker_new/api/src/main/java/org/opendaylight/controller/hosttracker/Entity.java b/opendaylight/hosttracker_new/api/src/main/java/org/opendaylight/controller/hosttracker/Entity.java index 924d0717e9..64f4c7ef1e 100644 --- a/opendaylight/hosttracker_new/api/src/main/java/org/opendaylight/controller/hosttracker/Entity.java +++ b/opendaylight/hosttracker_new/api/src/main/java/org/opendaylight/controller/hosttracker/Entity.java @@ -244,10 +244,12 @@ public class Entity implements Comparable { @Override public int compareTo(Entity o) { int r; - if (port == null) + if (port == null) { r = o.port == null ? 0 : -1; - else if (o.port == null) + } + else if (o.port == null) { r = 1; + } else { // XXX - the node id is only defined as an object rather // than something useful. We're just going to have to diff --git a/opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/Device.java b/opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/Device.java index fb81cddc96..b2180297da 100755 --- a/opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/Device.java +++ b/opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/Device.java @@ -181,7 +181,8 @@ public class Device implements IDevice { * @param newEntity * the entity to add. newEntity must be have the same entity * class as device - * @param if positive indicates the index in the entities array were the new + * @param insertionpoint + * if positive indicates the index in the entities array were the new * entity should be inserted. If negative we will compute the correct * insertion point */ @@ -240,10 +241,11 @@ public class Device implements IDevice { TreeSet vals = new TreeSet(); for (Entity e : entities) { - if (e.getVlan() == null) + if (e.getVlan() == null) { vals.add((short) -1); - else + } else { vals.add(e.getVlan()); + } } return vals.toArray(new Short[vals.size()]); } @@ -313,15 +315,16 @@ public class Device implements IDevice { return false; for (AttachmentPoint ap : apList) { - if (ap.getLastSeen() + AttachmentPoint.INACTIVITY_INTERVAL < System - .currentTimeMillis()) - expiredAPs.add(ap); + if (ap.getLastSeen() + AttachmentPoint.INACTIVITY_INTERVAL < System.currentTimeMillis()) { + expiredAPs.add(ap); + } } if (expiredAPs.size() > 0) { apList.removeAll(expiredAPs); return true; - } else + } else { return false; + } } /** @@ -410,7 +413,6 @@ public class Device implements IDevice { * any change to the list of attachment points for the device -- which * indicates a device move. * - * @param sw * @param port * @param lastSeen * @return @@ -525,7 +527,6 @@ public class Device implements IDevice { /** * Delete (sw,port) from the list of list of attachment points and oldAPs. * - * @param sw * @param port * @return */ @@ -703,10 +704,12 @@ public class Device implements IDevice { TreeSet vals = new TreeSet(); for (Entity e : entities) { if (e.getPort().equals(swp.getPort())) { - if (e.getVlan() == null) + if (e.getVlan() == null) { vals.add(VLAN_UNTAGGED); - else + } + else { vals.add(e.getVlan()); + } } } return vals.toArray(new Short[vals.size()]); diff --git a/opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/DeviceManagerImpl.java b/opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/DeviceManagerImpl.java index 95d33ceef9..0390907616 100755 --- a/opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/DeviceManagerImpl.java +++ b/opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/DeviceManagerImpl.java @@ -385,11 +385,11 @@ public class DeviceManagerImpl implements IDeviceService, IEntityClassListener, long newDomain = 0; boolean newBD = false; - if (oldDomain < newDomain) - return -1; - else if (oldDomain > newDomain) + if (oldDomain < newDomain) { + return -1; + } else if (oldDomain > newDomain) { return 1; - + } // Give preference to OFPP_LOCAL always if (!oldAP.getPort().getType().equals(NodeConnectorIDType.SWSTACK) && newAP.getPort().getType() diff --git a/opendaylight/northbound/commons/pom.xml b/opendaylight/northbound/commons/pom.xml index 6031ddea35..b88375ffdc 100644 --- a/opendaylight/northbound/commons/pom.xml +++ b/opendaylight/northbound/commons/pom.xml @@ -28,12 +28,14 @@ org.opendaylight.controller.northbound.commons.exception, + org.opendaylight.controller.northbound.commons.types, org.opendaylight.controller.northbound.commons.utils, org.opendaylight.controller.northbound.commons 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, diff --git a/opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/types/StringList.java b/opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/types/StringList.java new file mode 100644 index 0000000000..62d1608098 --- /dev/null +++ b/opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/types/StringList.java @@ -0,0 +1,38 @@ +/** + * 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 list; + + public StringList() { + } + + public StringList(List list) { + this.list = list; + } + + public List getList() { + return list; + } + +} diff --git a/opendaylight/northbound/containermanager/enunciate.xml b/opendaylight/northbound/containermanager/enunciate.xml new file mode 100644 index 0000000000..9dcab30900 --- /dev/null +++ b/opendaylight/northbound/containermanager/enunciate.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/opendaylight/northbound/containermanager/pom.xml b/opendaylight/northbound/containermanager/pom.xml new file mode 100644 index 0000000000..067e5f19b9 --- /dev/null +++ b/opendaylight/northbound/containermanager/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.0-SNAPSHOT + ../../commons/opendaylight + + + org.opendaylight.controller + containermanager.northbound + 0.4.0-SNAPSHOT + bundle + + + + + org.codehaus.enunciate + maven-enunciate-plugin + ${enunciate.version} + + + org.apache.felix + maven-bundle-plugin + 2.3.6 + true + + + + + + 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 + + /controller/nb/v2/containermanager + + + + + + + + org.opendaylight.controller + containermanager + 0.5.0-SNAPSHOT + + + org.opendaylight.controller + appauth + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + web + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + sal + 0.5.0-SNAPSHOT + + + org.codehaus.enunciate + enunciate-core-annotations + ${enunciate.version} + + + org.opendaylight.controller + commons.northbound + 0.4.0-SNAPSHOT + + + org.opendaylight.controller.thirdparty + com.sun.jersey.jersey-servlet + 1.17-SNAPSHOT + + + diff --git a/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerConfigs.java b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerConfigs.java new file mode 100644 index 0000000000..eecc19f216 --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerConfigs.java @@ -0,0 +1,48 @@ + +/* + * 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; + + //To satisfy JAXB + @SuppressWarnings("unused") + private ContainerConfigs() { + + } + + + public ContainerConfigs(List containerconfig) { + this.containerConfig = containerconfig; + } + + + public List getcontainerConfig() { + return containerConfig; + } + + public void setcontainerConfig(List containerConfig) { + this.containerConfig = containerConfig; + } +} diff --git a/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthbound.java b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthbound.java new file mode 100644 index 0000000000..511addae78 --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthbound.java @@ -0,0 +1,716 @@ + +/* + * 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 + * + *
+ *
+ * Authentication scheme : HTTP Basic
+ * Authentication realm : opendaylight
+ * Transport : HTTP and HTTPS
+ *
+ * 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.
+ * 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} + * + *
+     *
+     * 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" }] } ] }
+     *
+     * 
+ */ + @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} + * + *
+     *
+     * 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" }
+     *
+     * 
+ */ + @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 containerConfigs = new ArrayList(); + 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 + * + *
+     *
+     * 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"}
+     *
+     * 
+ */ + @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 + * + *
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/green
+     *
+     * 
+ */ + @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} + * + *
+     *
+     * 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" }
+     *
+     * 
+ */ + @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 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} + * + *
+     *
+     * 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" }
+     *
+     * 
+ */ + @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 + * + *
+     *
+     * 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" }
+     *
+     * 
+ */ + @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 list = new ArrayList(); + 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 + * + *
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/black/flowspec/telnet
+     *
+     * 
+ */ + @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 set = new HashSet(); + 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 "|@|", as "OF|1@OF|00:00:00:ab:00:00:00:01" + * @return response as dictated by the HTTP Status code + * + *
+     *
+     * 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" }
+     *
+     * 
+ */ + @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 "|@|", as "OF|1@OF|00:00:00:ab:00:00:00:01" + * @return response as dictated by the HTTP Status code + * + *
+     *
+     * 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" }
+     *
+     * 
+ */ + @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"); + } + +} diff --git a/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthboundRSApplication.java b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthboundRSApplication.java new file mode 100644 index 0000000000..10559a2bbd --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthboundRSApplication.java @@ -0,0 +1,33 @@ + +/* + * 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> getClasses() { + Set> classes = new HashSet>(); + classes.add(ContainerManagerNorthbound.class); + classes.add(JacksonJaxbJsonProvider.class); + return classes; + } +} diff --git a/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/FlowSpecConfigs.java b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/FlowSpecConfigs.java new file mode 100644 index 0000000000..cc7505505c --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/FlowSpecConfigs.java @@ -0,0 +1,44 @@ +/* + * 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; + + // To satisfy JAXB + @SuppressWarnings("unused") + private FlowSpecConfigs() { + + } + + public FlowSpecConfigs(List containerFlowConfig) { + this.containerFlowConfig = containerFlowConfig; + } + + public List getContainerFlowConfig() { + return containerFlowConfig; + } + + public void setContainerFlowConfig(List containerFlowConfig) { + this.containerFlowConfig = containerFlowConfig; + } +} diff --git a/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/StringList.java b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/StringList.java new file mode 100644 index 0000000000..7cba528b2e --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/StringList.java @@ -0,0 +1,38 @@ + +/* + * 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 list; + + public StringList() { + } + + public StringList(List list) { + this.list = list; + } + + public List getList() { + return list; + } + +} diff --git a/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.factories b/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..93db02ec92 --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.beans.BeanInfoFactory=org.springframework.beans.ExtendedBeanInfoFactory diff --git a/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.handlers b/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000000..6aeb8caa14 --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.handlers @@ -0,0 +1,10 @@ +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 + diff --git a/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.schemas b/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000000..acf748af72 --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,46 @@ +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 diff --git a/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.tooling b/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.tooling new file mode 100644 index 0000000000..057d834289 --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.tooling @@ -0,0 +1,39 @@ +# 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 diff --git a/opendaylight/northbound/containermanager/src/main/resources/WEB-INF/web.xml b/opendaylight/northbound/containermanager/src/main/resources/WEB-INF/web.xml new file mode 100644 index 0000000000..6ace7ecaed --- /dev/null +++ b/opendaylight/northbound/containermanager/src/main/resources/WEB-INF/web.xml @@ -0,0 +1,49 @@ + + + + JAXRSContainerManager + com.sun.jersey.spi.container.servlet.ServletContainer + + javax.ws.rs.Application + org.opendaylight.controller.containermanager.northbound.ContainerManagerNorthboundRSApplication + + 1 + + + JAXRSContainerManager + /* + + + + NB api + /* + + + System-Admin + Network-Admin + Network-Operator + Container-User + + + + + System-Admin + + + Network-Admin + + + Network-Operator + + + Container-User + + + + BASIC + opendaylight + + diff --git a/opendaylight/northbound/flowprogrammer/enunciate.xml b/opendaylight/northbound/flowprogrammer/enunciate.xml index 90bf6de1d6..3ac676907b 100644 --- a/opendaylight/northbound/flowprogrammer/enunciate.xml +++ b/opendaylight/northbound/flowprogrammer/enunciate.xml @@ -3,7 +3,7 @@ xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd"> - + diff --git a/opendaylight/northbound/flowprogrammer/pom.xml b/opendaylight/northbound/flowprogrammer/pom.xml index 7aef0652fe..2c729ba115 100644 --- a/opendaylight/northbound/flowprogrammer/pom.xml +++ b/opendaylight/northbound/flowprogrammer/pom.xml @@ -60,7 +60,7 @@
- /controller/nb/v2/flow + /controller/nb/v2/flowprogrammer
${project.basedir}/src/main/resources/META-INF @@ -71,7 +71,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/northbound/flowprogrammer/src/main/java/org/opendaylight/controller/flowprogrammer/northbound/FlowProgrammerNorthbound.java b/opendaylight/northbound/flowprogrammer/src/main/java/org/opendaylight/controller/flowprogrammer/northbound/FlowProgrammerNorthbound.java index 257fbbda67..ea3f748dcc 100644 --- a/opendaylight/northbound/flowprogrammer/src/main/java/org/opendaylight/controller/flowprogrammer/northbound/FlowProgrammerNorthbound.java +++ b/opendaylight/northbound/flowprogrammer/src/main/java/org/opendaylight/controller/flowprogrammer/northbound/FlowProgrammerNorthbound.java @@ -48,7 +48,7 @@ import org.opendaylight.controller.sal.utils.Status; import org.opendaylight.controller.switchmanager.ISwitchManager; /** - * Flow Configuration Northbound API + * Flow Configuration Northbound API provides capabilities to program flows. * *
*
@@ -56,11 +56,7 @@ import org.opendaylight.controller.switchmanager.ISwitchManager; * Authentication realm : opendaylight
* Transport : HTTP and HTTPS
*
- * 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.
- * More info : - * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration + * HTTPS Authentication is disabled by default. * */ @Path("/") @@ -158,7 +154,7 @@ public class FlowProgrammerNorthbound { * 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"?> @@ -223,7 +219,7 @@ public class FlowProgrammerNorthbound { * 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"?> @@ -296,7 +292,7 @@ public class FlowProgrammerNorthbound { * 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"?> @@ -320,7 +316,7 @@ public class FlowProgrammerNorthbound { * * */ - @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) @@ -357,7 +353,8 @@ public class FlowProgrammerNorthbound { } /** - * 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') @@ -376,7 +373,7 @@ public class FlowProgrammerNorthbound { * 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> @@ -400,7 +397,7 @@ public class FlowProgrammerNorthbound { * */ - @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}") + @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}") @PUT @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ @@ -476,16 +473,16 @@ public class FlowProgrammerNorthbound { * 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 * * */ - @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"), @@ -523,6 +520,7 @@ public class FlowProgrammerNorthbound { Status status = frm.removeStaticFlow(name, node); if (status.isSuccess()) { NorthboundUtils.auditlog("Flow", username, "removed", name, containerName); + return Response.noContent().build(); } return NorthboundUtils.getResponse(status); } @@ -545,11 +543,11 @@ public class FlowProgrammerNorthbound { * 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 * * */ - @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}") + @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}") @POST @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ diff --git a/opendaylight/northbound/hosttracker/pom.xml b/opendaylight/northbound/hosttracker/pom.xml index 3757ea1f36..c1f6598a8a 100644 --- a/opendaylight/northbound/hosttracker/pom.xml +++ b/opendaylight/northbound/hosttracker/pom.xml @@ -61,7 +61,7 @@ org.codehaus.jackson.jaxrs, !org.codehaus.enunciate.jaxrs - /controller/nb/v2/host + /controller/nb/v2/hosttracker ${project.basedir}/src/main/resources/META-INF @@ -77,7 +77,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/northbound/hosttracker/src/main/java/org/opendaylight/controller/hosttracker/northbound/HostTrackerNorthbound.java b/opendaylight/northbound/hosttracker/src/main/java/org/opendaylight/controller/hosttracker/northbound/HostTrackerNorthbound.java index f0bdf5d891..836bfa2d60 100644 --- a/opendaylight/northbound/hosttracker/src/main/java/org/opendaylight/controller/hosttracker/northbound/HostTrackerNorthbound.java +++ b/opendaylight/northbound/hosttracker/src/main/java/org/opendaylight/controller/hosttracker/northbound/HostTrackerNorthbound.java @@ -62,11 +62,7 @@ import org.opendaylight.controller.switchmanager.ISwitchManager; * Authentication realm : opendaylight
* Transport : HTTP and HTTPS
*
- * 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.
- * More info : - * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration + * HTTPS Authentication is disabled by default. * */ @@ -137,7 +133,7 @@ public class HostTrackerNorthbound { * * RequestURL: * - * http://localhost:8080/controller/nb/v2/host/default + * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/active * * Response in XML * @@ -192,7 +188,7 @@ public class HostTrackerNorthbound { * } * */ - @Path("/{containerName}") + @Path("/{containerName}/hosts/active") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(Hosts.class) @@ -224,7 +220,7 @@ public class HostTrackerNorthbound { * * RequestURL: * - * http://localhost:8080/controller/nb/v2/host/default/inactive + * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/inactive * * Response in XML * @@ -279,7 +275,7 @@ public class HostTrackerNorthbound { * } * */ - @Path("/{containerName}/inactive") + @Path("/{containerName}/hosts/inactive") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(Hosts.class) @@ -312,7 +308,7 @@ public class HostTrackerNorthbound { * * 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 * @@ -341,7 +337,7 @@ public class HostTrackerNorthbound { * } * */ - @Path("/{containerName}/{networkAddress}") + @Path("/{containerName}/address/{networkAddress}") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(HostConfig.class) @@ -374,7 +370,8 @@ public class HostTrackerNorthbound { } /** - * 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 @@ -391,7 +388,7 @@ public class HostTrackerNorthbound { * * 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 * @@ -421,7 +418,7 @@ public class HostTrackerNorthbound { * */ - @Path("/{containerName}/{networkAddress}") + @Path("/{containerName}/address/{networkAddress}") @PUT @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ @@ -477,7 +474,7 @@ public class HostTrackerNorthbound { * @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({ diff --git a/opendaylight/northbound/integrationtest/pom.xml b/opendaylight/northbound/integrationtest/pom.xml index 1db1a49f7f..f0c04ae5e2 100644 --- a/opendaylight/northbound/integrationtest/pom.xml +++ b/opendaylight/northbound/integrationtest/pom.xml @@ -120,12 +120,12 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller - containermanager.implementation - 0.4.0-SNAPSHOT + containermanager.it.implementation + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java index 27a50c015d..59065fc7c1 100644 --- a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java +++ b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java @@ -293,7 +293,7 @@ public class NorthboundIT { @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"; @@ -313,7 +313,7 @@ public class NorthboundIT { 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"); @@ -326,7 +326,7 @@ public class NorthboundIT { // 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 @@ -340,31 +340,31 @@ public class NorthboundIT { // 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"); @@ -419,7 +419,7 @@ public class NorthboundIT { 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"); @@ -428,15 +428,15 @@ public class NorthboundIT { // 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"); @@ -475,9 +475,9 @@ public class NorthboundIT { // 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); @@ -490,7 +490,7 @@ public class NorthboundIT { @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; @@ -599,7 +599,7 @@ public class NorthboundIT { // 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); @@ -613,7 +613,7 @@ public class NorthboundIT { // 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); @@ -834,19 +834,19 @@ public class NorthboundIT { @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); @@ -860,17 +860,17 @@ public class NorthboundIT { 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. @@ -892,10 +892,10 @@ public class NorthboundIT { 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")); } @@ -970,7 +970,7 @@ public class NorthboundIT { 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(); @@ -983,7 +983,7 @@ public class NorthboundIT { 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(); @@ -996,7 +996,7 @@ public class NorthboundIT { 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 @@ -1005,7 +1005,7 @@ public class NorthboundIT { // 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); @@ -1041,7 +1041,7 @@ public class NorthboundIT { } // 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); @@ -1063,7 +1063,7 @@ public class NorthboundIT { // 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); @@ -1073,7 +1073,7 @@ public class NorthboundIT { // 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); @@ -1091,13 +1091,13 @@ public class NorthboundIT { // 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); @@ -1246,11 +1246,11 @@ public class NorthboundIT { .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); @@ -1295,12 +1295,12 @@ public class NorthboundIT { // === 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); @@ -1355,7 +1355,7 @@ public class NorthboundIT { 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(), diff --git a/opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml b/opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml index b7da9641fa..a8d81a7367 100644 --- a/opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml +++ b/opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml @@ -94,7 +94,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.codehaus.enunciate diff --git a/opendaylight/northbound/staticrouting/pom.xml b/opendaylight/northbound/staticrouting/pom.xml index 62f5b42699..eb285f2e81 100644 --- a/opendaylight/northbound/staticrouting/pom.xml +++ b/opendaylight/northbound/staticrouting/pom.xml @@ -77,7 +77,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/northbound/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/northbound/StaticRoutingNorthbound.java b/opendaylight/northbound/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/northbound/StaticRoutingNorthbound.java index 65e68aab7d..07f125dae3 100644 --- a/opendaylight/northbound/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/northbound/StaticRoutingNorthbound.java +++ b/opendaylight/northbound/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/northbound/StaticRoutingNorthbound.java @@ -16,6 +16,7 @@ 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; @@ -113,7 +114,7 @@ public class StaticRoutingNorthbound { * 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> @@ -129,7 +130,7 @@ public class StaticRoutingNorthbound { * * */ - @Path("/{containerName}") + @Path("/{containerName}/routes") @GET @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(StaticRoutes.class) @@ -204,7 +205,8 @@ public class StaticRoutingNorthbound { /** * - * 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 @@ -214,7 +216,7 @@ public class StaticRoutingNorthbound { * 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"} @@ -222,7 +224,7 @@ public class StaticRoutingNorthbound { * */ @Path("/{containerName}/route/{route}") - @POST + @PUT @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes( { @ResponseCode(code = 201, condition = "Created Static Route successfully"), @@ -284,7 +286,7 @@ public class StaticRoutingNorthbound { @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( @@ -311,9 +313,9 @@ public class StaticRoutingNorthbound { 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) { diff --git a/opendaylight/northbound/statistics/pom.xml b/opendaylight/northbound/statistics/pom.xml index eb2bf587de..76129f4cb1 100644 --- a/opendaylight/northbound/statistics/pom.xml +++ b/opendaylight/northbound/statistics/pom.xml @@ -80,7 +80,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/northbound/statistics/src/main/java/org/opendaylight/controller/statistics/northbound/StatisticsNorthbound.java b/opendaylight/northbound/statistics/src/main/java/org/opendaylight/controller/statistics/northbound/StatisticsNorthbound.java index a07f6435fe..a47bfa70b7 100644 --- a/opendaylight/northbound/statistics/src/main/java/org/opendaylight/controller/statistics/northbound/StatisticsNorthbound.java +++ b/opendaylight/northbound/statistics/src/main/java/org/opendaylight/controller/statistics/northbound/StatisticsNorthbound.java @@ -50,11 +50,7 @@ import org.opendaylight.controller.switchmanager.ISwitchManager; * Authentication realm : opendaylight
* Transport : HTTP and HTTPS
*
- * 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.
- * More info : - * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration + * HTTPS Authentication is disabled by default. * */ @Path("/") diff --git a/opendaylight/northbound/subnets/enunciate.xml b/opendaylight/northbound/subnets/enunciate.xml index 5ff13ffe80..abdc40a174 100644 --- a/opendaylight/northbound/subnets/enunciate.xml +++ b/opendaylight/northbound/subnets/enunciate.xml @@ -3,10 +3,10 @@ xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd"> - + - + diff --git a/opendaylight/northbound/subnets/pom.xml b/opendaylight/northbound/subnets/pom.xml index 9feb95fce0..c3ad783773 100644 --- a/opendaylight/northbound/subnets/pom.xml +++ b/opendaylight/northbound/subnets/pom.xml @@ -76,7 +76,7 @@ - /controller/nb/v2/subnet + /controller/nb/v2/subnetservice ${project.basedir}/src/main/resources/META-INF @@ -92,7 +92,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/northbound/subnets/src/main/java/org/opendaylight/controller/subnets/northbound/SubnetsNorthbound.java b/opendaylight/northbound/subnets/src/main/java/org/opendaylight/controller/subnets/northbound/SubnetsNorthbound.java index 50024fd739..b16c8f9ea6 100644 --- a/opendaylight/northbound/subnets/src/main/java/org/opendaylight/controller/subnets/northbound/SubnetsNorthbound.java +++ b/opendaylight/northbound/subnets/src/main/java/org/opendaylight/controller/subnets/northbound/SubnetsNorthbound.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.subnets.northbound; import java.util.HashSet; + import java.util.List; import java.util.Set; @@ -45,6 +46,19 @@ import org.opendaylight.controller.switchmanager.SubnetConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * This class provides REST APIs to manage subnets. + * + *
+ *
+ * Authentication scheme : HTTP Basic
+ * Authentication realm : opendaylight
+ * Transport : HTTP and HTTPS
+ *
+ * HTTPS Authentication is disabled by default. + * + */ + @Path("/") public class SubnetsNorthbound { protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthbound.class); @@ -100,7 +114,7 @@ public class SubnetsNorthbound { *
      * 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>
@@ -131,7 +145,7 @@ public class SubnetsNorthbound {
      * }
      * 
*/ - @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"), @@ -165,7 +179,7 @@ public class SubnetsNorthbound { *
      * 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>
@@ -213,7 +227,8 @@ public class SubnetsNorthbound {
     }
 
     /**
-     * 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
@@ -227,7 +242,7 @@ public class SubnetsNorthbound {
      *         
      * 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>
@@ -244,7 +259,7 @@ public class SubnetsNorthbound {
      */
 
     @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"),
@@ -297,7 +312,7 @@ public class SubnetsNorthbound {
      *
      *         
      * 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
      *
      * 
*/ @@ -345,7 +360,7 @@ public class SubnetsNorthbound { *
      * 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>
@@ -364,8 +379,8 @@ public class SubnetsNorthbound {
      * }
      * 
*/ - @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"), @@ -442,7 +457,7 @@ public class SubnetsNorthbound { } /** - * 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 @@ -455,7 +470,7 @@ public class SubnetsNorthbound { * *
      * 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>
@@ -475,8 +490,8 @@ public class SubnetsNorthbound {
      *
      * 
*/ - @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"), @@ -557,7 +572,7 @@ public class SubnetsNorthbound { * *
      * 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>
@@ -573,7 +588,7 @@ public class SubnetsNorthbound {
      *
      * 
*/ - @Path("/{containerName}/subnet/{subnetName}/node-ports") + @Path("/{containerName}/subnet/{subnetName}/nodePorts") @DELETE @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ @@ -635,7 +650,7 @@ public class SubnetsNorthbound { } } if (successful) { - return Response.status(Response.Status.NO_CONTENT).build(); + return Response.noContent().build(); } throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString()); } diff --git a/opendaylight/northbound/switchmanager/enunciate.xml b/opendaylight/northbound/switchmanager/enunciate.xml index 8a8bfdb029..6f2dd4d0e7 100644 --- a/opendaylight/northbound/switchmanager/enunciate.xml +++ b/opendaylight/northbound/switchmanager/enunciate.xml @@ -3,7 +3,7 @@ xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd"> - + diff --git a/opendaylight/northbound/switchmanager/pom.xml b/opendaylight/northbound/switchmanager/pom.xml index 965075a271..a416414d06 100644 --- a/opendaylight/northbound/switchmanager/pom.xml +++ b/opendaylight/northbound/switchmanager/pom.xml @@ -60,7 +60,7 @@ org.codehaus.jackson.jaxrs, !org.codehaus.enunciate.jaxrs - /controller/nb/v2/switch + /controller/nb/v2/switchmanager ${project.basedir}/src/main/resources/META-INF @@ -77,7 +77,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthbound.java b/opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthbound.java index 1edd945dfa..f4d302b2d1 100644 --- a/opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthbound.java +++ b/opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthbound.java @@ -119,7 +119,7 @@ public class SwitchNorthbound { * 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"?> @@ -194,7 +194,7 @@ public class SwitchNorthbound { List res = new ArrayList(); Set nodes = switchManager.getNodes(); if (nodes == null) { - return null; + return new Nodes(res); } for (Node node : nodes) { @@ -212,7 +212,9 @@ public class SwitchNorthbound { } /** - * 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') @@ -237,7 +239,7 @@ public class SwitchNorthbound { * 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 * *
*/ @@ -316,7 +318,7 @@ public class SwitchNorthbound { * 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 * *
*/ @@ -325,7 +327,7 @@ public class SwitchNorthbound { @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"), @@ -369,7 +371,8 @@ public class SwitchNorthbound { 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(); } } } @@ -401,7 +404,7 @@ public class SwitchNorthbound { * 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"?> @@ -489,7 +492,9 @@ public class SwitchNorthbound { } /** - * 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') @@ -521,7 +526,7 @@ public class SwitchNorthbound { * 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 * * */ @@ -610,7 +615,7 @@ public class SwitchNorthbound { * 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 * * */ @@ -619,7 +624,7 @@ public class SwitchNorthbound { @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") }) @@ -657,7 +662,7 @@ public class SwitchNorthbound { 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()); } @@ -674,11 +679,11 @@ public class SwitchNorthbound { * Example: * * RequestURL: - * http://localhost:8080/controller/nb/v2/switch/default/switch-config + * http://localhost:8080/controller/nb/v2/switchmanager/default/save * * */ - @Path("/{containerName}/switch-config") + @Path("/{containerName}/save") @POST @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ diff --git a/opendaylight/northbound/topology/enunciate.xml b/opendaylight/northbound/topology/enunciate.xml index b7c451c8e6..fed0f68e30 100644 --- a/opendaylight/northbound/topology/enunciate.xml +++ b/opendaylight/northbound/topology/enunciate.xml @@ -7,6 +7,6 @@ - + diff --git a/opendaylight/northbound/topology/pom.xml b/opendaylight/northbound/topology/pom.xml index b2b7b9c85f..803a28079e 100644 --- a/opendaylight/northbound/topology/pom.xml +++ b/opendaylight/northbound/topology/pom.xml @@ -79,7 +79,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java b/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java index 508ffafe6f..ff26bfd21f 100644 --- a/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java +++ b/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java @@ -52,11 +52,7 @@ import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig; * Authentication realm : opendaylight
* Transport : HTTP and HTTPS
*
- * 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.
- * More info : - * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration + * HTTPS Authentication is disabled by default. */ @Path("/") @@ -81,7 +77,7 @@ public class TopologyNorthboundJAXRS { * 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. * *
@@ -222,7 +218,7 @@ public class TopologyNorthboundJAXRS {
      * 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"?>
@@ -241,7 +237,7 @@ public class TopologyNorthboundJAXRS {
      *
      * 
*/ - @Path("/{containerName}/user-link") + @Path("/{containerName}/userLinks") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(TopologyUserLinks.class) @@ -278,6 +274,8 @@ public class TopologyNorthboundJAXRS { * * @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 @@ -287,7 +285,7 @@ public class TopologyNorthboundJAXRS { * 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"?> @@ -303,7 +301,7 @@ public class TopologyNorthboundJAXRS { * * */ - @Path("/{containerName}/user-link") + @Path("/{containerName}/userLink/{name}") @PUT @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @@ -315,6 +313,7 @@ public class TopologyNorthboundJAXRS { @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( @@ -352,16 +351,16 @@ public class TopologyNorthboundJAXRS { * Example: * * RequestURL: - * http://localhost:8080/controller/nb/v2/topology/default/user-link/config1 + * http://localhost:8080/controller/nb/v2/topology/default/userLink/config1 * * */ - @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( @@ -384,8 +383,8 @@ public class TopologyNorthboundJAXRS { 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); } } diff --git a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/DiscoveryService.java b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/DiscoveryService.java index 0895d9af27..869d36d9cb 100644 --- a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/DiscoveryService.java +++ b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/DiscoveryService.java @@ -33,17 +33,11 @@ import org.opendaylight.controller.protocol_plugin.openflow.IInventoryProvider; 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; @@ -61,6 +55,11 @@ import org.opendaylight.controller.sal.utils.HexEncode; 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. @@ -561,10 +560,11 @@ public class DiscoveryService implements IInventoryShimExternalListener, IDataPa private void addDiscovery(Node node) { Map switches = controller.getSwitches(); ISwitch sw = switches.get(node.getID()); - List ports = sw.getEnabledPorts(); - if (ports == null) { + if (sw == null) { + //switch could be removed by now, stop propagation return; } + List ports = sw.getEnabledPorts(); for (OFPhysicalPort port : ports) { NodeConnector nodeConnector = NodeConnectorCreator.createOFNodeConnector(port.getPortNumber(), node); if (!readyListHi.contains(nodeConnector)) { diff --git a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/InventoryServiceShim.java b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/InventoryServiceShim.java index 9e01796806..92eec43b4c 100644 --- a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/InventoryServiceShim.java +++ b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/InventoryServiceShim.java @@ -482,8 +482,7 @@ public class InventoryServiceShim implements IContainerListener, Set props = new HashSet(); 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")); diff --git a/opendaylight/sal/yang-prototype/sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java b/opendaylight/sal/yang-prototype/sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java new file mode 100644 index 0000000000..86dcba9596 --- /dev/null +++ b/opendaylight/sal/yang-prototype/sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java @@ -0,0 +1,103 @@ +/* + * 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.sal.common.util; + +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; + +/** + * @author mirehak + * + */ +public class RpcErrors { + + /** + * @param applicationTag + * @param tag + * @param info + * @param severity + * @param message + * @param errorType + * @param cause + * @return {@link RpcError} implementation + */ + public static RpcError getRpcError(String applicationTag, String tag, String info, + ErrorSeverity severity, String message, ErrorType errorType, Throwable cause) { + RpcErrorTO ret = new RpcErrorTO(applicationTag, tag, info, severity, message, + errorType, cause); + return ret; + } + + private static class RpcErrorTO implements RpcError { + + private final String applicationTag; + private final String tag; + private final String info; + private final ErrorSeverity severity; + private final String message; + private final ErrorType errorType; + private final Throwable cause; + + /** + * @param applicationTag + * @param tag + * @param info + * @param severity + * @param message + * @param errorType + * @param cause + */ + protected RpcErrorTO(String applicationTag, String tag, String info, + ErrorSeverity severity, String message, ErrorType errorType, Throwable cause) { + super(); + this.applicationTag = applicationTag; + this.tag = tag; + this.info = info; + this.severity = severity; + this.message = message; + this.errorType = errorType; + this.cause = cause; + } + + @Override + public String getApplicationTag() { + return applicationTag; + } + + @Override + public String getInfo() { + return info; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public ErrorSeverity getSeverity() { + return severity; + } + + @Override + public String getTag() { + return tag; + } + + @Override + public Throwable getCause() { + return cause; + } + + @Override + public ErrorType getErrorType() { + return errorType; + } + } +} diff --git a/opendaylight/samples/northbound/loadbalancer/pom.xml b/opendaylight/samples/northbound/loadbalancer/pom.xml index 5ec72024c2..2ccb440952 100644 --- a/opendaylight/samples/northbound/loadbalancer/pom.xml +++ b/opendaylight/samples/northbound/loadbalancer/pom.xml @@ -77,7 +77,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/statisticsmanager/api/pom.xml b/opendaylight/statisticsmanager/api/pom.xml index a755f83560..bddcc2ae0e 100644 --- a/opendaylight/statisticsmanager/api/pom.xml +++ b/opendaylight/statisticsmanager/api/pom.xml @@ -52,7 +52,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/statisticsmanager/implementation/pom.xml b/opendaylight/statisticsmanager/implementation/pom.xml index 9b3dd0e8e2..8be254e6b5 100644 --- a/opendaylight/statisticsmanager/implementation/pom.xml +++ b/opendaylight/statisticsmanager/implementation/pom.xml @@ -105,7 +105,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/statisticsmanager/integrationtest/pom.xml b/opendaylight/statisticsmanager/integrationtest/pom.xml index ebe9365320..527dd0a02b 100644 --- a/opendaylight/statisticsmanager/integrationtest/pom.xml +++ b/opendaylight/statisticsmanager/integrationtest/pom.xml @@ -36,12 +36,12 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller - containermanager.implementation - 0.4.0-SNAPSHOT + containermanager.it.implementation + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/statisticsmanager/integrationtest/src/test/java/org/opendaylight/controller/statisticsmanager/internal/StatisticsManagerIT.java b/opendaylight/statisticsmanager/integrationtest/src/test/java/org/opendaylight/controller/statisticsmanager/internal/StatisticsManagerIT.java index 43c297da18..457e99dcd4 100644 --- a/opendaylight/statisticsmanager/integrationtest/src/test/java/org/opendaylight/controller/statisticsmanager/internal/StatisticsManagerIT.java +++ b/opendaylight/statisticsmanager/integrationtest/src/test/java/org/opendaylight/controller/statisticsmanager/internal/StatisticsManagerIT.java @@ -85,7 +85,7 @@ public class StatisticsManagerIT { // 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(), diff --git a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java index 55a7ecb9cd..1deda7c9d0 100644 --- a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java +++ b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java @@ -156,16 +156,20 @@ public class Subnet implements Cloneable, Serializable { } public boolean isSubnetOf(InetAddress ip) { - if (ip == null) + if (ip == null) { return false; + } InetAddress thisPrefix = getPrefixForAddress(this.networkAddress); InetAddress otherPrefix = getPrefixForAddress(ip); - if ((thisPrefix == null) || (otherPrefix == null)) + if ((thisPrefix == null) || (otherPrefix == null)) { return false; - if (thisPrefix.equals(otherPrefix)) + } + if (thisPrefix.equals(otherPrefix)) { return true; - else + } + else { return false; + } } public short getVlan() { diff --git a/opendaylight/switchmanager/integrationtest/pom.xml b/opendaylight/switchmanager/integrationtest/pom.xml index d057c8e3ab..d571d9df53 100644 --- a/opendaylight/switchmanager/integrationtest/pom.xml +++ b/opendaylight/switchmanager/integrationtest/pom.xml @@ -41,12 +41,12 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller - containermanager.implementation - 0.4.0-SNAPSHOT + containermanager.it.implementation + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java b/opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java index eb8a07e707..2a40eb0ff8 100644 --- a/opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java +++ b/opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java @@ -92,7 +92,7 @@ public class SwitchManagerIT { 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") diff --git a/opendaylight/usermanager/api/pom.xml b/opendaylight/usermanager/api/pom.xml index c5f78ab56c..eb4bea584c 100644 --- a/opendaylight/usermanager/api/pom.xml +++ b/opendaylight/usermanager/api/pom.xml @@ -71,7 +71,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/usermanager/implementation/pom.xml b/opendaylight/usermanager/implementation/pom.xml index fb96928745..25e558eed9 100644 --- a/opendaylight/usermanager/implementation/pom.xml +++ b/opendaylight/usermanager/implementation/pom.xml @@ -75,7 +75,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/web/devices/pom.xml b/opendaylight/web/devices/pom.xml index 64e8ff5e01..b322f5845f 100644 --- a/opendaylight/web/devices/pom.xml +++ b/opendaylight/web/devices/pom.xml @@ -97,7 +97,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/web/flows/pom.xml b/opendaylight/web/flows/pom.xml index 52b3ef3de3..0f1f0cf135 100644 --- a/opendaylight/web/flows/pom.xml +++ b/opendaylight/web/flows/pom.xml @@ -97,7 +97,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/web/root/pom.xml b/opendaylight/web/root/pom.xml index bc5c73a7d8..98c740fa2a 100644 --- a/opendaylight/web/root/pom.xml +++ b/opendaylight/web/root/pom.xml @@ -123,7 +123,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/ClusterNodeBean.java b/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/ClusterNodeBean.java index 5e4f22afe2..e59379916a 100644 --- a/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/ClusterNodeBean.java +++ b/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/ClusterNodeBean.java @@ -11,6 +11,7 @@ public class ClusterNodeBean { private final String name; private final Boolean me; private final Boolean coordinator; + private final Integer numConnectedNodes; public static class Builder { // required params @@ -20,6 +21,7 @@ public class ClusterNodeBean { // optional params private Boolean me = null; private Boolean coordinator = null; + private Integer numConnectedNodes = null; public Builder(InetAddress address) { this.address = address.getAddress(); @@ -36,6 +38,11 @@ public class ClusterNodeBean { return this; } + public Builder nodesConnected(int numNodes) { + this.numConnectedNodes = numNodes; + return this; + } + public ClusterNodeBean build() { return new ClusterNodeBean(this); } @@ -46,5 +53,6 @@ public class ClusterNodeBean { this.name = builder.name; this.me = builder.me; this.coordinator = builder.coordinator; + this.numConnectedNodes = builder.numConnectedNodes; } } \ No newline at end of file diff --git a/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/DaylightWebAdmin.java b/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/DaylightWebAdmin.java index 3b0b85c065..13a3a10667 100644 --- a/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/DaylightWebAdmin.java +++ b/opendaylight/web/root/src/main/java/org/opendaylight/controller/web/DaylightWebAdmin.java @@ -55,15 +55,29 @@ public class DaylightWebAdmin { if (clusterServices == null) { return null; } + IConnectionManager connectionManager = (IConnectionManager) ServiceHelper.getGlobalInstance( + IConnectionManager.class, this); + if (connectionManager == null) { + return null; + } List clusterNodes = new ArrayList(); List controllers = clusterServices.getClusteredControllers(); for (InetAddress controller : controllers) { ClusterNodeBean.Builder clusterBeanBuilder = new ClusterNodeBean.Builder(controller); + + //get number of connected nodes + Set 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(); } diff --git a/opendaylight/web/root/src/main/resources/js/open.js b/opendaylight/web/root/src/main/resources/js/open.js index 85e4d472ef..43a7dfdc44 100644 --- a/opendaylight/web/root/src/main/resources/js/open.js +++ b/opendaylight/web/root/src/main/resources/js/open.js @@ -1,680 +1,695 @@ 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 */ @@ -684,39 +699,39 @@ one.role = $('#admin').data('role'); // 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 */ diff --git a/opendaylight/web/topology/pom.xml b/opendaylight/web/topology/pom.xml index f2f7a3e59e..c4c66e46d5 100644 --- a/opendaylight/web/topology/pom.xml +++ b/opendaylight/web/topology/pom.xml @@ -101,7 +101,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/web/troubleshoot/pom.xml b/opendaylight/web/troubleshoot/pom.xml index 38b82494ea..2be78c49a5 100644 --- a/opendaylight/web/troubleshoot/pom.xml +++ b/opendaylight/web/troubleshoot/pom.xml @@ -99,7 +99,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/third-party/openflowj/src/main/java/org/openflow/protocol/OFError.java b/third-party/openflowj/src/main/java/org/openflow/protocol/OFError.java index 74e39b225c..361a03b926 100644 --- a/third-party/openflowj/src/main/java/org/openflow/protocol/OFError.java +++ b/third-party/openflowj/src/main/java/org/openflow/protocol/OFError.java @@ -128,10 +128,11 @@ public class OFError extends OFMessage implements OFMessageFactoryAware { // OVS apparently sends partial messages in errors // need to be careful of that AND can't use data.limit() as // a packet boundary because there could be more data queued - if (messages.size() > 0) + if (messages.size() > 0) { return messages.get(0); - else + } else { return null; + } } /** diff --git a/third-party/openflowj/src/main/java/org/openflow/protocol/action/ActionVendorOutputNextHop.java b/third-party/openflowj/src/main/java/org/openflow/protocol/action/ActionVendorOutputNextHop.java index d5e5ab0546..f26ca51a79 100644 --- a/third-party/openflowj/src/main/java/org/openflow/protocol/action/ActionVendorOutputNextHop.java +++ b/third-party/openflowj/src/main/java/org/openflow/protocol/action/ActionVendorOutputNextHop.java @@ -74,11 +74,12 @@ import org.openflow.util.HexString; public void setNextHop(InetAddress address) { short actionLen; - if (address instanceof Inet4Address) - actionLen = (short)ONHLength.ONH_LEN_IPV4.getValue(); - else - actionLen = (short)ONHLength.ONH_LEN_IPV6.getValue(); - super.setLength(actionLen); + if (address instanceof Inet4Address) { + actionLen = (short)ONHLength.ONH_LEN_IPV4.getValue(); + } else { + actionLen = (short)ONHLength.ONH_LEN_IPV6.getValue(); + } + super.setLength(actionLen); this.address = address; } public InetAddress getNextHop() {