From 5ec563c2e47e7e104223bbad155f9f3f0fadd38b Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Wed, 11 Sep 2013 16:20:26 -0700 Subject: [PATCH] Container Management and associated Northbound APIs. Features : 1. Create, Modify and delete containers 2. ContainerFlow management 3. Northbound APIs 4. Application authorization support (contributed by Alessandro Boch) Change-Id: I080d58c2869720c7e78fcdff673b51b142b1c2c2 Signed-off-by: Madhu Venugopal --- opendaylight/appauth/pom.xml | 68 + .../controller/appauth/Application.java | 20 + .../appauth/authorization/Authorization.java | 614 +++++++ .../clustering/integrationtest/pom.xml | 6 +- .../internal/ClusteringServicesIT.java | 2 +- .../configuration/integrationtest/pom.xml | 6 +- .../internal/ConfigurationIT.java | 2 +- opendaylight/containermanager/api/pom.xml | 12 +- .../ContainerChangeEvent.java | 33 + .../containermanager/ContainerConfig.java | 628 +++++++ .../containermanager/ContainerData.java | 241 +++ .../ContainerFlowChangeEvent.java | 39 + .../containermanager/ContainerFlowConfig.java | 665 +++++++ .../containermanager/IContainerManager.java | 171 +- .../NodeConnectorsChangeEvent.java | 43 + .../containermanager/implementation/pom.xml | 27 +- .../containermanager/internal/Activator.java | 127 +- .../internal/ContainerImpl.java | 63 +- .../internal/ContainerManager.java | 1539 ++++++++++++++++- .../internal/IContainerInternal.java | 33 + .../it.implementation/pom.xml | 73 + .../containermanager/internal/Activator.java | 129 ++ .../internal/ContainerImpl.java | 68 + .../internal/ContainerManager.java | 217 +++ .../internal/ContainerImplTest.java | 0 .../internal/ContainerManagerTest.java | 1 - .../distribution/opendaylight/pom.xml | 3 + .../opendaylight/src/assemble/bin.xml | 1 + .../integrationtest/pom.xml | 6 +- .../internal/ForwardingRulesManagerIT.java | 2 +- .../hosttracker/integrationtest/pom.xml | 6 +- .../hosttracker/internal/HostTrackerIT.java | 2 +- .../northbound/containermanager/enunciate.xml | 11 + .../northbound/containermanager/pom.xml | 94 + .../northbound/ContainerConfigs.java | 48 + .../ContainerManagerNorthbound.java | 716 ++++++++ ...ntainerManagerNorthboundRSApplication.java | 33 + .../northbound/FlowSpecConfigs.java | 44 + .../northbound/StringList.java | 38 + .../main/resources/META-INF/spring.factories | 1 + .../main/resources/META-INF/spring.handlers | 10 + .../main/resources/META-INF/spring.schemas | 46 + .../main/resources/META-INF/spring.tooling | 39 + .../src/main/resources/WEB-INF/web.xml | 49 + .../northbound/flowprogrammer/pom.xml | 2 +- opendaylight/northbound/hosttracker/pom.xml | 2 +- .../northbound/integrationtest/pom.xml | 6 +- .../integrationtest/NorthboundIT.java | 2 +- .../networkconfiguration/bridgedomain/pom.xml | 2 +- opendaylight/northbound/staticrouting/pom.xml | 2 +- opendaylight/northbound/statistics/pom.xml | 2 +- opendaylight/northbound/subnets/pom.xml | 2 +- opendaylight/northbound/switchmanager/pom.xml | 2 +- opendaylight/northbound/topology/pom.xml | 2 +- .../samples/northbound/loadbalancer/pom.xml | 2 +- opendaylight/statisticsmanager/api/pom.xml | 2 +- .../statisticsmanager/implementation/pom.xml | 2 +- .../statisticsmanager/integrationtest/pom.xml | 6 +- .../internal/StatisticsManagerIT.java | 2 +- .../switchmanager/integrationtest/pom.xml | 6 +- .../internal/SwitchManagerIT.java | 2 +- opendaylight/usermanager/api/pom.xml | 2 +- .../usermanager/implementation/pom.xml | 2 +- opendaylight/web/devices/pom.xml | 2 +- opendaylight/web/flows/pom.xml | 2 +- opendaylight/web/root/pom.xml | 2 +- opendaylight/web/topology/pom.xml | 2 +- opendaylight/web/troubleshoot/pom.xml | 2 +- 68 files changed, 5885 insertions(+), 151 deletions(-) create mode 100644 opendaylight/appauth/pom.xml create mode 100644 opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/Application.java create mode 100644 opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/authorization/Authorization.java create mode 100644 opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerChangeEvent.java create mode 100644 opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerConfig.java create mode 100644 opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerData.java create mode 100644 opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowChangeEvent.java create mode 100644 opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowConfig.java create mode 100644 opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/NodeConnectorsChangeEvent.java create mode 100644 opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/IContainerInternal.java create mode 100644 opendaylight/containermanager/it.implementation/pom.xml create mode 100644 opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java create mode 100644 opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java create mode 100644 opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java rename opendaylight/containermanager/{implementation => it.implementation}/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerImplTest.java (100%) rename opendaylight/containermanager/{implementation => it.implementation}/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerManagerTest.java (91%) create mode 100644 opendaylight/northbound/containermanager/enunciate.xml create mode 100644 opendaylight/northbound/containermanager/pom.xml create mode 100644 opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerConfigs.java create mode 100644 opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthbound.java create mode 100644 opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthboundRSApplication.java create mode 100644 opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/FlowSpecConfigs.java create mode 100644 opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/StringList.java create mode 100644 opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.factories create mode 100644 opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.handlers create mode 100644 opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.schemas create mode 100644 opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.tooling create mode 100644 opendaylight/northbound/containermanager/src/main/resources/WEB-INF/web.xml 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/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/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/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/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/pom.xml b/opendaylight/northbound/flowprogrammer/pom.xml index 960c835dc8..2c729ba115 100644 --- a/opendaylight/northbound/flowprogrammer/pom.xml +++ b/opendaylight/northbound/flowprogrammer/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/northbound/hosttracker/pom.xml b/opendaylight/northbound/hosttracker/pom.xml index d39ae17c90..c1f6598a8a 100644 --- a/opendaylight/northbound/hosttracker/pom.xml +++ b/opendaylight/northbound/hosttracker/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/integrationtest/pom.xml b/opendaylight/northbound/integrationtest/pom.xml index 90b36de7ef..ffbce7b16e 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 38336c1dd8..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 @@ -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/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/subnets/pom.xml b/opendaylight/northbound/subnets/pom.xml index 4715664132..c3ad783773 100644 --- a/opendaylight/northbound/subnets/pom.xml +++ b/opendaylight/northbound/subnets/pom.xml @@ -92,7 +92,7 @@ org.opendaylight.controller containermanager - 0.4.0-SNAPSHOT + 0.5.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/northbound/switchmanager/pom.xml b/opendaylight/northbound/switchmanager/pom.xml index 6b320b1992..a416414d06 100644 --- a/opendaylight/northbound/switchmanager/pom.xml +++ b/opendaylight/northbound/switchmanager/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/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/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/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/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 -- 2.36.6