Container Management and associated Northbound APIs. 64/1164/3
authorMadhu Venugopal <vmadhu@cisco.com>
Wed, 11 Sep 2013 23:20:26 +0000 (16:20 -0700)
committerMadhu Venugopal <vmadhu@cisco.com>
Thu, 12 Sep 2013 02:20:05 +0000 (19:20 -0700)
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 <vmadhu@cisco.com>
68 files changed:
opendaylight/appauth/pom.xml [new file with mode: 0644]
opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/Application.java [new file with mode: 0644]
opendaylight/appauth/src/main/java/org/opendaylight/controller/appauth/authorization/Authorization.java [new file with mode: 0644]
opendaylight/clustering/integrationtest/pom.xml
opendaylight/clustering/integrationtest/src/test/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusteringServicesIT.java
opendaylight/configuration/integrationtest/pom.xml
opendaylight/configuration/integrationtest/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationIT.java
opendaylight/containermanager/api/pom.xml
opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerChangeEvent.java [new file with mode: 0644]
opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerConfig.java [new file with mode: 0644]
opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerData.java [new file with mode: 0644]
opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowChangeEvent.java [new file with mode: 0644]
opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowConfig.java [new file with mode: 0644]
opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/IContainerManager.java
opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/NodeConnectorsChangeEvent.java [new file with mode: 0644]
opendaylight/containermanager/implementation/pom.xml
opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java
opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java
opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java
opendaylight/containermanager/implementation/src/main/java/org/opendaylight/controller/containermanager/internal/IContainerInternal.java [new file with mode: 0644]
opendaylight/containermanager/it.implementation/pom.xml [new file with mode: 0644]
opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/Activator.java [new file with mode: 0644]
opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerImpl.java [new file with mode: 0644]
opendaylight/containermanager/it.implementation/src/main/java/org/opendaylight/controller/containermanager/internal/ContainerManager.java [new file with mode: 0644]
opendaylight/containermanager/it.implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerImplTest.java [moved from opendaylight/containermanager/implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerImplTest.java with 100% similarity]
opendaylight/containermanager/it.implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerManagerTest.java [moved from opendaylight/containermanager/implementation/src/test/java/org/opendaylight/controller/containermanager/internal/ContainerManagerTest.java with 91% similarity]
opendaylight/distribution/opendaylight/pom.xml
opendaylight/distribution/opendaylight/src/assemble/bin.xml
opendaylight/forwardingrulesmanager/integrationtest/pom.xml
opendaylight/forwardingrulesmanager/integrationtest/src/test/java/org/opendaylight/controller/forwardingrulesmanager/internal/ForwardingRulesManagerIT.java
opendaylight/hosttracker/integrationtest/pom.xml
opendaylight/hosttracker/integrationtest/src/test/java/org/opendaylight/controller/hosttracker/internal/HostTrackerIT.java
opendaylight/northbound/containermanager/enunciate.xml [new file with mode: 0644]
opendaylight/northbound/containermanager/pom.xml [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerConfigs.java [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthbound.java [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/ContainerManagerNorthboundRSApplication.java [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/FlowSpecConfigs.java [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/java/org/opendaylight/controller/containermanager/northbound/StringList.java [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.factories [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.handlers [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.schemas [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/resources/META-INF/spring.tooling [new file with mode: 0644]
opendaylight/northbound/containermanager/src/main/resources/WEB-INF/web.xml [new file with mode: 0644]
opendaylight/northbound/flowprogrammer/pom.xml
opendaylight/northbound/hosttracker/pom.xml
opendaylight/northbound/integrationtest/pom.xml
opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java
opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml
opendaylight/northbound/staticrouting/pom.xml
opendaylight/northbound/statistics/pom.xml
opendaylight/northbound/subnets/pom.xml
opendaylight/northbound/switchmanager/pom.xml
opendaylight/northbound/topology/pom.xml
opendaylight/samples/northbound/loadbalancer/pom.xml
opendaylight/statisticsmanager/api/pom.xml
opendaylight/statisticsmanager/implementation/pom.xml
opendaylight/statisticsmanager/integrationtest/pom.xml
opendaylight/statisticsmanager/integrationtest/src/test/java/org/opendaylight/controller/statisticsmanager/internal/StatisticsManagerIT.java
opendaylight/switchmanager/integrationtest/pom.xml
opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java
opendaylight/usermanager/api/pom.xml
opendaylight/usermanager/implementation/pom.xml
opendaylight/web/devices/pom.xml
opendaylight/web/flows/pom.xml
opendaylight/web/root/pom.xml
opendaylight/web/topology/pom.xml
opendaylight/web/troubleshoot/pom.xml

diff --git a/opendaylight/appauth/pom.xml b/opendaylight/appauth/pom.xml
new file mode 100644 (file)
index 0000000..71aa49f
--- /dev/null
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>commons.opendaylight</artifactId>
+    <version>1.4.0-SNAPSHOT</version>
+    <relativePath>../commons/opendaylight</relativePath>
+  </parent>
+  <scm>
+    <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+  </scm>
+  <artifactId>appauth</artifactId>
+  <version>0.4.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.3.6</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            org.opendaylight.controller.sal.authorization,
+                            org.opendaylight.controller.sal.utils,
+                            org.opendaylight.controller.usermanager,
+                            org.slf4j,
+                            org.apache.felix.dm,
+                             org.apache.commons.lang3.builder,
+                            org.eclipse.osgi.framework.console
+                        </Import-Package>
+                        <Export-Package>
+                            org.opendaylight.controller.appauth,
+                            org.opendaylight.controller.appauth.authorization
+                        </Export-Package>
+                        <Bundle-Activator>
+                        </Bundle-Activator>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+         <groupId>org.opendaylight.controller</groupId>
+           <artifactId>sal</artifactId>
+          <version>0.5.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+         <groupId>org.opendaylight.controller</groupId>
+           <artifactId>usermanager</artifactId>
+          <version>0.4.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+          <groupId>junit</groupId>
+          <artifactId>junit</artifactId>
+          <version>4.8.1</version>
+          <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
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 (file)
index 0000000..84a2e91
--- /dev/null
@@ -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<T> extends Authorization<T> {
+
+    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 (file)
index 0000000..acfc725
--- /dev/null
@@ -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<T> implements IResourceAuthorization {
+private static final Logger logger = LoggerFactory.getLogger(Authorization.class);
+    private static final String namesRegex = "^[a-zA-Z0-9]+[{\\.|\\_|\\-}[a-zA-Z0-9]]*$";
+    /*
+     * The configured resource groups
+     */
+    protected ConcurrentMap<String, Set<T>> resourceGroups;
+    /*
+     * The configured roles along with their level
+     */
+    protected ConcurrentMap<String, AppRoleLevel> roles;
+    /*
+     * The association of groups to roles
+     */
+    protected ConcurrentMap<String, Set<ResourceGroup>> groupsAuthorizations;
+    /*
+     * The name of the default group. It is the group which contains all the
+     * resources
+     */
+    protected String allResourcesGroupName;
+
+    @Override
+    public Status createRole(String role, AppRoleLevel level) {
+        if (role == null || role.trim().isEmpty()
+                || !role.matches(Authorization.namesRegex)) {
+            return new Status(StatusCode.BADREQUEST,
+                    "Role name must start with alphanumeric, no special characters.");
+        }
+        if (isControllerRole(role)) {
+            return new Status(StatusCode.NOTALLOWED,
+                    "Controller roles cannot be explicitely "
+                            + "created in App context");
+        }
+        if (isRoleInUse(role)) {
+            return new Status(StatusCode.CONFLICT, "Role already in use");
+        }
+        if (roles.containsKey(role)) {
+            if (roles.get(role).equals(level)) {
+                return new Status(StatusCode.SUCCESS, "Role is already present");
+            } else {
+                return new Status(StatusCode.BADREQUEST,
+                        "Role exists and has different level");
+            }
+        }
+
+        return createRoleInternal(role, level);
+    }
+
+    protected Status createRoleInternal(String role, AppRoleLevel level) {
+        roles.put(role, level);
+        groupsAuthorizations.put(role, new HashSet<ResourceGroup>());
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    @Override
+    public Status removeRole(String role) {
+        if (role == null || role.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
+        }
+        if (isControllerRole(role)) {
+            return new Status(StatusCode.NOTALLOWED,
+                    "Controller roles cannot be removed");
+        }
+
+        return removeRoleInternal(role);
+    }
+
+    protected Status removeRoleInternal(String role) {
+        groupsAuthorizations.remove(role);
+        roles.remove(role);
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    @Override
+    public List<String> getRoles() {
+        return new ArrayList<String>(groupsAuthorizations.keySet());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Status createResourceGroup(String groupName, List<Object> resources) {
+        //verify group name not null/empty
+        if (groupName == null || groupName.trim().isEmpty()
+                || !groupName.matches(Authorization.namesRegex)) {
+            return new Status(StatusCode.BADREQUEST, "Group name must start with alphanumeric, no special characters");
+        }
+        //verify group name is not same as all-resources
+        if (groupName.equals(this.allResourcesGroupName)) {
+            return new Status(StatusCode.NOTALLOWED, "All resource group cannot be created");
+        }
+        //verify group name is unique
+        if (resourceGroups.containsKey(groupName)) {
+        return new Status(StatusCode.CONFLICT, "Group name already exists");
+        }
+
+        //try adding resources, discard if not of type T
+        Set<T> toBeAdded = new HashSet<T>();
+        boolean allAdded = true;
+        for (Object obj : resources) {
+            try {
+                toBeAdded.add((T) obj);
+            } catch (ClassCastException e) {
+                allAdded = false;
+            }
+        }
+        /*if (toBeAdded.size() == 0){ // TODO andrekim - we should have the ability to create a group with no resources (so we can add and delete entries)
+           return new Status(StatusCode.NOTACCEPTABLE, "Group not created. No valid resources specified");
+        }*/
+        resourceGroups.put(groupName, toBeAdded);
+        return (allAdded ? new Status(StatusCode.SUCCESS, "All resources added succesfully") :
+            new Status(StatusCode.SUCCESS, "One or more resources couldn't be added"));
+    }
+
+    public Status addResourceToGroup(String groupName, T resource) {
+        if (groupName == null || groupName.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Invalid group name");
+        }
+
+        Set<T> group = resourceGroups.get(groupName);
+        if (group != null && resource != null) {
+            group.add(resource);
+            return new Status(StatusCode.SUCCESS, "Resource added successfully");
+        }
+
+        return new Status(StatusCode.NOTFOUND, "Group not found or incompatible resource");
+    }
+
+    public Status removeRoleResourceGroupMapping(String groupName) {
+        List<String> affectedRoles = new ArrayList<String>();
+        Status result;
+        for (Entry<String, Set<ResourceGroup>> pairs : groupsAuthorizations
+                .entrySet()) {
+            String role = pairs.getKey();
+            Set<ResourceGroup> groups = pairs.getValue();
+            for (ResourceGroup group : groups) {
+            if (group.getGroupName().equals(groupName)) {
+            affectedRoles.add(role);
+            break;
+            }
+            }
+        }
+        StringBuffer msg = new StringBuffer();
+        for (String role : affectedRoles) {
+             result = unassignResourceGroupFromRole(groupName, role);
+             if (!result.isSuccess()) {
+                msg.append(result.getDescription());
+                msg.append(' ');
+             }
+         }
+
+        if (msg.length() != 0) {
+                return new Status(StatusCode.BADREQUEST, msg.toString());
+        } else {
+                return new Status(StatusCode.SUCCESS);
+        }
+    }
+
+    @Override
+    public Status removeResourceGroup(String groupName) {
+        // Default resource group cannot be deleted
+        if (groupName == null || groupName.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Invalid group name");
+        }
+        if (groupName.equals(this.allResourcesGroupName)) {
+            return new Status(StatusCode.NOTALLOWED,
+                    "All resource group cannot be removed");
+        }
+
+        Status result = removeRoleResourceGroupMapping(groupName);
+
+        resourceGroups.remove(groupName);
+
+        if (!result.isSuccess()) {
+            return result;
+        }
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+
+    public Status removeResourceFromGroup(String groupName, T resource) {
+        if (groupName == null || groupName.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Invalid group name");
+        }
+
+        Set<T> group = resourceGroups.get(groupName);
+        if (group != null && group.remove(resource)) {
+            return new Status(StatusCode.SUCCESS, "Resource removed successfully");
+        }
+
+        return new Status(StatusCode.NOTFOUND, "Group/Resource not found");
+    }
+
+
+    /**
+     * Relay the call to user manager
+     */
+    @Override
+    public UserLevel getUserLevel(String userName) {
+        IUserManager userManager = (IUserManager) ServiceHelper
+                .getGlobalInstance(IUserManager.class, this);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("User {} has UserLevel {}", userName, userManager.getUserLevel(userName));
+        }
+
+        return (userManager == null) ? UserLevel.NOUSER : userManager
+                .getUserLevel(userName);
+    }
+
+    @Override
+    public Set<Resource> getAllResourcesforUser(String userName) {
+        Set<Resource> resources = new HashSet<Resource>();
+        IUserManager userManager = (IUserManager) ServiceHelper
+                .getGlobalInstance(IUserManager.class, this);
+        if (userManager == null) {
+            return new HashSet<Resource>(0);
+        }
+
+        // Get the roles associated with this user
+        List<String> roles = userManager.getUserRoles(userName);
+        if (roles == null) {
+            return resources;
+        }
+
+        for (String role : roles) {
+            // Get our resource groups associated with this role
+            List<ResourceGroup> groups = this.getAuthorizedGroups(role);
+            if (groups.isEmpty()) {
+                continue;
+            }
+            for (ResourceGroup group : groups) {
+                // Get the list of resources in this group
+                List<Object> list = this.getResources(group.getGroupName());
+                if (list.isEmpty()) {
+                    continue;
+                }
+                for (Object resource : list) {
+                    Resource toBeAdded = new Resource(resource,
+                            group.getPrivilege());
+                    /*
+                     * Add the resource to the set if the same resource with
+                     * higher privilege is not present. If the same resource
+                     * with lower privilege is present, remove it. No check on
+                     * same privilege resource as set guarantees no duplicates.
+                     */
+                    Resource existing = higherPrivilegeResourcePresent(
+                            resources, toBeAdded);
+                    if (existing == null) {
+                        resources.add(toBeAdded);
+                    }
+                    existing = lowerPrivilegeResourcePresent(resources,
+                            toBeAdded);
+                    if (existing != null) {
+                        resources.remove(existing);
+                    }
+                }
+            }
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("User {} has resources {}", userName, resources);
+        }
+
+        return resources;
+    }
+
+    @Override
+    public List<Resource> getAuthorizedResources(String role) {
+        Set<Resource> resources = new HashSet<Resource>();
+
+        // Get our resource groups associated with this role
+        List<ResourceGroup> groups = this.getAuthorizedGroups(role);
+        if (groups.isEmpty()) {
+            return new ArrayList<Resource>(0);
+        }
+        for (ResourceGroup group : groups) {
+            // Get the list of resources in this group
+            List<Object> list = this.getResources(group.getGroupName());
+            if (list.isEmpty()) {
+                continue;
+            }
+            for (Object resource : list) {
+                Resource toBeAdded = new Resource(resource,
+                        group.getPrivilege());
+                /*
+                 * Add the resource to the set if the same resource with higher
+                 * privilege is not present. If the same resource with lower
+                 * privilege is present, remove it. No check on same privilege
+                 * resource as set guarantees no duplicates.
+                 */
+                Resource existing = higherPrivilegeResourcePresent(resources,
+                        toBeAdded);
+                if (existing == null) {
+                    resources.add(toBeAdded);
+                }
+                existing = lowerPrivilegeResourcePresent(resources, toBeAdded);
+                if (existing != null) {
+                    resources.remove(existing);
+                }
+            }
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("For the Role {}, Authorized Resources are {}", role, resources);
+        }
+
+        return new ArrayList<Resource>(resources);
+    }
+
+    /**
+     * Given a set of resources and a resource to test, it returns the element
+     * in the set which represent the same resource with a lower privilege
+     * associated to it, if present.
+     *
+     * @param resources
+     * @param resource
+     * @return
+     */
+    private Resource lowerPrivilegeResourcePresent(Set<Resource> resources,
+            Resource resource) {
+
+        if (resource == null || resources == null) {
+            return null;
+        }
+
+        Object resourceElement = resource.getResource();
+        Privilege resourcePrivilege = resource.getPrivilege();
+        for (Resource element : resources) {
+            if (element.getResource().equals(resourceElement)
+                    && element.getPrivilege().ordinal() < resourcePrivilege
+                            .ordinal()) {
+                return element;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Given a set of resources and a resource to test, it returns the element
+     * in the set which represents the same resource with an higher privilege,
+     * if present.
+     *
+     * @param resources
+     * @param resource
+     * @return
+     */
+    private Resource higherPrivilegeResourcePresent(Set<Resource> resources,
+            Resource resource) {
+
+        if (resource == null || resources == null) {
+            return null;
+        }
+
+        Object resourceElement = resource.getResource();
+        Privilege resourcePrivilege = resource.getPrivilege();
+        for (Resource element : resources) {
+            if (element.getResource().equals(resourceElement)
+                    && element.getPrivilege().ordinal() > resourcePrivilege
+                            .ordinal()) {
+                return element;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public Privilege getResourcePrivilege(String userName, Object resource) {
+
+        if (userName == null || userName.trim().isEmpty() || resource == null) {
+            return Privilege.NONE;
+        }
+
+        Set<Resource> hisResources = getAllResourcesforUser(userName);
+        for (Resource element : hisResources) {
+            if (element.getResource().equals(resource)) {
+                return element.getPrivilege();
+            }
+        }
+
+        return Privilege.NONE;
+    }
+
+    @Override
+    public List<String> getResourceGroups() {
+        return new ArrayList<String>(resourceGroups.keySet());
+    }
+
+    @Override
+    public Status assignResourceGroupToRole(String group, Privilege privilege,
+            String role) {
+        if (group == null || group.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Invalid group name");
+        }
+        if (role == null || role.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
+        }
+        if (isControllerRole(role)) {
+            return new Status(StatusCode.NOTALLOWED,
+                    "No group assignment is accepted for Controller roles");
+        }
+
+        return assignResourceGroupToRoleInternal(group, privilege, role);
+    }
+
+    protected Status assignResourceGroupToRoleInternal(String group,
+            Privilege privilege, String role) {
+        groupsAuthorizations.get(role).add(new ResourceGroup(group, privilege));
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    @Override
+    public Status assignResourceGroupToRole(String groupName, String roleName) {
+        if (groupName == null || groupName.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
+        }
+        if (roleName == null || roleName.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
+        }
+        // Infer group privilege from role's level
+        Privilege privilege = Privilege.NONE;
+        switch (this.getApplicationRoleLevel(roleName)) {
+        case APPADMIN:
+            privilege = Privilege.WRITE;
+            break;
+        case APPUSER:
+            privilege = Privilege.USE;
+            break;
+        case APPOPERATOR:
+            privilege = Privilege.READ;
+            break;
+        default:
+            break;
+        }
+        return this.assignResourceGroupToRole(groupName, privilege, roleName);
+    }
+
+    @Override
+    public Status unassignResourceGroupFromRole(String group, String role) {
+        if (group == null || group.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
+        }
+        if (role == null || role.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
+        }
+        if (isControllerRole(role)) {
+            return new Status(StatusCode.NOTALLOWED,
+                    "No group assignment change is allowed for "
+                            + "Controller roles");
+        }
+
+        return unassignResourceGroupFromRoleInternal(group, role);
+    }
+
+    protected Status unassignResourceGroupFromRoleInternal(String group,
+            String role) {
+        ResourceGroup target = null;
+        for (ResourceGroup rGroup : groupsAuthorizations.get(role)) {
+            if (rGroup.getGroupName().equals(group)) {
+                target = rGroup;
+                break;
+            }
+        }
+        if (target == null) {
+            return new Status(StatusCode.SUCCESS, "Group " + group
+                    + " was not assigned to " + role);
+        } else {
+        groupsAuthorizations.get(role).remove(target);
+        return new Status(StatusCode.SUCCESS);
+
+        }
+    }
+
+    @Override
+    public List<ResourceGroup> getAuthorizedGroups(String role) {
+        return (groupsAuthorizations.containsKey(role)) ? new ArrayList<ResourceGroup>(
+                groupsAuthorizations.get(role))
+                : new ArrayList<ResourceGroup>();
+    }
+
+    @Override
+    public List<Object> getResources(String groupName) {
+        return (resourceGroups.containsKey(groupName)) ? new ArrayList<Object>(
+                resourceGroups.get(groupName)) : new ArrayList<Object>();
+    }
+
+    @Override
+    public boolean isApplicationRole(String roleName) {
+        if (roleName == null) {
+            return false;
+        }
+        return roles.containsKey(roleName);
+    }
+
+    @Override
+    public AppRoleLevel getApplicationRoleLevel(String roleName) {
+        if (roleName == null || roleName.trim().isEmpty()) {
+            return AppRoleLevel.NOUSER;
+        }
+
+        if (isControllerRole(roleName)) {
+            if (roleName.equals(UserLevel.NETWORKADMIN.toString()) ||
+                    roleName.equals(UserLevel.SYSTEMADMIN.toString())) {
+                return AppRoleLevel.APPADMIN;
+            } else {
+                return AppRoleLevel.APPOPERATOR;
+            }
+        }
+
+        return (roles.containsKey(roleName)) ? roles.get(roleName)
+                : AppRoleLevel.NOUSER;
+    }
+
+    @Override
+    public AppRoleLevel getUserApplicationLevel(String userName) {
+        List<String> roles = null;
+        IUserManager userManager = (IUserManager) ServiceHelper
+                .getGlobalInstance(IUserManager.class, this);
+
+        if (userName == null || userName.trim().isEmpty()
+                || (userManager == null)
+                || (roles = userManager.getUserRoles(userName)).isEmpty()) {
+            return AppRoleLevel.NOUSER;
+        }
+        AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
+        for (String role : roles) {
+            AppRoleLevel level = getApplicationRoleLevel(role);
+            if (level.ordinal() < highestLevel.ordinal()) {
+                highestLevel = level;
+            }
+        }
+        return highestLevel;
+    }
+
+    /**
+     * Returns the highest role the specified user has in this application
+     * context
+     *
+     * @param user
+     *            The user name
+     * @return The highest role associated to the user in this application
+     *         context
+     */
+    public String getHighestUserRole(String user) {
+        IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
+        String highestRole = "";
+        if (userManager != null && !userManager.getUserRoles(user).isEmpty()) {
+            List<String> roles = userManager.getUserRoles(user);
+            AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
+            for (String role : roles) {
+                AppRoleLevel current;
+                if (isApplicationRole(role)
+                        && (current = getApplicationRoleLevel(role)).ordinal() < highestLevel.ordinal()) {
+                    highestRole = role;
+                    highestLevel = current;
+                }
+            }
+        }
+        return highestRole;
+    }
+
+    private boolean isControllerRole(String role) {
+        return (role.equals(UserLevel.NETWORKADMIN.toString())
+                || role.equals(UserLevel.SYSTEMADMIN.toString()) || role
+                    .equals(UserLevel.NETWORKOPERATOR.toString()));
+    }
+
+    private boolean isRoleInUse(String role) {
+        IUserManager userManager = (IUserManager) ServiceHelper
+                .getGlobalInstance(IUserManager.class, this);
+
+        if (userManager == null) {
+            return true;
+        }
+        return userManager.isRoleInUse(role);
+    }
+}
index 50d6f5c..0fadb12 100644 (file)
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
-      <artifactId>containermanager.implementation</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <artifactId>containermanager.it.implementation</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
   </dependencies>
   <properties>
index 8cbebe5..5c8b096 100644 (file)
@@ -103,7 +103,7 @@ public class ClusteringServicesIT {
                         "sal.implementation").versionAsInProject(),\r
             mavenBundle("org.opendaylight.controller", "containermanager").versionAsInProject(),\r
             mavenBundle("org.opendaylight.controller",\r
-                        "containermanager.implementation").versionAsInProject(),\r
+                        "containermanager.it.implementation").versionAsInProject(),\r
             mavenBundle("org.jboss.spec.javax.transaction",\r
                         "jboss-transaction-api_1.1_spec").versionAsInProject(),\r
             mavenBundle("org.apache.commons", "commons-lang3").versionAsInProject(),\r
index 51ff2a6..19a1e66 100644 (file)
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
-      <artifactId>containermanager.implementation</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <artifactId>containermanager.it.implementation</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index a2f2297..b0e351a 100644 (file)
@@ -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(),
index 65adf68..2b207aa 100644 (file)
@@ -15,7 +15,7 @@
   </scm>
 
   <artifactId>containermanager</artifactId>
-  <version>0.4.0-SNAPSHOT</version>
+  <version>0.5.0-SNAPSHOT</version>
   <packaging>bundle</packaging>
 
   <build>
         <configuration>
           <instructions>
             <Import-Package>
-              org.opendaylight.controller.sal.action,
               org.opendaylight.controller.sal.authorization,
+              org.opendaylight.controller.sal.utils,
               org.opendaylight.controller.sal.core,
+              org.opendaylight.controller.sal.action,
               org.opendaylight.controller.sal.flowprogrammer,
               org.opendaylight.controller.sal.match,
+              org.opendaylight.controller.sal.packet,
               org.opendaylight.controller.sal.reader,
-              org.opendaylight.controller.sal.utils,
-              org.apache.commons.lang3.builder
+              org.apache.commons.lang3.builder,
+              org.slf4j,
+              javax.xml.bind.annotation,
+              javax.xml.bind
             </Import-Package>
             <Export-Package>
               org.opendaylight.controller.containermanager
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 (file)
index 0000000..7e040c7
--- /dev/null
@@ -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 (file)
index 0000000..01715cc
--- /dev/null
@@ -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<String> ports;
+
+    @XmlElement(name = "flowSpecs")
+    private List<ContainerFlowConfig> containerFlows;
+
+    public String getContainer() {
+        return container;
+    }
+
+    public void setContainer(String container) {
+        this.container = container;
+    }
+
+    public String getStaticVlan() {
+        return staticVlan;
+    }
+
+    public void setStaticVlan(String staticVlan) {
+        this.staticVlan = staticVlan;
+    }
+
+    public List<ContainerFlowConfig> getContainerFlows() {
+        return containerFlows;
+    }
+
+    public void setContainerFlows(List<ContainerFlowConfig> containerFlows) {
+        this.containerFlows = containerFlows;
+    }
+
+    public static long getSerialversionuid() {
+        return serialVersionUID;
+    }
+
+    public static String getRegexname() {
+        return regexName;
+    }
+
+    public void setPorts(List<String> ports) {
+        this.ports = ports;
+    }
+
+    /**
+     * Default constructor needed by Gson.
+     *
+     * @return a Default ContainerConfig
+     */
+    public ContainerConfig() {
+        this.container = null;
+        this.staticVlan = null;
+        this.ports = new ArrayList<String>(0);
+        this.containerFlows = new ArrayList<ContainerFlowConfig>(0);
+    }
+
+    /**
+     * Constructor for the ContainerConfig.
+     *
+     * @param container
+     *            Name of the container in this configuration
+     * @param vlan
+     *            vlan assigned to this container
+     * @param nodeName
+     *            the name of the node assigned to the container from this
+     *            configuration
+     * @param ports
+     *            the list of NodeConnectors on the Node belonging to the container
+     * @return the constructed object
+     */
+    public ContainerConfig(String container, String vlan, List<String> portList, List<ContainerFlowConfig> containerFlows) {
+        this.container = container;
+        this.staticVlan = vlan;
+        this.ports = (portList == null) ? new ArrayList<String>(0) : new ArrayList<String>(portList);
+        this.containerFlows = (containerFlows == null) ? new ArrayList<ContainerFlowConfig>(0)
+                : new ArrayList<ContainerFlowConfig>(containerFlows);
+    }
+
+    public ContainerConfig(ContainerConfig config) {
+        this.container = config.container;
+        this.staticVlan = config.staticVlan;
+        this.ports = (config.ports == null) ? new ArrayList<String>(0) : new ArrayList<String>(config.ports);
+        this.containerFlows = (config.containerFlows == null) ? new ArrayList<ContainerFlowConfig>(0)
+                : new ArrayList<ContainerFlowConfig>(config.containerFlows);
+    }
+
+    /**
+     * Returns the container name.
+     *
+     * @return the container name
+     */
+    public String getContainerName() {
+        return container;
+    }
+
+    /**
+     * Returns the Vlan tag.
+     *
+     * @return the Vlan Tag configured for this container configuration
+     */
+    public String getVlanTag() {
+        return staticVlan;
+    }
+
+    /**
+     * Returns the configured ports.
+     *
+     * @return the string with the list of ports associated to the container on this
+     *         configuration
+     */
+    public List<String> getPorts() {
+        return new ArrayList<String>(ports);
+    }
+
+    /**
+     * Returns the list of container flows configured for this container
+     *
+     * @return
+     */
+    public List<ContainerFlowConfig> getContainerFlowConfigs() {
+        return (containerFlows == null || containerFlows.isEmpty()) ? new ArrayList<ContainerFlowConfig>(0)
+                : new ArrayList<ContainerFlowConfig>(containerFlows);
+    }
+
+    /**
+     * Matches container name against passed parameter.
+     *
+     * @param name
+     *            name of the container to be matched
+     * @return true if the passed argument correspond with the container name in the
+     *         configuration
+     */
+    public boolean matchName(String name) {
+        return this.container.equals(name);
+    }
+
+    /**
+     * Parse the port list in several NodeConnector descriptor.
+     *
+     * @return the list of NodeConnector corresponding to the ports configured
+     *         on this configuration
+     */
+    public List<NodeConnector> getPortList() {
+        List<NodeConnector> portList = new ArrayList<NodeConnector>();
+        if (ports != null && !ports.isEmpty()) {
+            for (String portString : ports) {
+                portList.add(NodeConnector.fromString(portString));
+            }
+        }
+        return portList;
+    }
+
+    /**
+     * Checks if this is a valid container configuration
+     *
+     * @return true, if is valid container configuration, false otherwise
+     */
+    public Status validate() {
+        Status status = validateName();
+        if (status.isSuccess()) {
+            status = validateStaticVlan();
+            if (status.isSuccess()) {
+                status = validatePorts();
+                if (status.isSuccess()) {
+                    status = validateContainerFlows();
+                }
+            }
+        }
+        return status;
+    }
+
+    /**
+     * Checks for valid name.
+     *
+     * @return true, if successful
+     */
+    private Status validateName() {
+        // No Container configuration allowed to container default
+        return (container.matches(regexName) && !container.equalsIgnoreCase(GlobalConstants.DEFAULT.toString())) ?
+                new Status(StatusCode.SUCCESS) : new Status(StatusCode.BADREQUEST, "Invalid container name");
+    }
+
+    /**
+     * Checks for valid ports.
+     *
+     * @return true, if successful
+     */
+    private Status validatePorts() {
+        return validateNodeConnectors(this.ports);
+    }
+
+    public static Status validateNodeConnectors(List<String> connectorList) {
+        if (connectorList != null && !connectorList.isEmpty()) {
+            for (String ncString : connectorList) {
+                if (NodeConnector.fromString(ncString) == null) {
+                    return new Status(StatusCode.BADREQUEST, "Invalid node connector: " + ncString);
+                }
+            }
+
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    public static List<NodeConnector> nodeConnectorsFromString(List<String> nodeConnectorStrings) {
+        List<NodeConnector> list = new ArrayList<NodeConnector>(nodeConnectorStrings.size());
+        for (String str : nodeConnectorStrings) {
+            list.add(NodeConnector.fromString(str));
+        }
+        return list;
+    }
+
+    /**
+     * Checks for valid static vlan.
+     *
+     * @return true, if successful
+     */
+    private Status validateStaticVlan() {
+        if (staticVlan != null && !staticVlan.trim().isEmpty()) {
+            short vl = 0;
+            try {
+                vl = Short.valueOf(staticVlan);
+            } catch (NumberFormatException e) {
+                return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
+            }
+            if ((vl < 1) || (vl > 4095)) {
+                return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
+            }
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    private Status validateContainerFlows() {
+        if (containerFlows != null && !containerFlows.isEmpty()) {
+            for (ContainerFlowConfig conf : containerFlows) {
+                Status status = conf.validate();
+                if (!status.isSuccess()) {
+                    return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec: " + status.getDescription());
+                }
+            }
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    /**
+     * Returns Vlan value in short
+     *
+     * @return the Vlan tag
+     */
+    public short getStaticVlanValue() {
+        if ((staticVlan == null) || (staticVlan.trim().isEmpty())) {
+            return 0;
+        }
+        try {
+            return Short.valueOf(staticVlan);
+        } catch (NumberFormatException e) {
+            return 0;
+        }
+    }
+
+    public Status addNodeConnectors(List<String> ncList) {
+        // Syntax check
+        Status status = ContainerConfig.validateNodeConnectors(ncList);
+        if (!status.isSuccess()) {
+            return status;
+        }
+
+        /* Allow adding ports which are already present
+        if (!ports.isEmpty()) {
+            List<String> intersection = new ArrayList<String>(ports);
+            intersection.retainAll(ncList);
+            if (!intersection.isEmpty()) {
+                return new Status(StatusCode.CONFLICT, "The following node connectors are already part of this container: "
+                        + intersection);
+            }
+        }
+        */
+
+        // Add ports
+        ports.addAll(ncList);
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    public Status removeNodeConnectors(List<String> ncList) {
+        // Syntax check
+        Status status = ContainerConfig.validateNodeConnectors(ncList);
+        if (!status.isSuccess()) {
+            return status;
+        }
+        // Presence check
+        if (ports.isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "The following node connectors are not part of this container: "
+                    + ncList);
+        }
+        List<String> extra = new ArrayList<String>(ncList);
+        extra.removeAll(ports);
+        if (!extra.isEmpty()) {
+            return new Status(StatusCode.CONFLICT, "The following node connectors are not part of this container: " + extra);
+        }
+        // Remove ports
+        ports.removeAll(ncList);
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    public Status validateContainerFlowModify(List<ContainerFlowConfig> cFlowConfigs, boolean delete) {
+        // Sanity Check
+        if (cFlowConfigs == null || cFlowConfigs.isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec configuration(s): null or empty list");
+        }
+        // Validity check
+        for (ContainerFlowConfig cFlowConf : cFlowConfigs) {
+            Status status = cFlowConf.validate();
+            if (!status.isSuccess()) {
+                return new Status(StatusCode.BADREQUEST, String.format("Invalid Flow Spec configuration (%s): %s",
+                        cFlowConf.getName(), status.getDescription()));
+            }
+        }
+        // Name conflict check
+        List<String> existingNames = this.getContainerFlowConfigsNames();
+        List<String> proposedNames = this.getContainerFlowConfigsNames(cFlowConfigs);
+
+        // Check for duplicates in the request
+        if (proposedNames.size() < cFlowConfigs.size()) {
+            return new Status(StatusCode.BADREQUEST,
+                    "Invalid Flow Spec configuration(s): duplicate name configs present");
+        }
+
+        // Check for overflow
+        if (delete) {
+            // Raw size check
+            if (proposedNames.size() > existingNames.size()) {
+                return new Status(StatusCode.BADREQUEST,
+                        "Invalid request: requested to remove more flow spec configs than available ones");
+            }
+            // Presence check
+            for (ContainerFlowConfig config : cFlowConfigs) {
+                if (!this.containerFlows.contains(config)) {
+                    return new Status(StatusCode.BADREQUEST, String.format(
+                            "Invalid request: requested to remove nonexistent flow spec config: %s",
+                            config.getName()));
+                }
+            }
+        }
+        /*
+         * Revisit the following flow-spec confict validation later based on more testing.
+         */
+         /*
+        if (!delete) {
+            // Check for overlapping container flows in the request
+            int size = cFlowConfigs.size();
+            for (int i = 0; i < size; i++) {
+                ContainerFlowConfig first = cFlowConfigs.get(i);
+                for (int j = i + 1; j < size; j++) {
+                    ContainerFlowConfig second = cFlowConfigs.get(j);
+                    if (first.overlap(second)) {
+                        return new Status(StatusCode.BADREQUEST, String.format(
+                                "Invalid Request: the proposed flow specs overlap: %s <-> %s", first.getName(),
+                                second.getName()));
+                    }
+                }
+            }
+            // Check if any of the proposed container flows overlap with the
+            // existing ones
+            for (ContainerFlowConfig current : cFlowConfigs) {
+                for (ContainerFlowConfig existing : this.containerFlows) {
+                    if (current.overlap(existing)) {
+                        return new Status(StatusCode.BADREQUEST, String.format(
+                                "Invalid Request: the proposed flow specs overlap: %s <-> %s", current.getName(),
+                                existing.getName()));
+                    }
+                }
+            }
+        }
+        */
+
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    public ContainerFlowConfig getContainerFlowConfig(String name) {
+        if (this.containerFlows != null && !this.containerFlows.isEmpty()) {
+            for (ContainerFlowConfig conf : this.containerFlows) {
+                if (conf.getName().equals(name)) {
+                    return new ContainerFlowConfig(conf);
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<String> getContainerFlowConfigsNames() {
+        return getContainerFlowConfigsNames(this.containerFlows);
+    }
+
+    /**
+     * Returns the list of unique names for the passed list of
+     * ContainerFlowConfig objects. the list will not contain duplicates even
+     * though the passed object list has ContainerFlowConfig objects with same
+     * names
+     *
+     * @param confList
+     *            the list of ContainerFlowConfig objects
+     * @return the list of correspondent unique container flow names. The return
+     *         list may differ from the passed list in size, if the latter
+     *         contains duplicates
+     */
+    private List<String> getContainerFlowConfigsNames(List<ContainerFlowConfig> confList) {
+        // Use set to check for duplicates later
+        Set<String> namesSet = new HashSet<String>();
+        if (confList != null) {
+            for (ContainerFlowConfig conf : confList) {
+                namesSet.add(conf.getName());
+            }
+        }
+        return new ArrayList<String>(namesSet);
+    }
+
+    /**
+     * Add the proposed list of container flow configurations to this container
+     * configuration. A validation check on the operation is first run.
+     *
+     * @param containerFlowConfigs
+     *            the proposed list of container flow configuration objects to
+     *            add to this container configuration object
+     * @return the result of this request as Status object
+     */
+    public Status addContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
+        Status status = this.validateContainerFlowModify(containerFlowConfigs, false);
+        if (!status.isSuccess()) {
+            return status;
+        }
+        if (this.containerFlows.addAll(containerFlowConfigs) == false) {
+            return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    public Status removeContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
+        Status status = this.validateContainerFlowModify(containerFlowConfigs, true);
+        if (!status.isSuccess()) {
+            return status;
+        }
+        if (this.containerFlows.removeAll(containerFlowConfigs) == false) {
+            return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    public Status removeContainerFlows(Set<String> names) {
+        // Sanity check
+        if (names == null || names.isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Invalid flow spec names list");
+        }
+        // Validation check
+        List<String> present = this.getContainerFlowConfigsNames();
+        if (!present.containsAll(names)) {
+            List<String> notPresent = new ArrayList<String>(names);
+            notPresent.retainAll(present);
+            return new Status(StatusCode.BADREQUEST, "Following flow spec(s) are not present: " + notPresent);
+        }
+        // Remove
+        List<ContainerFlowConfig> toDelete = new ArrayList<ContainerFlowConfig>(names.size());
+        for (ContainerFlowConfig config : this.containerFlows) {
+            if (names.contains(config.getName())) {
+                toDelete.add(config);
+            }
+        }
+        if (this.containerFlows.removeAll(toDelete) == false) {
+            return new Status(StatusCode.INTERNALERROR, "Unable to remove the flow spec configuration(s)");
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    public List<ContainerFlowConfig> getContainerFlowConfigs(Set<String> names) {
+        List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>(names.size());
+        for (String name : names) {
+            ContainerFlowConfig conf = this.getContainerFlowConfig(name);
+            if (conf != null) {
+                list.add(new ContainerFlowConfig(conf));
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((containerFlows == null) ? 0 : containerFlows.hashCode());
+        result = prime * result + ((ports == null) ? 0 : ports.hashCode());
+        result = prime * result + ((container == null) ? 0 : container.hashCode());
+        result = prime * result + ((staticVlan == null) ? 0 : staticVlan.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        ContainerConfig other = (ContainerConfig) obj;
+        if (containerFlows == null) {
+            if (other.containerFlows != null) {
+                return false;
+            }
+        } else if (!containerFlows.equals(other.containerFlows)) {
+            return false;
+        }
+        if (ports == null) {
+            if (other.ports != null) {
+                return false;
+            }
+        } else if (!ports.equals(other.ports)) {
+            return false;
+        }
+        if (container == null) {
+            if (other.container != null) {
+                return false;
+            }
+        } else if (!container.equals(other.container)) {
+            return false;
+        }
+        if (staticVlan == null) {
+            if (other.staticVlan != null) {
+                return false;
+            }
+        } else if (!staticVlan.equals(other.staticVlan)) {
+            return false;
+        }
+        return true;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        String vlString = "";
+        if (staticVlan != null) {
+            vlString = staticVlan;
+        }
+        return "container=" + container + ((vlString.equals("") ? "" : " static Vlan=" + vlString)) + " ports=" + ports + " flowspecs=" + containerFlows;
+    }
+
+    /**
+     * Returns whether this Container configuration object has any ports specified
+     *
+     * @return true if any port is specified, false otherwise
+     */
+    public boolean hasNodeConnectors() {
+        return (ports != null && !ports.isEmpty());
+    }
+
+    /**
+     * Returns whether this Container configuration object has any flow specs specified
+     *
+     * @return true if any flow spec is specified, false otherwise
+     */
+    public boolean hasFlowSpecs() {
+        return (containerFlows != null && !containerFlows.isEmpty());
+    }
+
+    public List<ContainerFlow> getContainerFlowSpecs() {
+        List<ContainerFlow> list = new ArrayList<ContainerFlow>();
+        if (containerFlows != null && !containerFlows.isEmpty()) {
+            for (ContainerFlowConfig flowSpec : containerFlows) {
+                for (Match match : flowSpec.getMatches()) {
+                    list.add(new ContainerFlow(match));
+                }
+            }
+        }
+        return list;
+    }
+}
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 (file)
index 0000000..5a7cdd2
--- /dev/null
@@ -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<Node, Set<NodeConnector>> swPorts;
+    private short staticVlan;
+    private List<ContainerFlow> cFlowList;
+
+    /**
+     * Default constructor
+
+     *
+     * @return constructed ContainerData
+     */
+    public ContainerData() {
+        name = null;
+        swPorts = new ConcurrentHashMap<Node, Set<NodeConnector>>();
+        staticVlan = 0;
+        cFlowList = new ArrayList<ContainerFlow>();
+    }
+
+    /**
+     * Build a ContainerData from container configuration
+     *
+     * @param conf configuration from where the ContainerData need to be extracted
+     *
+     * @return the constructed containerData
+     */
+    public ContainerData(ContainerConfig conf) {
+        /*
+         * Back-end Non-Configuration Classes store names in lower case
+         */
+        name = conf.getContainerName().toLowerCase(Locale.ENGLISH);
+        swPorts = new ConcurrentHashMap<Node, Set<NodeConnector>>();
+        cFlowList = new ArrayList<ContainerFlow>();
+        staticVlan = conf.getStaticVlanValue();
+    }
+
+    /**
+     * getter for containerName
+     *
+     *
+     * @return the container name
+     */
+    public String getContainerName() {
+        return name;
+    }
+
+    /**
+     * getter for static vlan
+     *
+     *
+     * @return attribute static vlan
+     */
+    public short getStaticVlan() {
+        return staticVlan;
+    }
+
+    /**
+     * Check if the static vlan has non-zero value in that case is
+     * assumed to be assigned
+     *
+     *
+     * @return true if static vlan non-zero
+     */
+    public boolean hasStaticVlanAssigned() {
+        return (staticVlan != 0);
+    }
+
+    /**
+     * retrieve all NodeConnectors on a given switch on the containerName
+     * used in this object
+     *
+     * @param switchId the Node for switch we want to retrieve the list of ports
+     *
+     * @return get all the ports in form of a set of nodeconnectors
+     */
+    public Set<NodeConnector> getPorts(Node switchId) {
+        Set<NodeConnector> swpinfo = swPorts.get(switchId);
+        if (swpinfo != null) {
+            return swpinfo;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the DB of all the association Node -> List of
+     * NodeConnectors composing this container
+     *
+     * @return the ConcurrentMap of all the association Node -> List
+     * of NodeConnectors
+     */
+    public ConcurrentMap<Node, Set<NodeConnector>> getSwPorts() {
+        return swPorts;
+    }
+
+    /**
+     * Add to the container a NodeConnector, that implicitely means also
+     * the Node being added
+     *
+     * @param port NodeConnector corresponding to the port being added
+     * in the container
+     */
+    public void addPortToSwitch(NodeConnector port) {
+        Node switchId = port.getNode();
+        if (switchId == null) {
+            return;
+        }
+        Set<NodeConnector> portSet = swPorts.get(switchId);
+        if (portSet == null) {
+            portSet = new HashSet<NodeConnector>();
+            swPorts.put(switchId, portSet);
+        }
+        portSet.add(port);
+    }
+
+    /**
+     * Remove from the container a NodeConnector, that implicitely means also
+     * the Node being removed
+     *
+     * @param port NodeConnector corresponding to the port being removed
+     * in the container
+     */
+    public void removePortFromSwitch(NodeConnector port) {
+        Node switchId = port.getNode();
+        if (switchId == null) {
+            return;
+        }
+        Set<NodeConnector> swpinfo = swPorts.get(switchId);
+        if (swpinfo != null) {
+            swpinfo.remove(port);
+            if (swpinfo.isEmpty()) {
+                // There are no more ports lets get ride of the switch
+                swPorts.remove(switchId);
+            }
+        }
+    }
+
+    /**
+     * Existence check of a Node in a container
+     *
+     * @param switchId See if a particular node is part of the container
+     *
+     * @return true if the node is in it else false
+     */
+    public boolean isSwitchInContainer(Node switchId) {
+        if (swPorts.containsKey(switchId)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Test for existance of NodeConnectors on a Node in a container
+     *
+     * @param switchId Node we are testing for port existance
+     *
+     * @return ture if the portset is non-empty
+     */
+    public boolean portListEmpty(Node switchId) {
+        Set<NodeConnector> swpinfo = swPorts.get(switchId);
+        if (swpinfo != null) {
+            if (!swpinfo.isEmpty()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public void deleteFlowSpec(ContainerFlow cFlow) {
+        cFlowList.remove(cFlow);
+    }
+
+    public void addFlowSpec(ContainerFlow cFlow) {
+        cFlowList.add(cFlow);
+    }
+
+    public boolean isFlowSpecEmpty() {
+        return (cFlowList.isEmpty());
+
+    }
+
+    public boolean containsFlowSpec(ContainerFlow cFlow) {
+        return cFlowList.contains(cFlow);
+    }
+
+    public int getFlowSpecCount() {
+        return cFlowList.size();
+    }
+
+    public List<ContainerFlow> getContainerFlowSpecs() {
+        return cFlowList;
+    }
+
+    public boolean matchName(String name) {
+        return this.name.equalsIgnoreCase(name);
+    }
+
+    /**
+     * Returns all the node connectors part of the container
+     * @return The node connectors belonging to this container. The returning set is never null.
+     */
+    public Set<NodeConnector> getNodeConnectors() {
+        Set<NodeConnector> set = new HashSet<NodeConnector>();
+        for (Map.Entry<Node, Set<NodeConnector>> entry : swPorts.entrySet()) {
+            set.addAll(entry.getValue());
+        }
+        return set;
+    }
+}
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 (file)
index 0000000..7405a72
--- /dev/null
@@ -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<ContainerFlowConfig> configs;
+    private UpdateType updateType;
+
+    public ContainerFlowChangeEvent(List<ContainerFlowConfig> configs, UpdateType updateType) {
+        this.configs = configs;
+        this.updateType = updateType;
+    }
+
+    public List<ContainerFlowConfig> getConfigList() {
+        return configs;
+    }
+
+    public UpdateType getUpdateType() {
+        return updateType;
+    }
+
+    @Override
+    public String toString() {
+        return "ContainerFlowChangeEvent [configs: " + configs + " updateType: " + updateType + "]";
+    }
+}
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 (file)
index 0000000..c0b0a65
--- /dev/null
@@ -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<Match> thisMatch = this.getMatches();
+        List<Match> otherMatch = that.getMatches();
+        // both the lists cannot be null
+        for(Match m1 : thisMatch) {
+            for(Match m2 : otherMatch) {
+                  if(m1.equals(m2)) {
+                    return true;
+                }
+            }
+        }
+        // if you have reached here without a match
+        // being found
+        // return false
+        return false;
+    }
+
+    /**
+     * Matches the name of this flow spec with that of ContainerFlowConfig parameter's flow spec.
+     *
+     * @param o the ContainerFlowConfig parameter
+     * @return true, if successful
+     */
+    private boolean matchName(ContainerFlowConfig flowSpec) {
+        if (name == flowSpec.name) {
+            return true;
+        }
+        if (name == null || flowSpec.name == null) {
+            return false;
+        }
+        return name.equals(flowSpec.name);
+    }
+
+
+    /**
+     * Match Source IP Address.
+     *
+     * @param flowSpec Flow Specification
+     * @return true, if successful
+     */
+    private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
+        if (nwSrc == flowSpec.nwSrc) {
+            return true;
+        }
+        if (nwSrc == null || flowSpec.nwSrc == null) {
+            return false;
+        }
+        return nwSrc.equals(flowSpec.nwSrc);
+    }
+
+    /**
+     * Match Destination IP Address.
+     *
+     * @param flowSpec Flow Specification
+     * @return true, if successful
+     */
+    private boolean matchDstIP(ContainerFlowConfig flowSpec) {
+        if (nwDst == flowSpec.nwDst) {
+            return true;
+        }
+        if (nwDst == null || flowSpec.nwDst == null) {
+            return false;
+        }
+        return this.nwDst.equals(flowSpec.nwDst);
+    }
+
+    /**
+     * Match protocol.
+     *
+     * @param flowSpec Flow Specification
+     * @return true, if successful
+     */
+    private boolean matchProtocol(ContainerFlowConfig flowSpec) {
+        if (protocol == flowSpec.protocol) {
+            return true;
+        }
+        if (protocol == null || flowSpec.protocol == null) {
+            return false;
+        }
+        return this.protocol.equals(flowSpec.protocol);
+    }
+
+    /**
+     * Match Source Layer4 Port.
+     *
+     * @param flowSpec Flow Specification
+     * @return true, if successful
+     */
+    private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
+        if (tpSrc == flowSpec.tpSrc) {
+            return true;
+        }
+        if (tpSrc == null || flowSpec.tpSrc == null) {
+            return false;
+        }
+        return tpSrc.equals(flowSpec.tpSrc);
+    }
+
+    /**
+     * Match Destination Layer4 Port.
+     *
+     * @param flowSpec Flow Specification
+     * @return true, if successful
+     */
+    private boolean matchDstPort(ContainerFlowConfig flowSpec) {
+        if (tpDst == flowSpec.tpDst) {
+            return true;
+        }
+        if (tpDst == null || flowSpec.tpDst == null) {
+            return false;
+        }
+        return this.tpDst.equals(flowSpec.tpDst);
+    }
+
+    /**
+     * Returns the Source IP Address mask length.
+     *
+     * @return the Source IP Address mask length
+     */
+    public Short getSrcIPMaskLen() {
+        Short maskLen = 0;
+
+        if (nwSrc != null && !nwSrc.isEmpty()) {
+            String[] s = nwSrc.split("/");
+            if (s.length == 2) {
+                try {
+                    maskLen = Short.valueOf(s[1]);
+                } catch (Exception e) {
+                    // no mask or bad mask
+                }
+            } else {
+                InetAddress ip = this.getSrcIPNum();
+                maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
+            }
+        }
+        return maskLen;
+    }
+
+    /**
+     * Returns the Destination IP Address mask length.
+     *
+     * @return the Destination IP Address mask length
+     */
+    public Short getDstIPMaskLen() {
+        Short maskLen = 0;
+        if (nwDst != null && !nwDst.isEmpty()) {
+            String[] s = nwDst.split("/");
+            if (s.length == 2) {
+                try {
+                    maskLen = Short.valueOf(s[1]);
+                } catch (Exception e) {
+                    // no mask or bad mask
+                }
+            } else {
+                InetAddress ip = this.getDstIPNum();
+                maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
+            }
+        }
+        return maskLen;
+    }
+
+    /**
+     * Returns the Source IP Address.
+     *
+     * @return the Source IP Address
+     */
+    public InetAddress getSrcIPNum() {
+        InetAddress ip = null;
+        if (nwSrc == null || nwSrc.isEmpty()) {
+            try {
+                ip = InetAddress.getByAddress(new byte[16]);
+                return ip;
+            } catch (UnknownHostException e) {
+                log.error("", e);
+                return null;
+            }
+        }
+        try {
+            ip = InetAddress.getByName(nwSrc.split("/")[0]);
+        } catch (UnknownHostException e1) {
+            log.error("", e1);
+            return null;
+        }
+        return ip;
+    }
+
+    /**
+     * Returns the Destination IP Address.
+     *
+     * @return the Destination IP Address
+     */
+    public InetAddress getDstIPNum() {
+        InetAddress ip = null;
+        if (nwDst == null || nwDst.isEmpty()) {
+            try {
+                ip = InetAddress.getByAddress(new byte[16]);
+                return ip;
+            } catch (UnknownHostException e) {
+                log.error("",e);
+                return null;
+            }
+        }
+        try {
+            ip = InetAddress.getByName(nwDst.split("/")[0]);
+        } catch (UnknownHostException e1) {
+            log.error("", e1);
+            return null;
+        }
+        return ip;
+    }
+
+    /**
+     * Returns Source Layer4 Port number.
+     *
+     * @return Source Layer4 Port number
+     */
+    public Short getSrcPortNum() {
+        return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
+                : Short.valueOf(tpSrc);
+    }
+
+    /**
+     * Returns Destination Layer4 Port number.
+     *
+     * @return Destination Layer4 Port number
+     */
+    public Short getDstPortNum() {
+        return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
+                : Short.valueOf(tpDst);
+    }
+
+    /**
+     * Returns the protocol
+     *
+     * @return the protocol
+     */
+    public Short getProtoNum() {
+        return protocol == null ? IPProtocols.ANY.shortValue() : IPProtocols.getProtocolNumberShort(protocol);
+    }
+
+    /**
+     * Returns whether this container flow overlap with the passed one This is
+     * true when any two of the resulting matches for the two container flow
+     * configurations intersect.
+     *
+     * @param other
+     *            the other container flow config with which checking the
+     *            overlap
+     * @return true if the two configurations overlap, false otherwise
+     */
+    public boolean overlap(ContainerFlowConfig other) {
+        if (other == null) {
+            return false;
+        }
+        List<Match> myMathes = this.getMatches();
+        List<Match> hisMatches = other.getMatches();
+        for (Match mine : myMathes) {
+            for (Match his : hisMatches) {
+                if (mine.intersetcs(his)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if this flow specification configuration is valid.
+     *
+     * @return true, if is valid
+     */
+    public Status validate() {
+        if (!hasValidName()) {
+            return new Status(StatusCode.BADREQUEST, "Invalid name");
+        }
+        Status status = validateIPs();
+        if (!status.isSuccess()) {
+            return status;
+        }
+        if(!hasValidProtocol()) {
+            return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
+        }
+        if (this.getMatches().get(0).getMatches() == 0) {
+            return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    /**
+     * Checks if this flow specification configuration has a valid name.
+     *
+     * @return true, if successful
+     */
+    private boolean hasValidName() {
+        return (name != null && !name.isEmpty() && name.matches(regexName));
+    }
+
+    /**
+     * Validates the network addresses, checks syntax and semantic
+     *
+     * @return the result of the check as Status object, if successful
+     */
+    private Status validateIPs() {
+        if (nwSrc != null) {
+            if (!NetUtils.isIPAddressValid(nwSrc)) {
+                return new Status(StatusCode.BADREQUEST, "Invalid network source address");
+            }
+            byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
+            long prefix = BitBufferHelper.getLong(bytePrefix);
+            if (prefix == 0) {
+                return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
+            }
+        }
+        if (nwDst != null) {
+            if (!NetUtils.isIPAddressValid(nwDst)) {
+                return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
+            }
+            byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
+            long prefix = BitBufferHelper.getLong(bytePrefix);
+            if (prefix == 0) {
+                return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
+            }
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    private boolean hasValidProtocol() {
+        if (protocol != null && !protocol.isEmpty()) {
+            return (this.getProtoNum() != 0 || protocol.equalsIgnoreCase("any"));
+        }
+        return true;
+    }
+    /**
+     * Returns the matches.
+     * If unidirectional flag is set, there will be only one match in the list
+     * If unidirectional flag is unset there will be two matches in the list,
+     * only if the specified flow has an intrinsic direction.
+     * For Ex. if the cFlow only has the protocol field configured, no matter
+     * if unidirectional flag is set or not, only one match will be returned
+     * The client just has to iterate over the returned list
+     * @return the matches
+     */
+    public List<Match> getMatches() {
+        List<Match> matches = new ArrayList<Match>();
+        Match match = new Match();
+
+        if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
+            String parts[] = this.nwSrc.split("/");
+            InetAddress ip = NetUtils.parseInetAddress(parts[0]);
+            InetAddress mask = null;
+            int maskLen = 0;
+            if (parts.length > 1) {
+                maskLen = Integer.parseInt(parts[1]);
+            } else {
+                maskLen = (ip instanceof Inet6Address) ? 128 : 32;
+            }
+            mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
+            match.setField(MatchType.NW_SRC, ip, mask);
+        }
+        if (this.nwDst != null && !this.nwDst.trim().isEmpty()) {
+            String parts[] = this.nwDst.split("/");
+            InetAddress ip = NetUtils.parseInetAddress(parts[0]);
+            InetAddress mask = null;
+            int maskLen = 0;
+            if (parts.length > 1) {
+                maskLen = Integer.parseInt(parts[1]);
+            } else {
+                maskLen = (ip instanceof Inet6Address) ? 128 : 32;
+            }
+            mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
+            match.setField(MatchType.NW_DST, ip, mask);
+        }
+        if (this.protocol != null && !this.protocol.trim().isEmpty() && !this.protocol.equalsIgnoreCase("any")) {
+            match.setField(MatchType.NW_PROTO, IPProtocols
+                    .getProtocolNumberByte(this.protocol));
+        }
+        if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
+            Short srcPort = 0;
+            try {
+                srcPort = Short.parseShort(tpSrc);
+            } catch (NumberFormatException e) {
+                throw e;
+            }
+            match.setField(MatchType.TP_SRC, srcPort);
+        }
+        if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
+            Short dstPort = 0;
+            try {
+                dstPort = Short.parseShort(tpDst);
+            } catch (NumberFormatException e) {
+                throw e;
+            }
+            match.setField(MatchType.TP_DST, dstPort);
+        }
+
+        matches.add(match);
+        if(!ContainerFlowConfig.unidirectional) {
+            Match reverse = match.reverse();
+            if (!match.equals(reverse)) {
+                matches.add(reverse);
+            }
+        }
+        return matches;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "Container Flow={name:" + name + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
+                + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";
+    }
+}
index 9300921..5daaf00 100644 (file)
 package org.opendaylight.controller.containermanager;
 
 import java.util.List;
-
+import java.util.Map;
+import java.util.Set;
 import org.opendaylight.controller.sal.utils.Status;
 
 /**
- * Container Manager Interface - provides methods to get information on existing OSGI containers
- *
+ * Container Manager provides an ability for the Network Administrators to
+ * partitions a production network into smaller, isolated and manageable
+ * networks. IContainerManager interface exposes these Container management capabilities
+ * via the supported APIs
  */
 public interface IContainerManager {
 
+    /**
+     * Create a Container
+     *
+     * @param configObject
+     *            ContainerConfig object that carries name of the Container to be
+     *            created
+     * @return returns the status code of adding a container
+     */
+    public Status addContainer(ContainerConfig configObject);
+
+    /**
+     * Remove a container
+     *
+     * @param configObject
+     *            ContainerConfig object that carries the name of the Container to be
+     *            removed
+     * @return returns the status code of removing a container
+     */
+    public Status removeContainer(ContainerConfig configObject);
+
+    /**
+     * Remove a container
+     *
+     * @param containerName
+     *            the container name
+     * @return returns the status code of removing a container
+     */
+    public Status removeContainer(String containerName);
+
+    /**
+     * Adds resources to a given container. Updates the container data based on new
+     * resources added
+     *
+     * @param configObject
+     *            refer to {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+     *            ContainerConfig}
+     * @return returns the status code of adding a container entry
+     */
+    public Status addContainerEntry(String containerName, List<String> portList);
+
+    /**
+     * Remove a resource from a given container. Updates the container data based on new
+     * resources removed.
+     *
+     * @param configObject
+     *            refer to {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+     *            ContainerConfig}
+     * @return returns the status code of removing a container entry
+     */
+    public Status removeContainerEntry(String containerName, List<String> portList);
+
+    /**
+     * Adds/Removes a container flow
+     *
+     * @param configObject
+     *            refer to {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+     *            ContainerConfig}
+     * @return returns the status code of adding a container flow
+     */
+    public Status addContainerFlows(String containerName, List<ContainerFlowConfig> configObject);
+
+    /**
+     * Remove a container flow
+     *
+     * @param configObject
+     *            refer to {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+     *            ContainerConfig}
+     * @return returns the status of removing a container flow
+     */
+    public Status removeContainerFlows(String containerName, List<ContainerFlowConfig> configObject);
+
+    /**
+     * Remove a container flow
+     *
+     * @param containerName
+     *            the name of the container
+     * @param name
+     *            the name of the container flow
+     * @return the status of the request
+     */
+    public Status removeContainerFlows(String containerName, Set<String> name);
+
+    /**
+     * Get the list of {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+     * ContainerConfig} objects representing all the containers that have been
+     * configured previously.
+     *
+     * @return the lsit of {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+     *         ContainerConfig} objects configured so far
+     */
+    public List<ContainerConfig> getContainerConfigList();
+
+    /**
+     * Get the configuration object for the specified container
+     *
+     * @param containerName
+     *            the name of the container
+     * @return a copy of the {@link com.ContainerConfig.csdn.containermanager.ContainerConfig
+     *         ContainerConfig} object for the specified container if present, null
+     *         otherwise
+     */
+    public ContainerConfig getContainerConfig(String containerName);
+
+    /**
+     * Returns a list of container names that currently exist.
+     *
+     * @return array of String container names
+     */
+    public List<String> getContainerNameList();
+
+    /**
+     * Check for the existence of a container
+     *
+     * @param ContainerId
+     *            Name of the Container
+     *
+     * @return true if it exists, false otherwise
+     */
+    public boolean doesContainerExist(String ContainerId);
+
+    /**
+     * Get an array of ContainerFlowConfig objects representing all the
+     * container flows that have been configured previously.
+     *
+     * @return array of {@link org.opendaylight.controller.containermanager.ContainerFlowConfig
+     *         ContainerFlowConfig}
+     */
+    public Map<String, List<ContainerFlowConfig>> getContainerFlows();
+
+    /**
+     * Get an array of {@link org.opendaylight.controller.containermanager.ContainerFlowConfig
+     * ContainerFlowConfig} objects representing all the container flows that
+     * have been configured previously on the given containerName
+     *
+     * @param containerName
+     *            the container name
+     * @return array of {@link org.opendaylight.controller.containermanager.ContainerFlowConfig
+     *         ContainerFlowConfig}
+     */
+    public List<ContainerFlowConfig> getContainerFlows(String containerName);
+
+    /**
+     * Get an the list of names of the container flows that have been configured
+     * previously on the given containerName
+     *
+     * @param containerName
+     *            the container name
+     * @return the array containing the names of the container flows configured
+     *         on the specified container
+     */
+    public List<String> getContainerFlowNameList(String containerName);
+
     /**
      * Returns true if there are any non-default Containers present.
      *
@@ -32,14 +187,4 @@ public interface IContainerManager {
      * @return  List of Container name strings.
      */
     public List<String> getContainerNames();
-
-    /**
-     * Save the current container configuration to disk.
-     * TODO : REMOVE THIS FUNCTION and make Save as a service rather than the
-     * current hack of calling individual save routines.
-     *
-     * @return  status code
-     */
-    @Deprecated
-    public Status saveContainerConfig();
 }
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 (file)
index 0000000..5f992c7
--- /dev/null
@@ -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<NodeConnector> ncList;
+    private UpdateType updateType;
+
+    public NodeConnectorsChangeEvent(List<NodeConnector> ncList, UpdateType updateType) {
+        this.ncList = ncList;
+        this.updateType = updateType;
+    }
+
+    public List<NodeConnector> getNodeConnectors() {
+        return ncList;
+    }
+
+    public UpdateType getUpdateType() {
+        return updateType;
+    }
+
+    @Override
+    public String toString() {
+        return "ContainerConnectorsChangeEvent [ncList: " + ncList + " updateType: " + updateType + "]";
+    }
+}
index f6909ca..5af8e22 100644 (file)
@@ -15,7 +15,7 @@
   </scm>
 
   <artifactId>containermanager.implementation</artifactId>
-  <version>0.4.0-SNAPSHOT</version>
+  <version>0.5.0-SNAPSHOT</version>
   <packaging>bundle</packaging>
 
   <build>
           <instructions>
             <Import-Package>
               org.opendaylight.controller.containermanager,
+              org.opendaylight.controller.appauth.authorization,
+              org.opendaylight.controller.usermanager,
+              org.opendaylight.controller.configuration,
               org.opendaylight.controller.clustering.services,
+              org.opendaylight.controller.sal.authorization,
               org.opendaylight.controller.sal.packet,
               org.opendaylight.controller.sal.utils,
               org.opendaylight.controller.sal.core,
@@ -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,
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>configuration</artifactId>
+      <version>0.4.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>appauth</artifactId>
+      <version>0.4.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>topologymanager</artifactId>
       <version>0.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <artifactId>sal</artifactId>
       <version>0.5.0-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>usermanager</artifactId>
+      <version>0.4.0-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index 9caa620..aab07af 100644 (file)
 
 package org.opendaylight.controller.containermanager.internal;
 
-import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Hashtable;
 import org.opendaylight.controller.containermanager.IContainerManager;
-import org.opendaylight.controller.sal.core.IContainerAware;
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.containermanager.IContainerAuthorization;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
 import org.opendaylight.controller.sal.core.IContainer;
+import org.opendaylight.controller.sal.core.IContainerAware;
 import org.opendaylight.controller.sal.core.IContainerListener;
-import org.apache.felix.dm.Component;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
 
 public class Activator extends ComponentActivatorAbstractBase {
-    protected static final Logger logger = LoggerFactory
-            .getLogger(Activator.class);
+    protected static final Logger logger = LoggerFactory.getLogger(Activator.class);
 
     /**
-     * Function called when the activator starts just after some
-     * initializations are done by the
-     * ComponentActivatorAbstractBase.
+     * Function called when the activator starts just after some initializations
+     * are done by the ComponentActivatorAbstractBase.
      *
      */
+    @Override
     public void init() {
     }
 
     /**
-     * Function called when the activator stops just before the
-     * cleanup done by ComponentActivatorAbstractBase
+     * Function called when the activator stops just before the cleanup done by
+     * ComponentActivatorAbstractBase
      *
      */
+    @Override
     public void destroy() {
     }
 
     /**
-     * Function that is used to communicate to dependency manager the
-     * list of known implementations for services inside a container
+     * Function that is used to communicate to dependency manager the list of
+     * known implementations for containerd-services
      *
      *
      * @return An array containing all the CLASS objects that will be
-     * instantiated in order to get an fully working implementation
-     * Object
+     *         instantiated in order to get an fully working implementation
+     *         Object
      */
+    @Override
     public Object[] getImplementations() {
         Object[] res = { ContainerImpl.class };
         return res;
     }
 
     /**
-     * Function that is called when configuration of the dependencies
-     * is required.
+     * Function that is called when configuration of the dependencies is
+     * required.
      *
-     * @param c dependency manager Component object, used for
-     * configuring the dependencies exported and imported
-     * @param imp Implementation class that is being configured,
-     * needed as long as the same routine can configure multiple
-     * implementations
-     * @param containerName The containerName being configured, this allow
-     * also optional per-container different behavior if needed, usually
-     * should not be the case though.
+     * @param c
+     *            dependency manager Component object, used for configuring the
+     *            dependencies exported and imported
+     * @param imp
+     *            Implementation class that is being configured, needed as long
+     *            as the same routine can configure multiple implementations
+     * @param containerName
+     *            The containername being configured, this allow also optional
+     *            per-container different behavior if needed, usually should not be
+     *            the case though.
      */
+    @Override
     public void configureInstance(Component c, Object imp, String containerName) {
         if (imp.equals(ContainerImpl.class)) {
             // export the service
             c.setInterface(new String[] { IContainer.class.getName() }, null);
+
+            // private interface exported by ContainerManager to retrieve
+            // internal data, this is mandatory to implement the
+            // IContainer functionality
+            c.add(createServiceDependency().setService(IContainerInternal.class)
+                    .setCallbacks("setIContainerInternal", "unsetIContainerInternal")
+                    .setRequired(true));
         }
     }
 
     /**
-     * Method which tells how many Global implementations are
-     * supported by the bundle. This way we can tune the number of
-     * components created. This components will be created ONLY at the
-     * time of bundle startup and will be destroyed only at time of
-     * bundle destruction, this is the major difference with the
-     * implementation retrieved via getImplementations where all of
-     * them are assumed to be in a container!
+     * Method which tells how many NON-containerd implementations are supported by
+     * the bundle. This way we can tune the number of components created. This
+     * components will be created ONLY at the time of bundle startup and will be
+     * destroyed only at time of bundle destruction, this is the major
+     * difference with the implementation retrieved via getImplementations where
+     * all of them are assumed to be containerd!
      *
      *
-     * @return The list of implementations the bundle will support,
-     * in Global version
+     * @return The list of implementations the bundle will support, in
+     *         non-containerd version
      */
+    @Override
     protected Object[] getGlobalImplementations() {
         Object[] res = { ContainerManager.class };
         return res;
     }
 
     /**
-     * Configure the dependency for a given instance Global
+     * Configure the dependency for a given instance non-containerd
      *
-     * @param c Component assigned for this instance, this will be
-     * what will be used for configuration
-     * @param imp implementation to be configured
-     * @param containerName container on which the configuration happens
+     * @param c
+     *            Component assigned for this instance, this will be what will
+     *            be used for configuration
+     * @param imp
+     *            implementation to be configured
+     * @param containerName
+     *            container on which the configuration happens
      */
+    @Override
     protected void configureGlobalInstance(Component c, Object imp) {
         if (imp.equals(ContainerManager.class)) {
+            Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
+            Set<String> propSet = new HashSet<String>();
+            propSet.add("containermgr.event.containerChange");
+            props.put("cachenames", propSet);
 
             // export the service
-            c.setInterface(new String[] { IContainerManager.class.getName() },
-                    null);
+            c.setInterface(
+                    new String[] { IContainerManager.class.getName(),
+                            IContainerManager.class.getName(),
+                            IConfigurationAware.class.getName(),
+                            CommandProvider.class.getName(),
+                            IContainerInternal.class.getName(),
+                            IContainerAuthorization.class.getName(),
+                            ICacheUpdateAware.class.getName()}, props);
 
-            c.add(createServiceDependency().setService(
-                    IClusterGlobalServices.class).setCallbacks(
-                    "setClusterServices", "unsetClusterServices").setRequired(
-                    true));
+            c.add(createServiceDependency()
+                    .setService(IClusterGlobalServices.class)
+                    .setCallbacks("setClusterServices", "unsetClusterServices")
+                    .setRequired(true));
 
             // Key kick-starter for container creation in each component
             c.add(createServiceDependency().setService(IContainerAware.class)
@@ -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));
         }
     }
index 15f939d..1dfab88 100644 (file)
  * @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<ContainerFlow> getContainerFlows() {
-        return null;
+        List<ContainerFlow> list = new ArrayList<ContainerFlow>();
+
+        ContainerData d = this.iContainerInternal.getContainerData(this.containerName);
+        if (d != null) {
+            list.addAll(d.getContainerFlowSpecs());
+        }
+        return list;
     }
 
     @Override
     public short getTag(Node n) {
+        ContainerData d = this.iContainerInternal.getContainerData(this.containerName);
+        if (d != null) {
+            return d.getStaticVlan();
+        }
+        // Return 0 because in containerData that means an unassigned tag
         return (short) 0;
     }
 
     @Override
     public Set<NodeConnector> getNodeConnectors() {
-        return null;
+        Set<NodeConnector> set = new HashSet<NodeConnector>();
+
+        ContainerData d = this.iContainerInternal.getContainerData(this.containerName);
+        if (d != null) {
+            ConcurrentMap<Node, Set<NodeConnector>> m = d.getSwPorts();
+            if (m != null) {
+                for (Map.Entry<Node, Set<NodeConnector>> entry : m.entrySet()) {
+                    set.addAll(entry.getValue());
+                }
+            }
+        }
+        return set;
     }
 
     @Override
     public Set<Node> getNodes() {
-        return null;
+        Set<Node> set = new HashSet<Node>();
+
+        ContainerData d = this.iContainerInternal.getContainerData(this.containerName);
+        if (d != null) {
+            ConcurrentMap<Node, Set<NodeConnector>> m = d.getSwPorts();
+            if (m != null) {
+                set.addAll(m.keySet());
+            }
+        }
+        return set;
     }
 }
index d47aca8..da301ba 100644 (file)
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-/**
- * @file   ContainerManager.java
- *
- * @brief  Manage one or many Containers
- *
- *
- */
 package org.opendaylight.controller.containermanager.internal;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.configuration.IConfigurationService;
+import org.opendaylight.controller.containermanager.IContainerAuthorization;
 import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.sal.authorization.AppRoleLevel;
+import org.opendaylight.controller.sal.authorization.Privilege;
+import org.opendaylight.controller.sal.authorization.Resource;
+import org.opendaylight.controller.sal.authorization.ResourceGroup;
+import org.opendaylight.controller.sal.authorization.UserLevel;
+import org.opendaylight.controller.sal.core.ContainerFlow;
 import org.opendaylight.controller.sal.core.IContainerAware;
 import org.opendaylight.controller.sal.core.IContainerListener;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.match.Match;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.IObjectReader;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.opendaylight.controller.sal.utils.ObjectReader;
+import org.opendaylight.controller.sal.utils.ObjectWriter;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
 import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.appauth.authorization.Authorization;
+import org.opendaylight.controller.containermanager.ContainerFlowChangeEvent;
+import org.opendaylight.controller.containermanager.ContainerFlowConfig;
+import org.opendaylight.controller.containermanager.NodeConnectorsChangeEvent;
+import org.opendaylight.controller.containermanager.ContainerChangeEvent;
+import org.opendaylight.controller.containermanager.ContainerConfig;
+import org.opendaylight.controller.containermanager.ContainerData;
 
-public class ContainerManager implements IContainerManager {
-    private static final Logger logger = LoggerFactory
-            .getLogger(ContainerManager.class);
+public class ContainerManager extends Authorization<String> implements IContainerManager, IObjectReader,
+        CommandProvider, ICacheUpdateAware<String, Object>, IContainerInternal, IContainerAuthorization,
+        IConfigurationAware {
+    private static final Logger logger = LoggerFactory.getLogger(ContainerManager.class);
+    private static String ROOT = GlobalConstants.STARTUPHOME.toString();
+    private static String containersFileName = ROOT + "containers.conf";
+    private static final String allContainersGroup = "allContainers";
     private IClusterGlobalServices clusterServices;
     /*
-     * Collection containing the configuration objects.
-     * This is configuration world: container names (also the map key)
-     * are maintained as they were configured by user, same case
+     * Collection containing the configuration objects. This is configuration
+     * world: container names (also the map key) are maintained as they were
+     * configured by user, same case
      */
-    private Set<IContainerAware> iContainerAware = (Set<IContainerAware>) Collections
-            .synchronizedSet(new HashSet<IContainerAware>());
-    private Set<IContainerListener> iContainerListener = Collections
+    private ConcurrentMap<String, ContainerConfig> containerConfigs;
+    private ConcurrentMap<String, ContainerData> containerData;
+    private ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>> nodeConnectorToContainers;
+    private ConcurrentMap<Node, Set<String>> nodeToContainers;
+    private ConcurrentMap<String, Object> containerChangeEvents;
+    private final Set<IContainerAware> iContainerAware = Collections.synchronizedSet(new HashSet<IContainerAware>());
+    private final Set<IContainerListener> iContainerListener = Collections
             .synchronizedSet(new HashSet<IContainerListener>());
 
     void setIContainerListener(IContainerListener s) {
         if (this.iContainerListener != null) {
             this.iContainerListener.add(s);
+            /*
+             * At boot with startup, containers are created before listeners have
+             * joined. Replaying here the first container creation notification for
+             * the joining listener when containers are already present. Also
+             * replaying all the node connectors and container flows additions
+             * to the existing containers.
+             */
+            if (!this.containerData.isEmpty()) {
+                s.containerModeUpdated(UpdateType.ADDED);
+            }
+            for (ConcurrentMap.Entry<NodeConnector, CopyOnWriteArrayList<String>> entry : nodeConnectorToContainers
+                    .entrySet()) {
+                NodeConnector port = entry.getKey();
+                for (String container : entry.getValue()) {
+                    s.nodeConnectorUpdated(container, port, UpdateType.ADDED);
+                }
+            }
+            for (Map.Entry<String, ContainerData> container : containerData.entrySet()) {
+                for (ContainerFlow cFlow : container.getValue().getContainerFlowSpecs()) {
+                    s.containerFlowUpdated(container.getKey(), cFlow, cFlow, UpdateType.ADDED);
+                }
+            }
         }
     }
 
@@ -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<String> containerDB = getContainerNames();
-            if (containerDB != null) {
-                for (int i = 0; i < containerDB.size(); i++) {
-                    iContainerAware.containerCreate(containerDB.get(i));
-                }
+            // Now call the container creation for all the known containers so far
+            for (String container : getContainerNameList()) {
+                iContainerAware.containerCreate(container.toLowerCase(Locale.ENGLISH));
             }
         }
     }
@@ -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<String, ContainerConfig>) clusterServices.getCache("containermgr.containerConfigs");
+
+        containerChangeEvents = (ConcurrentMap<String, Object>) clusterServices.getCache("containermgr.event.containerChange");
+
+        containerData = (ConcurrentMap<String, ContainerData>) clusterServices.getCache("containermgr.containerData");
+
+        nodeConnectorToContainers = (ConcurrentMap<NodeConnector, CopyOnWriteArrayList<String>>) clusterServices
+                .getCache("containermgr.nodeConnectorToContainers");
+
+        nodeToContainers = (ConcurrentMap<Node, Set<String>>) clusterServices.getCache("containermgr.nodeToContainers");
+
+        resourceGroups = (ConcurrentMap<String, Set<String>>) clusterServices.getCache("containermgr.containerGroups");
+
+        groupsAuthorizations = (ConcurrentMap<String, Set<ResourceGroup>>) clusterServices
+                .getCache("containermgr.containerAuthorizations");
+
+        roles = (ConcurrentMap<String, AppRoleLevel>) clusterServices.getCache("containermgr.roles");
+
+        if (containerConfigs.size() > 0) {
+            for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+                notifyContainerChangeInternal(entry.getValue(), UpdateType.ADDED);
+            }
+        }
+    }
+
+    @Override
+    public void entryCreated(String containerName, String cacheName, boolean originLocal) {
+
+    }
+
+    @Override
+    public void entryUpdated(String key, Object value, String cacheName, boolean originLocal) {
+        if (!originLocal) {
+            if (value instanceof NodeConnectorsChangeEvent) {
+                NodeConnectorsChangeEvent event = (NodeConnectorsChangeEvent) value;
+                List<NodeConnector> ncList = event.getNodeConnectors();
+                notifyContainerEntryChangeInternal(key, ncList, event.getUpdateType());
+            } else if (value instanceof ContainerFlowChangeEvent) {
+                ContainerFlowChangeEvent event = (ContainerFlowChangeEvent) value;
+                notifyCFlowChangeInternal(key, event.getConfigList(), event.getUpdateType());
+            } else if (value instanceof ContainerChangeEvent) {
+                ContainerChangeEvent event = (ContainerChangeEvent) value;
+                notifyContainerChangeInternal(event.getConfig(), event.getUpdateType());
+            }
+        }
+    }
+
+    @Override
+    public void entryDeleted(String containerName, String cacheName, boolean originLocal) {
+    }
+
+    public ContainerManager() {
+    }
+
     public void init() {
-        logger.info("ContainerManager startup....");
+
+    }
+
+    public void start() {
+        // Get caches from cluster manager
+        allocateCaches();
+        retrieveCaches();
+
+        // Allocates default groups and association to default roles
+        createDefaultAuthorizationGroups();
+
+        // Read startup configuration and create local database
+        loadConfigurations();
     }
 
     public void destroy() {
         // Clear local states
         this.iContainerAware.clear();
         this.iContainerListener.clear();
+    }
+
+    /**
+     * Adds/Remove the list of flow specs to/from the specified container. This
+     * function is supposed to be called after all the validation checks have
+     * already been run on the proposed configuration.
+     */
+    private Status updateContainerFlow(String containerName, List<ContainerFlowConfig> confList, boolean delete) {
+        ContainerData container = getContainerByName(containerName);
+        if (container == null) {
+            return new Status(StatusCode.GONE, "Container not present");
+        }
+
+        for (ContainerFlowConfig conf : confList) {
+            // Validation was fine. Modify the database now.
+            for (Match match : conf.getMatches()) {
+                ContainerFlow cFlow = new ContainerFlow(match);
+                if (delete) {
+                    logger.trace("Removing Flow Spec %s from Container {}", conf.getName(), containerName);
+                    container.deleteFlowSpec(cFlow);
+                } else {
+                    logger.trace("Adding Flow Spec %s to Container {}", conf.getName(), containerName);
+                    container.addFlowSpec(cFlow);
+
+                }
+                // Update Database
+                putContainerDataByName(containerName, container);
+            }
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    /**
+     * Adds/Remove this container to/from the Container database, no updates are going
+     * to be generated here other that the destroying and creation of the container.
+     * This function is supposed to be called after all the validation checks
+     * have already been run on the configuration object
+     */
+    private Status updateContainerDatabase(ContainerConfig containerConf, boolean delete) {
+        /*
+         * Back-end world here, container names are all stored in lower case
+         */
+        String containerName = containerConf.getContainerName();
+        ContainerData container = getContainerByName(containerName);
+        if (delete && container == null) {
+            return new Status(StatusCode.NOTFOUND, "Container is not present");
+        }
+        if (!delete && container != null) {
+            // A container with the same (lower case) name already exists
+            return new Status(StatusCode.CONFLICT, "A container with the same name already exists");
+        }
+        if (delete) {
+            logger.debug("Removing container {}", containerName);
+            removeNodeToContainersMapping(container);
+            removeNodeConnectorToContainersMapping(container);
+            removeContainerDataByName(containerName);
+        } else {
+            logger.debug("Adding container {}", containerName);
+            container = new ContainerData(containerConf);
+            putContainerDataByName(containerName, container);
+
+            // If flow specs are specified, add them
+            if (containerConf.hasFlowSpecs()) {
+                updateContainerFlow(containerName, containerConf.getContainerFlowConfigs(), delete);
+            }
+
+            // If ports are specified, add them
+            if (!containerConf.getPortList().isEmpty()) {
+                updateContainerEntryDatabase(containerName, containerConf.getPortList(), delete);
+            }
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    private void removeNodeConnectorToContainersMapping(ContainerData container) {
+        Iterator<Entry<NodeConnector, CopyOnWriteArrayList<String>>> it = nodeConnectorToContainers.entrySet().iterator();
+        String containerName = container.getContainerName();
+        for (; it.hasNext();) {
+            Entry<NodeConnector, CopyOnWriteArrayList<String>> entry = it.next();
+            final NodeConnector nc = entry.getKey();
+            final CopyOnWriteArrayList<String> slist = entry.getValue();
+            for (final String sdata : slist) {
+                if (sdata.equalsIgnoreCase(containerName)) {
+                    logger.debug("Removing NodeConnector->Containers mapping, nodeConnector: {}", nc);
+                    slist.remove(containerName);
+                    if (slist.isEmpty()) {
+                        nodeConnectorToContainers.remove(nc);
+                    } else {
+                        nodeConnectorToContainers.put(nc, slist);
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    private void removeNodeToContainersMapping(ContainerData container) {
+        for (Entry<Node, Set<String>> entry : nodeToContainers.entrySet()) {
+            Node node = entry.getKey();
+            for (String sdata : entry.getValue()) {
+                if (sdata.equals(container.getContainerName())) {
+                    logger.debug("Removing Node->Containers mapping, node {} container {}", node, sdata);
+                    Set<String> value = nodeToContainers.get(node);
+                    value.remove(sdata);
+                    nodeToContainers.put(node, value);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds/Remove container data to/from the container. This function is supposed to be
+     * called after all the validation checks have already been run on the
+     * configuration object
+     */
+    private Status updateContainerEntryDatabase(String containerName, List<NodeConnector> nodeConnectors, boolean delete) {
+        ContainerData container = getContainerByName(containerName);
+        // Presence check
+        if (container == null) {
+            return new Status(StatusCode.NOTFOUND, "Container Not Found");
+        }
+
+        // Check changes in the portlist
+        for (NodeConnector port : nodeConnectors) {
+            Node node = port.getNode();
+            if (delete) {
+                container.removePortFromSwitch(port);
+                putContainerDataByName(containerName, container);
+
+                /* remove <sp> - container mapping */
+                if (nodeConnectorToContainers.containsKey(port)) {
+                    nodeConnectorToContainers.remove(port);
+                }
+                /*
+                 * If no more ports in the switch, remove switch from container
+                 * Generate switchRemoved Event
+                 */
+                if (container.portListEmpty(node)) {
+                    logger.debug("Port List empty for switch {}", node);
+                    putContainerDataByName(containerName, container);
+                    // remove node->containers mapping
+                    Set<String> slist = nodeToContainers.get(node);
+                    if (slist != null) {
+                        logger.debug("Removing container from switch-container list. node{}, container{}", node, containerName);
+                        slist.remove(container.getContainerName());
+                        nodeToContainers.put(node, slist);
+                        if (slist.isEmpty()) {
+                            logger.debug("Container list empty for switch {}. removing switch-container mapping", node);
+                            nodeToContainers.remove(node);
+                        }
+                    }
+                }
+            } else {
+                if (container.isSwitchInContainer(node) == false) {
+                    Set<String> value = nodeToContainers.get(node);
+                    // Add node->containers mapping
+                    if (value == null) {
+                        value = new HashSet<String>();
+                        logger.debug("Creating new Container Set for switch {}", node);
+                    }
+                    value.add(container.getContainerName());
+                    nodeToContainers.put(node, value);
+                }
+                container.addPortToSwitch(port);
+                putContainerDataByName(containerName, container);
+
+                // added nc->containers mapping
+                CopyOnWriteArrayList<String> slist = nodeConnectorToContainers.get(port);
+                if (slist == null) {
+                    slist = new CopyOnWriteArrayList<String>();
+                }
+                slist.add(container.getContainerName());
+                nodeConnectorToContainers.put(port, slist);
+            }
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    private Status validateContainerFlowAddRemoval(String containerName, ContainerFlow cFlow, boolean delete) {
+        /*
+         * It used to be the comment below: ~~~~~~~~~~~~~~~~~~~~ If Link Sharing
+         * at Host facing interfaces, then disallow last ContainerFlow removal
+         * ~~~~~~~~~~~~~~~~~~~~ But the interface being host facing is a
+         * condition that can change at runtime and so the final effect will be
+         * unreliable. So now we will always allow the container flow removal,
+         * if this is a link host facing and is shared by many that will cause
+         * issues but that validation should be done not at the configuration
+         * but in the UI/northbound side.
+         */
+        ContainerData container = this.getContainerByName(containerName);
+        if (container == null) {
+            String error = String.format("Cannot validate flow specs for container %s: (Container does not exist)", containerName);
+            logger.warn(error);
+            return new Status(StatusCode.BADREQUEST, error);
+        }
+
+        if (delete) {
+            Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
+            // Go through all the installed containers
+            for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
+                if (containerName.equalsIgnoreCase(entry.getKey())) {
+                    continue;
+                }
+                // Derive the common ports
+                Set<NodeConnector> commonPorts = entry.getValue().getNodeConnectors();
+                commonPorts.retainAll(thisContainerPorts);
+                if (commonPorts.isEmpty()) {
+                    continue;
+                }
+
+                // Check if this operation would remove the only flow spec
+                // assigned to this container
+                if (container.getFlowSpecCount() == 1) {
+                    if (!container.hasStaticVlanAssigned()) {
+                        // Ports are shared and static vlan is not present: this
+                        // is a failure
+                        // regardless the shared ports are host facing or
+                        // interswitch ports
+                        return new Status(StatusCode.BADREQUEST, "Container shares port with another container: "
+                                + "The only one flow spec assigned to this container cannot be removed,"
+                                + "because this container is not assigned any static vlan");
+                    }
+
+                    // Check on host facing port
+                    ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(
+                            ITopologyManager.class, GlobalConstants.DEFAULT.toString(), this);
+                    if (topologyManager == null) {
+                        return new Status(StatusCode.NOSERVICE,
+                                "Cannot validate the request: Required service is not available");
+                    }
+                    for (NodeConnector nc : commonPorts) {
+                        /*
+                         * Shared link case : For internal port check if it has
+                         * a vlan configured. If vlan is configured, allow the
+                         * flowspec to be deleted If the port is host-facing, do
+                         * not allow the flowspec to be deleted
+                         */
+                        if (!topologyManager.isInternal(nc)) {
+                            return new Status(StatusCode.BADREQUEST, String.format(
+                                    "Port %s is shared and is host facing port: "
+                                            + "The only one flow spec assigned to this container cannot be removed", nc));
+                        }
+                    }
+                }
+            }
+        } else {
+            // Adding a new flow spec: need to check if other containers with common
+            // ports do not have same flow spec
+            Set<NodeConnector> thisContainerPorts = container.getNodeConnectors();
+            List<ContainerFlow> proposed = new ArrayList<ContainerFlow>(container.getContainerFlowSpecs());
+            proposed.add(cFlow);
+            for (Map.Entry<String, ContainerData> entry : containerData.entrySet()) {
+                if (containerName.equalsIgnoreCase(entry.getKey())) {
+                    continue;
+                }
+                ContainerData otherContainer = entry.getValue();
+                Set<NodeConnector> commonPorts = otherContainer.getNodeConnectors();
+                commonPorts.retainAll(thisContainerPorts);
+
+                if (!commonPorts.isEmpty()) {
+                    Status status = checkCommonContainerFlow(otherContainer.getContainerFlowSpecs(), proposed);
+                    if (!status.isSuccess()) {
+                        return new Status(StatusCode.BADREQUEST, String.format(
+                                "Container %s which shares ports with this container has overlapping flow spec: %s",
+                                entry.getKey(), status.getDescription()));
+                    }
+                }
+            }
+        }
+
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    /**
+     * Checks if the passed list of node connectors can be safely applied to the
+     * specified existing container in terms of port sharing with other containers.
+     *
+     * @param containerName
+     *            the name of the existing container
+     * @param portList
+     *            the list of node connectors to be added to the container
+     * @return the status object representing the result of the check
+     */
+    private Status validatePortSharing(String containerName, List<NodeConnector> portList) {
+        ContainerData container = this.getContainerByName(containerName);
+        if (container == null) {
+            String error = String
+                    .format("Cannot validate port sharing for container %s: (container does not exist)", containerName);
+            logger.error(error);
+            return new Status(StatusCode.BADREQUEST, error);
+        }
+        return validatePortSharingInternal(portList, container.getContainerFlowSpecs());
+    }
+
+    /**
+     * Checks if the proposed container configuration is valid to be applied in
+     * terms of port sharing with other containers.
+     *
+     * @param containerConf
+     *            the container configuration object containing the list of node
+     *            connectors
+     * @return the status object representing the result of the check
+     */
+    private Status validatePortSharing(ContainerConfig containerConf) {
+        return validatePortSharingInternal(containerConf.getPortList(), containerConf.getContainerFlowSpecs());
+    }
+
+    /*
+     * If any port is shared with an existing container, need flowSpec to be
+     * configured. If no flowSpec for this or other container, or if containers have any
+     * overlapping flowspec in common, then let the caller know this
+     * configuration has to be rejected.
+     */
+    private Status validatePortSharingInternal(List<NodeConnector> portList, List<ContainerFlow> flowSpecList) {
+        for (NodeConnector port : portList) {
+            List<String> slist = nodeConnectorToContainers.get(port);
+            if (slist != null && !slist.isEmpty()) {
+                for (String otherContainerName : slist) {
+                    String msg = null;
+                    ContainerData other = containerData.get(otherContainerName);
+                    if (flowSpecList.isEmpty()) {
+                        msg = String.format("Port %s is shared and flow spec is emtpy for this container", port);
+                    } else if (other.isFlowSpecEmpty()) {
+                        msg = String.format("Port %s is shared and flow spec is emtpy for the other container", port);
+                    } else if (!checkCommonContainerFlow(flowSpecList, other.getContainerFlowSpecs()).isSuccess()) {
+                        msg = String.format("Port %s is shared and other container has common flow spec", port);
+                    }
+                    if (msg != null) {
+                        logger.debug(msg);
+                        return new Status(StatusCode.BADREQUEST, msg);
+                    }
+                }
+            }
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    /**
+     * Utility function to check if two lists of container flows share any same
+     * or overlapping container flows.
+     *
+     * @param oneFlowList
+     *            One of the two lists of container flows to test
+     * @param twoFlowList
+     *            One of the two lists of container flows to test
+     * @return The status of the check. Either SUCCESS or CONFLICT. In case of
+     *         conflict, the Status will contain the description for the failed
+     *         check.
+     */
+    private Status checkCommonContainerFlow(List<ContainerFlow> oneFlowList, List<ContainerFlow> twoFlowList) {
+        for (ContainerFlow oneFlow : oneFlowList) {
+            for (ContainerFlow twoFlow : twoFlowList) {
+                if (oneFlow.getMatch().intersetcs(twoFlow.getMatch())) {
+                    return new Status(StatusCode.CONFLICT, String.format("Flow Specs overlap: %s %s",
+                            oneFlow.getMatch(), twoFlow.getMatch()));
+                }
+            }
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
 
-        logger.info("ContainerManager Shutdown....");
+    /**
+     * Return the ContainerData object for the passed container name. Given this is a
+     * backend database, the lower case version of the passed name is used while
+     * searching for the corresponding ContainerData object.
+     *
+     * @param name
+     *            The container name in any case
+     * @return The corresponding ContainerData object
+     */
+    private ContainerData getContainerByName(String name) {
+        return containerData.get(name.toLowerCase(Locale.ENGLISH));
+    }
+
+    /**
+     * Add a ContainerData object for the given container name.
+     *
+     * @param name
+     *            The container name in any case
+     * @param sData
+     *            The container data object
+     */
+    private void putContainerDataByName(String name, ContainerData sData) {
+        containerData.put(name.toLowerCase(Locale.ENGLISH), sData);
+    }
+
+    /**
+     * Removes the ContainerData object for the given container name.
+     *
+     * @param name
+     *            The container name in any case
+     */
+    private void removeContainerDataByName(String name) {
+        containerData.remove(name.toLowerCase(Locale.ENGLISH));
     }
 
     @Override
-    public List<String> getContainerNames() {
+    public List<ContainerConfig> getContainerConfigList() {
+        return new ArrayList<ContainerConfig>(containerConfigs.values());
+    }
+
+    @Override
+    public ContainerConfig getContainerConfig(String containerName) {
+        ContainerConfig target = containerConfigs.get(containerName);
+        return (target == null) ? null : new ContainerConfig(target);
+    }
+
+    @Override
+    public List<String> getContainerNameList() {
         /*
          * Return container names as they were configured by user (case sensitive)
          * along with the default container
          */
         List<String> containerNameList = new ArrayList<String>();
         containerNameList.add(GlobalConstants.DEFAULT.toString());
+        containerNameList.addAll(containerConfigs.keySet());
         return containerNameList;
     }
 
     @Override
-    public boolean hasNonDefaultContainer() {
-        return false;
+    public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
+        Map<String, List<ContainerFlowConfig>> flowSpecConfig = new HashMap<String, List<ContainerFlowConfig>>();
+        for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+            List<ContainerFlowConfig> set = entry.getValue().getContainerFlowConfigs();
+            flowSpecConfig.put(entry.getKey(), set);
+        }
+        return flowSpecConfig;
+    }
+
+    private void loadConfigurations() {
+        /*
+         * Read containers, container flows and finally containers' entries from file
+         * and program the database accordingly
+         */
+        if (containerConfigs.isEmpty()) {
+            loadContainerConfig();
+        }
+    }
+
+    private Status saveContainerConfig() {
+        return saveContainerConfigLocal();
+    }
+
+    public Status saveContainerConfigLocal() {
+        ObjectWriter objWriter = new ObjectWriter();
+
+        Status status = objWriter.write(new ConcurrentHashMap<String, ContainerConfig>(containerConfigs), containersFileName);
+        if (!status.isSuccess()) {
+            return new Status(StatusCode.INTERNALERROR, "Failed to save container configurations: "
+                    + status.getDescription());
+        }
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    private void removeComponentsStartUpfiles(String containerName) {
+        String startupLocation = String.format("./%s", GlobalConstants.STARTUPHOME.toString());
+        String containerPrint = String.format("_%s.", containerName.toLowerCase(Locale.ENGLISH));
+
+        File directory = new File(startupLocation);
+        String[] fileList = directory.list();
+
+        logger.trace("Deleteing startup configuration files for container {}", containerName);
+        if (fileList != null) {
+            for (String fileName : fileList) {
+                if (fileName.contains(containerPrint)) {
+                    String fullPath = String.format("%s/%s", startupLocation, fileName);
+                    File file = new File(fullPath);
+                    boolean done = file.delete();
+                    logger.trace("{} {}", (done ? "Deleted: " : "Failed to delete: "), fileName);
+                }
+            }
+        }
+    }
+
+    /**
+     * Create and initialize default all resource group and create association
+     * with default well known users and profiles, if not already learnt from
+     * another cluster node
+     */
+    private void createDefaultAuthorizationGroups() {
+        allResourcesGroupName = ContainerManager.allContainersGroup;
+
+        // Add the default container to the all containers group if needed
+        String defaultContainer = GlobalConstants.DEFAULT.toString();
+        Set<String> allContainers = (resourceGroups.containsKey(allResourcesGroupName)) ? resourceGroups
+                .get(allResourcesGroupName) : new HashSet<String>();
+        if (!allContainers.contains(defaultContainer)) {
+            // Add Default container
+            allContainers.add(defaultContainer);
+            // Update cluster
+            resourceGroups.put(allResourcesGroupName, allContainers);
+        }
+
+        // Add the controller well known roles, if not known already
+        if (!roles.containsKey(UserLevel.SYSTEMADMIN.toString())) {
+            roles.put(UserLevel.SYSTEMADMIN.toString(), AppRoleLevel.APPADMIN);
+        }
+        if (!roles.containsKey(UserLevel.NETWORKADMIN.toString())) {
+            roles.put(UserLevel.NETWORKADMIN.toString(), AppRoleLevel.APPADMIN);
+        }
+        if (!roles.containsKey(UserLevel.NETWORKOPERATOR.toString())) {
+            roles.put(UserLevel.NETWORKOPERATOR.toString(), AppRoleLevel.APPOPERATOR);
+        }
+
+        /*
+         * Create and add the all containers user groups and associate them to the
+         * default well known user roles, if not present already
+         */
+        if (!groupsAuthorizations.containsKey(UserLevel.NETWORKADMIN.toString())) {
+            Set<ResourceGroup> writeProfile = new HashSet<ResourceGroup>(1);
+            Set<ResourceGroup> readProfile = new HashSet<ResourceGroup>(1);
+            writeProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.WRITE));
+            readProfile.add(new ResourceGroup(allResourcesGroupName, Privilege.READ));
+            groupsAuthorizations.put(UserLevel.SYSTEMADMIN.toString(), writeProfile);
+            groupsAuthorizations.put(UserLevel.NETWORKADMIN.toString(), writeProfile);
+            groupsAuthorizations.put(UserLevel.NETWORKOPERATOR.toString(), readProfile);
+        }
+    }
+
+    /**
+     * Until manual configuration is not available, automatically maintain the
+     * well known resource groups
+     *
+     * @param containerName
+     * @param delete
+     */
+    private void updateResourceGroups(String containerName, boolean delete) {
+        // Container Roles and Container Resource Group
+        String groupName = "Container-" + containerName;
+        String containerAdminRole = "Container-" + containerName + "-Admin";
+        String containerOperatorRole = "Container-" + containerName + "-Operator";
+        Set<String> allContainerSet = resourceGroups.get(allResourcesGroupName);
+        if (delete) {
+            resourceGroups.remove(groupName);
+            groupsAuthorizations.remove(containerAdminRole);
+            groupsAuthorizations.remove(containerOperatorRole);
+            roles.remove(containerAdminRole);
+            roles.remove(containerOperatorRole);
+            // Update the all container group
+            allContainerSet.remove(containerName);
+        } else {
+            Set<String> resources = new HashSet<String>(1);
+            resources.add(containerName);
+            resourceGroups.put(groupName, resources);
+            Set<ResourceGroup> adminGroups = new HashSet<ResourceGroup>(1);
+            Set<ResourceGroup> operatorGroups = new HashSet<ResourceGroup>(1);
+            adminGroups.add(new ResourceGroup(groupName, Privilege.WRITE));
+            operatorGroups.add(new ResourceGroup(groupName, Privilege.READ));
+            groupsAuthorizations.put(containerAdminRole, adminGroups);
+            groupsAuthorizations.put(containerOperatorRole, operatorGroups);
+            roles.put(containerAdminRole, AppRoleLevel.APPADMIN);
+            roles.put(containerOperatorRole, AppRoleLevel.APPOPERATOR);
+            // Update the all containers resource group
+            allContainerSet.add(containerName);
+        }
+        // Update resource groups in cluster
+        resourceGroups.put(allResourcesGroupName, allContainerSet);
+    }
+
+    /**
+     * Notify ContainerAware listeners of the creation/deletion of the container
+     *
+     * @param containerName
+     * @param delete
+     *            true is container was removed, false otherwise
+     */
+    private void notifyContainerAwareListeners(String containerName, boolean delete) {
+        // Back-end World: container name forced to lower case
+        String name = containerName.toLowerCase(Locale.ENGLISH);
+
+        synchronized (this.iContainerAware) {
+            for (IContainerAware i : this.iContainerAware) {
+                if (delete) {
+                    i.containerDestroy(name);
+                } else {
+                    i.containerCreate(name);
+                }
+            }
+        }
+    }
+
+    /**
+     * Notify the ContainerListener listeners in case the container mode has changed
+     * following a container configuration operation Note: this call must happen
+     * after the configuration db has been updated
+     *
+     * @param lastActionDelete
+     *            true if the last container configuration operation was a container
+     *            delete operation
+     */
+    private void notifyContainerModeChange(boolean lastActionDelete) {
+        if (lastActionDelete == false && containerConfigs.size() == 1) {
+            logger.info("First container Creation. Inform listeners");
+            synchronized (this.iContainerListener) {
+                for (IContainerListener i : this.iContainerListener) {
+                    i.containerModeUpdated(UpdateType.ADDED);
+                }
+            }
+        } else if (lastActionDelete == true && containerConfigs.isEmpty()) {
+            logger.info("Last container Deletion. Inform listeners");
+            synchronized (this.iContainerListener) {
+                for (IContainerListener i : this.iContainerListener) {
+                    i.containerModeUpdated(UpdateType.REMOVED);
+                }
+            }
+        }
+    }
+
+    private Status addRemoveContainerEntries(String containerName, List<String> nodeConnectorsString, boolean delete) {
+        // Construct action message
+        String action = String.format("Node conenctor(s) %s container %s: %s", delete ? "removal from" : "addition to",
+                containerName, nodeConnectorsString);
+
+        // Validity Check
+        if (nodeConnectorsString == null || nodeConnectorsString.isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Node connector list is null or empty");
+        }
+
+        // Presence check
+        ContainerConfig entryConf = containerConfigs.get(containerName);
+        if (entryConf == null) {
+            String msg = String.format("Container not found: %s", containerName);
+            String error = String.format("Failed to apply %s: (%s)", action, msg);
+            logger.warn(error);
+            return new Status(StatusCode.NOTFOUND, msg);
+        }
+
+        // Validation check
+        Status status = ContainerConfig.validateNodeConnectors(nodeConnectorsString);
+        if (!status.isSuccess()) {
+            String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
+            logger.warn(error);
+            return status;
+        }
+
+        List<NodeConnector> nodeConnectors = ContainerConfig.nodeConnectorsFromString(nodeConnectorsString);
+
+        // Port sharing check
+        if (!delete) {
+            /*
+             * Check if the ports being added to this container already belong to
+             * other containers. If so check whether the the appropriate flow specs
+             * are configured on this container
+             */
+            status = validatePortSharing(containerName, nodeConnectors);
+            if (!status.isSuccess()) {
+                String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
+                logger.warn(error);
+                return status;
+            }
+        }
+
+        // Update Database
+        status = updateContainerEntryDatabase(containerName, nodeConnectors, delete);
+        if (!status.isSuccess()) {
+            String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
+            logger.warn(error);
+            return status;
+        }
+
+        // Update Configuration
+        status = (delete) ? entryConf.removeNodeConnectors(nodeConnectorsString) : entryConf
+                .addNodeConnectors(nodeConnectorsString);
+        if (!status.isSuccess()) {
+            String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
+            logger.warn(error);
+            // Revert backend changes
+            Status statusRevert = updateContainerEntryDatabase(containerName, nodeConnectors, !delete);
+            if (!statusRevert.isSuccess()) {
+                // Unlikely
+                logger.error("Failed to revert changes in database (CRITICAL)");
+            }
+            return status;
+        }
+
+        // Update cluster Configuration cache
+        containerConfigs.put(containerName, entryConf);
+
+        // Notify
+        UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
+        notifyContainerEntryChangeInternal(containerName, nodeConnectors, update);
+        // Trigger cluster notification
+        containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(nodeConnectors, update));
+
+        return status;
+    }
+
+    private void notifyContainerChangeInternal(ContainerConfig conf, UpdateType update) {
+        String containerName = conf.getContainerName();
+        logger.trace("Notifying listeners on {} for container {}", update, containerName);
+        // Back-end World: container name forced to lower case
+        String container = containerName.toLowerCase(Locale.ENGLISH);
+        boolean delete = (update == UpdateType.REMOVED);
+        // Check if a container mode change notification is needed
+        notifyContainerModeChange(delete);
+        // Notify listeners
+        notifyContainerAwareListeners(container, delete);
+    }
+
+    private void notifyContainerEntryChangeInternal(String containerName, List<NodeConnector> ncList, UpdateType update) {
+        logger.trace("Notifying listeners on {} for ports {} in container {}", update, ncList, containerName);
+        // Back-end World: container name forced to lower case
+        String container = containerName.toLowerCase(Locale.ENGLISH);
+        for (NodeConnector nodeConnector : ncList) {
+            // Now signal that the port has been added/removed
+            synchronized (this.iContainerListener) {
+                for (IContainerListener i : this.iContainerListener) {
+                    i.nodeConnectorUpdated(container, nodeConnector, update);
+                }
+            }
+        }
+    }
+
+    private void notifyCFlowChangeInternal(String containerName, List<ContainerFlowConfig> confList, UpdateType update) {
+        logger.trace("Notifying listeners on {} for flow specs {} in container {}", update, confList, containerName);
+        // Back-end World: container name forced to lower case
+        String container = containerName.toLowerCase(Locale.ENGLISH);
+        synchronized (this.iContainerListener) {
+            for (ContainerFlowConfig conf : confList) {
+                for (Match match : conf.getMatches()) {
+                    ContainerFlow cFlow = new ContainerFlow(match);
+                    for (IContainerListener i : this.iContainerListener) {
+                        i.containerFlowUpdated(container, cFlow, cFlow, update);
+                    }
+                }
+            }
+        }
+    }
+
+    private Status addRemoveContainerFlow(String containerName, List<ContainerFlowConfig> cFlowConfList, boolean delete) {
+        // Construct action message
+        String action = String.format("Flow spec(s) %s container %s: %s", delete ? "removal from" : "addition to",
+                containerName, cFlowConfList);
+
+        // Presence check
+        ContainerConfig containerConfig = this.containerConfigs.get(containerName);
+        if (containerConfig == null) {
+            String msg = String.format("Container not found: %s", containerName);
+            String error = String.format("Failed to apply %s: (%s)", action, msg);
+            logger.warn(error);
+            return new Status(StatusCode.NOTFOUND, "Container not present");
+        }
+
+        // Validity check, check for overlaps on current container configuration
+        Status status = containerConfig.validateContainerFlowModify(cFlowConfList, delete);
+        if (!status.isSuccess()) {
+            String msg = status.getDescription();
+            String error = String.format("Failed to apply %s: (%s)", action, msg);
+            logger.warn(error);
+            return new Status(StatusCode.BADREQUEST, msg);
+        }
+
+        // Validate the operation in terms to the port sharing with other containers
+        for (ContainerFlowConfig conf : cFlowConfList) {
+            for (Match match : conf.getMatches()) {
+                ContainerFlow cFlow = new ContainerFlow(match);
+                status = validateContainerFlowAddRemoval(containerName, cFlow, delete);
+                if (!status.isSuccess()) {
+                    String msg = "Validation failed: " + status.getDescription();
+                    String error = String.format("Failed to apply %s: (%s)", action, msg);
+                    logger.warn(error);
+                    return new Status(StatusCode.BADREQUEST, msg);
+                }
+            }
+        }
+
+        // Update Database
+        status = updateContainerFlow(containerName, cFlowConfList, delete);
+        if (!status.isSuccess()) {
+            String error = String.format("Failed to apply %s: (%s)", action, status.getDescription());
+            logger.error(error);
+            return status;
+        }
+
+        // Update Configuration
+        status = (delete) ? containerConfig.removeContainerFlows(cFlowConfList) : containerConfig
+                .addContainerFlows(cFlowConfList);
+        if (!status.isSuccess()) {
+            String error = String.format("Failed to modify config for %s: (%s)", action, status.getDescription());
+            logger.error(error);
+            // Revert backend changes
+            Status statusRevert = updateContainerFlow(containerName, cFlowConfList, !delete);
+            if (!statusRevert.isSuccess()) {
+                // Unlikely
+                logger.error("Failed to revert changes in database (CRITICAL)");
+            }
+            return status;
+        }
+        // Update cluster cache
+        this.containerConfigs.put(containerName, containerConfig);
+
+        // Notify listeners
+        UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
+        notifyCFlowChangeInternal(containerName, cFlowConfList, update);
+        // Trigger cluster notification
+        containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(cFlowConfList, update));
+
+        return status;
+    }
+
+    private Status addRemoveContainer(ContainerConfig containerConf, boolean delete) {
+        // Construct action message
+        String action = String.format("Container %s", delete ? "removal" : "creation");
+
+        // Valid configuration check
+        Status status = null;
+        String error = (containerConfigs == null) ? String.format("Invalid %s configuration: (null config object)", action)
+                : (!(status = containerConf.validate()).isSuccess()) ? String.format("Invalid %s configuration: (%s)",
+                        action, status.getDescription()) : null;
+        if (error != null) {
+            logger.warn(error);
+            return new Status(StatusCode.BADREQUEST, error);
+        }
+
+        // Configuration presence check
+        String containerName = containerConf.getContainerName();
+        if (delete) {
+            if (!containerConfigs.containsKey(containerName)) {
+                String msg = String.format("%s Failed: (Container does not exist: %s)", action, containerName);
+                logger.warn(msg);
+                return new Status(StatusCode.NOTFOUND, msg);
+            }
+        } else {
+            if (containerConfigs.containsKey(containerName)) {
+                String msg = String.format("%s Failed: (Container already exist: %s)", action, containerName);
+                logger.warn(msg);
+                return new Status(StatusCode.CONFLICT, msg);
+            }
+        }
+
+        /*
+         * The proposed container configuration could be a complex one containing
+         * both ports and flow spec. If so, check if it has shared ports with
+         * other existing containers. If that is the case verify flow spec isolation
+         * is in place. No need to check on flow spec validation first. This
+         * would take care of both
+         */
+        if (!delete) {
+            status = validatePortSharing(containerConf);
+            if (!status.isSuccess()) {
+                error = String.format("%s Failed: (%s)", action, status.getDescription());
+                logger.error(error);
+                return status;
+            }
+        }
+
+        // Update Database
+        status = updateContainerDatabase(containerConf, delete);
+
+        // Abort and exit here if back-end database update failed
+        if (!status.isSuccess()) {
+            return status;
+        }
+
+        /*
+         * This is a quick fix until configuration service becomes the
+         * centralized configuration management place. Here container manager will
+         * remove the startup files for all the bundles that are present in the
+         * container being deleted. Do the cleanup here in Container manger as do not
+         * want to put this temporary code in Configuration manager yet which is
+         * ODL.
+         */
+        if (delete) {
+            // TODO: remove when Config Mgr takes over
+            removeComponentsStartUpfiles(containerName);
+        }
+
+        /*
+         * Update Configuration: This will trigger the notifications on cache
+         * update callback locally and on the other cluster nodes
+         */
+        if (delete) {
+            this.containerConfigs.remove(containerName);
+        } else {
+            this.containerConfigs.put(containerName, containerConf);
+        }
+
+        // Automatically create and populate user and resource groups
+        updateResourceGroups(containerName, delete);
+
+        // Notify listeners
+        UpdateType update = (delete) ? UpdateType.REMOVED : UpdateType.ADDED;
+        notifyContainerChangeInternal(containerConf, update);
+
+        // Trigger cluster notification
+        containerChangeEvents.put(containerName, new ContainerChangeEvent(containerConf, update));
+
+        if (update == UpdateType.ADDED) {
+            if (containerConf.hasFlowSpecs()) {
+                List<ContainerFlowConfig> specList = containerConf.getContainerFlowConfigs();
+                // Notify flow spec addition
+                notifyCFlowChangeInternal(containerName, specList, update);
+
+                // Trigger cluster notification
+                containerChangeEvents.put(containerName, new ContainerFlowChangeEvent(specList, update));
+            }
+
+            if (containerConf.hasNodeConnectors()) {
+                List<NodeConnector> ncList = containerConf.getPortList();
+                // Notify port(s) addition
+                notifyContainerEntryChangeInternal(containerName, ncList, update);
+                // Trigger cluster notification
+                containerChangeEvents.put(containerName, new NodeConnectorsChangeEvent(ncList, update));
+            }
+        }
+
+        return status;
+    }
+
+    @Override
+    public Status addContainer(ContainerConfig containerConf) {
+        return addRemoveContainer(containerConf, false);
+    }
+
+    @Override
+    public Status removeContainer(ContainerConfig containerConf) {
+        return addRemoveContainer(containerConf, true);
+    }
+
+    @Override
+    public Status removeContainer(String containerName) {
+        // Construct action message
+        String action = String.format("Container removal: %s", containerName);
+
+        ContainerConfig containerConf = containerConfigs.get(containerName);
+        if (containerConf == null) {
+            String msg = String.format("Container not found");
+            String error = String.format("Failed to apply %s: (%s)", action, msg);
+            logger.warn(error);
+            return new Status(StatusCode.NOTFOUND, msg);
+        }
+        return addRemoveContainer(containerConf, true);
+    }
+
+    @Override
+    public Status addContainerEntry(String containerName, List<String> nodeConnectors) {
+        return addRemoveContainerEntries(containerName, nodeConnectors, false);
+    }
+
+    @Override
+    public Status removeContainerEntry(String containerName, List<String> nodeConnectors) {
+        return addRemoveContainerEntries(containerName, nodeConnectors, true);
+    }
+
+    @Override
+    public Status addContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
+        return addRemoveContainerFlow(containerName, fSpecConf, false);
+    }
+
+    @Override
+    public Status removeContainerFlows(String containerName, List<ContainerFlowConfig> fSpecConf) {
+        return addRemoveContainerFlow(containerName, fSpecConf, true);
+    }
+
+    @Override
+    public Status removeContainerFlows(String containerName, Set<String> names) {
+        // Construct action message
+        String action = String.format("Flow spec(s) removal from container %s: %s", containerName, names);
+
+        // Presence check
+        ContainerConfig sc = containerConfigs.get(containerName);
+        if (sc == null) {
+            String msg = String.format("Container not found: %s", containerName);
+            String error = String.format("Failed to apply %s: (%s)", action, msg);
+            logger.warn(error);
+            return new Status(StatusCode.NOTFOUND, msg);
+        }
+        List<ContainerFlowConfig> list = sc.getContainerFlowConfigs(names);
+        if (list.isEmpty() || list.size() != names.size()) {
+            String msg = String.format("Cannot find all the specified flow specs");
+            String error = String.format("Failed to apply %s: (%s)", action, msg);
+            logger.warn(error);
+            return new Status(StatusCode.BADREQUEST, msg);
+        }
+        return addRemoveContainerFlow(containerName, list, true);
+    }
+
+    @Override
+    public List<ContainerFlowConfig> getContainerFlows(String containerName) {
+        ContainerConfig sc = containerConfigs.get(containerName);
+        return (sc == null) ? new ArrayList<ContainerFlowConfig>(0) : sc.getContainerFlowConfigs();
+    }
+
+    @Override
+    public List<String> getContainerFlowNameList(String containerName) {
+        ContainerConfig sc = containerConfigs.get(containerName);
+        return (sc == null) ? new ArrayList<String>(0) : sc.getContainerFlowConfigsNames();
+    }
+
+    @Override
+    public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
+        // Perform the class deserialization locally, from inside the package
+        // where the class is defined
+        return ois.readObject();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void loadContainerConfig() {
+        ObjectReader objReader = new ObjectReader();
+        ConcurrentMap<String, ContainerConfig> configMap = (ConcurrentMap<String, ContainerConfig>) objReader.read(this,
+                containersFileName);
+
+        if (configMap == null) {
+            return;
+        }
+
+        for (Map.Entry<String, ContainerConfig> configEntry : configMap.entrySet()) {
+            addContainer(configEntry.getValue());
+        }
+    }
+
+    public void _psc(CommandInterpreter ci) {
+        for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+            ContainerConfig sc = entry.getValue();
+            ci.println(String.format("%s: %s", sc.getContainerName(), sc.toString()));
+        }
+        ci.println("Total number of containers: " + containerConfigs.entrySet().size());
+    }
+
+    public void _pfc(CommandInterpreter ci) {
+        for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+            ContainerConfig sc = entry.getValue();
+            ci.println(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
+        }
+    }
+
+    public void _psd(CommandInterpreter ci) {
+        for (String containerName : containerData.keySet()) {
+            ContainerData sd = containerData.get(containerName);
+            for (Node sid : sd.getSwPorts().keySet()) {
+                Set<NodeConnector> s = sd.getSwPorts().get(sid);
+                ci.println("\t" + sid + " : " + s);
+            }
+
+            for (ContainerFlow s : sd.getContainerFlowSpecs()) {
+                ci.println("\t" + s.toString());
+            }
+        }
+    }
+
+    public void _psp(CommandInterpreter ci) {
+        for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
+            ci.println(nodeConnectorToContainers.get(sp));
+        }
+    }
+
+    public void _psm(CommandInterpreter ci) {
+        for (Node sp : nodeToContainers.keySet()) {
+            ci.println(nodeToContainers.get(sp));
+        }
+    }
+
+    public void _addContainer(CommandInterpreter ci) {
+        String containerName = ci.nextArgument();
+        if (containerName == null) {
+            ci.print("Container Name not specified");
+            return;
+        }
+        String staticVlan = ci.nextArgument();
+        if (staticVlan == null) {
+            ci.print("Static Vlan not specified");
+            return;
+        }
+        ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
+        ci.println(this.addRemoveContainer(containerConfig, false));
+    }
+
+    public void _createContainer(CommandInterpreter ci) {
+        String containerName = ci.nextArgument();
+        if (containerName == null) {
+            ci.print("Container Name not specified");
+            return;
+        }
+        String staticVlan = ci.nextArgument();
+        if (staticVlan == null) {
+            ci.print("Static Vlan not specified");
+            return;
+        }
+        List<String> ports = new ArrayList<String>();
+        for (long l = 1L; l < 10L; l++) {
+            ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
+        }
+        List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
+        cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
+        ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
+        ci.println(this.addRemoveContainer(containerConfig, false));
+    }
+
+    public void _removeContainer(CommandInterpreter ci) {
+        String containerName = ci.nextArgument();
+        if (containerName == null) {
+            ci.print("Container Name not specified");
+            return;
+        }
+        ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
+        ci.println(this.addRemoveContainer(containerConfig, true));
+    }
+
+    public void _addContainerEntry(CommandInterpreter ci) {
+        String containerName = ci.nextArgument();
+        if (containerName == null) {
+            ci.print("Container Name not specified");
+            return;
+        }
+        String nodeId = ci.nextArgument();
+        if (nodeId == null) {
+            ci.print("Node Id not specified");
+            return;
+        }
+        String portId = ci.nextArgument();
+        if (portId == null) {
+            ci.print("Port not specified");
+            return;
+        }
+        Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
+        Short port = Short.valueOf(portId);
+        NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
+        List<String> portList = new ArrayList<String>(1);
+        portList.add(nc.toString());
+        ci.println(this.addRemoveContainerEntries(containerName, portList, false));
+    }
+
+    public void _removeContainerEntry(CommandInterpreter ci) {
+        String containerName = ci.nextArgument();
+        if (containerName == null) {
+            ci.print("Container Name not specified");
+            return;
+        }
+        String nodeId = ci.nextArgument();
+        if (nodeId == null) {
+            ci.print("Node Id not specified");
+            return;
+        }
+        String portId = ci.nextArgument();
+        if (portId == null) {
+            ci.print("Port not specified");
+            return;
+        }
+        Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
+        Short port = Short.valueOf(portId);
+        NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
+        List<String> portList = new ArrayList<String>(1);
+        portList.add(nc.toString());
+        ci.println(this.addRemoveContainerEntries(containerName, portList, true));
+    }
+
+    private ContainerFlowConfig createSampleContainerFlowConfig(String cflowName, boolean boolUnidirectional) {
+        ContainerFlowConfig cfg = new ContainerFlowConfig(cflowName, "9.9.1.0/24", "19.9.1.2", "TCP", "1234", "25");
+        return cfg;
+    }
+
+    public void _addContainerFlow(CommandInterpreter ci) {
+        String containerName = ci.nextArgument();
+        if (containerName == null) {
+            ci.print("Container Name not specified");
+            return;
+        }
+        String cflowName = ci.nextArgument();
+        if (cflowName == null) {
+            ci.print("cflowName not specified");
+            return;
+        }
+        String unidirectional = ci.nextArgument();
+        boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
+        List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
+        list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
+        ci.println(this.addRemoveContainerFlow(containerName, list, false));
+    }
+
+    public void _removeContainerFlow(CommandInterpreter ci) {
+        String containerName = ci.nextArgument();
+        if (containerName == null) {
+            ci.print("Container Name not specified");
+            return;
+        }
+        String cflowName = ci.nextArgument();
+        if (cflowName == null) {
+            ci.print("cflowName not specified");
+            return;
+        }
+        Set<String> set = new HashSet<String>(1);
+        set.add(cflowName);
+        ci.println(this.removeContainerFlows(containerName, set));
+    }
+
+    @Override
+    public String getHelp() {
+        StringBuffer help = new StringBuffer();
+        help.append("---ContainerManager Testing---\n");
+        help.append("\tpsc        - Print ContainerConfigs\n");
+        help.append("\tpfc        - Print FlowSpecConfigs\n");
+        help.append("\tpsd        - Print ContainerData\n");
+        help.append("\tpsp        - Print nodeConnectorToContainers\n");
+        help.append("\tpsm        - Print nodeToContainers\n");
+        help.append("\t addContainer <containerName> <staticVlan> \n");
+        help.append("\t removeContainer <containerName> \n");
+        help.append("\t addContainerEntry <containerName> <nodeId> <port> \n");
+        help.append("\t removeContainerEntry <containerName> <nodeId> <port> \n");
+        help.append("\t addContainerFlow <containerName> <cflowName> <unidirectional true/false>\n");
+        help.append("\t removeContainerFlow <containerName> <cflowName> \n");
+        return help.toString();
+    }
+
+    @Override
+    public boolean doesContainerExist(String containerName) {
+        // Test for default container
+        if (GlobalConstants.DEFAULT.toString().equalsIgnoreCase(containerName)) {
+            return true;
+        }
+        // Test for non-default one
+        return (getContainerByName(containerName) != null);
+    }
+
+    @Override
+    public ContainerData getContainerData(String containerName) {
+        return (getContainerByName(containerName));
+    }
+
+    @Override
+    public Status saveConfiguration() {
+        return saveContainerConfig();
+    }
+
+    public void _containermgrGetRoles(CommandInterpreter ci) {
+        ci.println("Configured roles for Container Mgr:");
+        List<String> list = this.getRoles();
+        for (String role : list) {
+            ci.println(role + "\t" + roles.get(role));
+        }
+    }
+
+    public void _containermgrGetAuthorizedGroups(CommandInterpreter ci) {
+        String roleName = ci.nextArgument();
+        if (roleName == null || roleName.trim().isEmpty()) {
+            ci.println("Invalid argument");
+            ci.println("mmGetAuthorizedGroups <role_name>");
+            return;
+        }
+        ci.println("Resource Groups associated to role " + roleName + ":");
+        List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
+        for (ResourceGroup group : list) {
+            ci.println(group.toString());
+        }
+    }
+
+    public void _containermgrGetAuthorizedResources(CommandInterpreter ci) {
+        String roleName = ci.nextArgument();
+        if (roleName == null || roleName.trim().isEmpty()) {
+            ci.println("Invalid argument");
+            ci.println("mmGetAuthorizedResources <role_name>");
+            return;
+        }
+        ci.println("Resource associated to role " + roleName + ":");
+        List<Resource> list = this.getAuthorizedResources(roleName);
+        for (Resource resource : list) {
+            ci.println(resource.toString());
+        }
+    }
+
+    public void _containermgrGetResourcesForGroup(CommandInterpreter ci) {
+        String groupName = ci.nextArgument();
+        if (groupName == null || groupName.trim().isEmpty()) {
+            ci.println("Invalid argument");
+            ci.println("containermgrResourcesForGroup <group_name>");
+            return;
+        }
+        ci.println("Group " + groupName + " contains the following resources:");
+        List<Object> resources = this.getResources(groupName);
+        for (Object resource : resources) {
+            ci.println(resource.toString());
+        }
+    }
+
+    public void _containermgrGetUserLevel(CommandInterpreter ci) {
+        String userName = ci.nextArgument();
+        if (userName == null || userName.trim().isEmpty()) {
+            ci.println("Invalid argument");
+            ci.println("containermgrGetUserLevel <user_name>");
+            return;
+        }
+        ci.println("User " + userName + " has level: " + this.getUserLevel(userName));
+    }
+
+    public void _containermgrGetUserResources(CommandInterpreter ci) {
+        String userName = ci.nextArgument();
+        if (userName == null || userName.trim().isEmpty()) {
+            ci.println("Invalid argument");
+            ci.println("containermgrGetUserResources <user_name>");
+            return;
+        }
+        ci.println("User " + userName + " owns the following resources: ");
+        Set<Resource> resources = this.getAllResourcesforUser(userName);
+        for (Resource resource : resources) {
+            ci.println(resource.toString());
+        }
+    }
+
+    /*
+     * For scalability testing where as of now controller gui is unresponsive
+     * providing here an osgi hook to trigger the save config so that DT do not
+     * have to reaply the scalable configuration each time they restart the
+     * controller
+     */
+    // TODO: remove when no longer needed
+    public void _saveConfig(CommandInterpreter ci) {
+        Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
+
+        IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
+                IConfigurationService.class, this);
+        if (configService != null) {
+            status = configService.saveConfigurations();
+        }
+        ci.println(status.toString());
     }
 
     @Override
-    public Status saveContainerConfig() {
-        return null;
+    public List<String> getContainerNames() {
+        return getContainerNameList();
     }
 
+    @Override
+    public boolean hasNonDefaultContainer() {
+        return !containerConfigs.keySet().isEmpty();
+    }
 }
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 (file)
index 0000000..555f37a
--- /dev/null
@@ -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 (file)
index 0000000..ba71dbf
--- /dev/null
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>commons.opendaylight</artifactId>
+    <version>1.4.0-SNAPSHOT</version>
+    <relativePath>../../commons/opendaylight</relativePath>
+  </parent>
+  <scm>
+    <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+  </scm>
+
+  <artifactId>containermanager.it.implementation</artifactId>
+  <version>0.5.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.6</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Import-Package>
+              org.opendaylight.controller.containermanager,
+              org.opendaylight.controller.clustering.services,
+              org.opendaylight.controller.sal.packet,
+              org.opendaylight.controller.sal.utils,
+              org.opendaylight.controller.sal.core,
+              org.opendaylight.controller.sal.action,
+              org.opendaylight.controller.sal.flowprogrammer,
+              org.opendaylight.controller.sal.match,
+              org.opendaylight.controller.sal.reader,
+              org.eclipse.osgi.framework.console,
+              org.osgi.framework,
+              org.slf4j,
+              org.apache.felix.dm
+            </Import-Package>
+            <Export-Package>
+            </Export-Package>
+            <Bundle-Activator>
+              org.opendaylight.controller.containermanager.internal.Activator
+            </Bundle-Activator>
+          </instructions>
+          <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>clustering.services</artifactId>
+      <version>0.4.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>containermanager</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+</project>
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 (file)
index 0000000..9caa620
--- /dev/null
@@ -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 (file)
index 0000000..15f939d
--- /dev/null
@@ -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<ContainerFlow> getContainerFlows() {
+        return null;
+    }
+
+    @Override
+    public short getTag(Node n) {
+        return (short) 0;
+    }
+
+    @Override
+    public Set<NodeConnector> getNodeConnectors() {
+        return null;
+    }
+
+    @Override
+    public Set<Node> getNodes() {
+        return null;
+    }
+}
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 (file)
index 0000000..b5624af
--- /dev/null
@@ -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> iContainerAware = (Set<IContainerAware>) Collections
+            .synchronizedSet(new HashSet<IContainerAware>());
+    private Set<IContainerListener> iContainerListener = Collections
+            .synchronizedSet(new HashSet<IContainerListener>());
+
+    void setIContainerListener(IContainerListener s) {
+        if (this.iContainerListener != null) {
+            this.iContainerListener.add(s);
+        }
+    }
+
+    void unsetIContainerListener(IContainerListener s) {
+        if (this.iContainerListener != null) {
+            this.iContainerListener.remove(s);
+        }
+    }
+
+    public void setIContainerAware(IContainerAware iContainerAware) {
+        if (!this.iContainerAware.contains(iContainerAware)) {
+            this.iContainerAware.add(iContainerAware);
+            // Now call the container creation for all the known containers so
+            // far
+            List<String> containerDB = getContainerNames();
+            if (containerDB != null) {
+                for (int i = 0; i < containerDB.size(); i++) {
+                    iContainerAware.containerCreate(containerDB.get(i));
+                }
+            }
+        }
+    }
+
+    public void unsetIContainerAware(IContainerAware iContainerAware) {
+        this.iContainerAware.remove(iContainerAware);
+        // There is no need to do cleanup of the component when
+        // unregister because it will be taken care by the Containerd
+        // component itself
+    }
+
+    public void setClusterServices(IClusterGlobalServices i) {
+        this.clusterServices = i;
+        logger.debug("IClusterServices set");
+    }
+
+    public void unsetClusterServices(IClusterGlobalServices i) {
+        if (this.clusterServices == i) {
+            this.clusterServices = null;
+            logger.debug("IClusterServices Unset");
+        }
+    }
+
+    public void init() {
+        logger.info("ContainerManager startup....");
+    }
+
+    public void destroy() {
+        // Clear local states
+        this.iContainerAware.clear();
+        this.iContainerListener.clear();
+
+        logger.info("ContainerManager Shutdown....");
+    }
+
+    @Override
+    public List<String> getContainerNames() {
+        /*
+         * Return container names as they were configured by user (case sensitive)
+         * along with the default container
+         */
+        List<String> containerNameList = new ArrayList<String>();
+        containerNameList.add(GlobalConstants.DEFAULT.toString());
+        return containerNameList;
+    }
+
+    @Override
+    public boolean hasNonDefaultContainer() {
+        return false;
+    }
+
+    @Override
+    public Status addContainer(ContainerConfig configObject) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Status removeContainer(ContainerConfig configObject) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Status removeContainer(String containerName) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Status addContainerEntry(String containerName, List<String> portList) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Status removeContainerEntry(String containerName,
+            List<String> portList) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Status addContainerFlows(String containerName,
+            List<ContainerFlowConfig> configObject) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Status removeContainerFlows(String containerName,
+            List<ContainerFlowConfig> configObject) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Status removeContainerFlows(String containerName, Set<String> name) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public List<ContainerConfig> getContainerConfigList() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public ContainerConfig getContainerConfig(String containerName) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public List<String> getContainerNameList() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean doesContainerExist(String ContainerId) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public Map<String, List<ContainerFlowConfig>> getContainerFlows() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public List<ContainerFlowConfig> getContainerFlows(String containerName) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public List<String> getContainerFlowNameList(String containerName) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}
index d44cfc5..56ca0c9 100644 (file)
@@ -66,6 +66,8 @@
     <module>../../hosttracker_new/implementation</module>
     <module>../../containermanager/api</module>
     <module>../../containermanager/implementation</module>
+    <module>../../containermanager/it.implementation</module>
+    <module>../../appauth</module>
     <module>../../switchmanager/api</module>
     <module>../../switchmanager/implementation</module>
     <module>../../switchmanager/integrationtest</module>
     <module>../../northbound/hosttracker</module>
     <module>../../northbound/subnets</module>
     <module>../../northbound/switchmanager</module>
+    <module>../../northbound/containermanager</module>
     <module>../../northbound/networkconfiguration/bridgedomain</module>
 
     <!-- Northbound integration tests -->
index a85c3f8..92a718b 100644 (file)
@@ -15,6 +15,7 @@
         <exclude>org.opendaylight.controller:logging.bridge</exclude>
         <exclude>org.opendaylight.controller:protocol_plugins.stub</exclude>
         <exclude>org.opendaylight.controller:*.integrationtest</exclude>
+        <exclude>org.opendaylight.controller:containermanager.it.implementation</exclude>
         <exclude>org.opendaylight.controller:hosttracker_new</exclude>
         <exclude>org.opendaylight.controller:hosttracker_new.implementation</exclude>
         <exclude>org.opendaylight.controller:checkstyle</exclude>
index b0ffaac..7779243 100644 (file)
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
-      <artifactId>containermanager.implementation</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <artifactId>containermanager.it.implementation</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 4fc3afc..ef2e0fa 100644 (file)
@@ -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(),
index 351e31b..8155165 100644 (file)
     <dependency>\r
       <groupId>org.opendaylight.controller</groupId>\r
       <artifactId>containermanager</artifactId>\r
-      <version>0.4.0-SNAPSHOT</version>\r
+      <version>0.5.0-SNAPSHOT</version>\r
     </dependency>\r
 \r
     <dependency>\r
       <groupId>org.opendaylight.controller</groupId>\r
-      <artifactId>containermanager.implementation</artifactId>\r
-      <version>0.4.0-SNAPSHOT</version>\r
+      <artifactId>containermanager.it.implementation</artifactId>\r
+      <version>0.5.0-SNAPSHOT</version>\r
     </dependency>\r
 \r
     <dependency>\r
index 57e2714..4281d0d 100644 (file)
@@ -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 (file)
index 0000000..9dcab30
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<enunciate label="full" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd">
+  <services>
+    <rest defaultRestSubcontext="/controller/nb/v2/containermanager"/>
+  </services>
+
+  <modules>
+    <docs docsDir="rest" title="Container Manager REST API" includeExampleXml="true" includeExampleJson="true"/>
+  </modules>
+</enunciate>
\ No newline at end of file
diff --git a/opendaylight/northbound/containermanager/pom.xml b/opendaylight/northbound/containermanager/pom.xml
new file mode 100644 (file)
index 0000000..067e5f1
--- /dev/null
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>commons.opendaylight</artifactId>
+    <version>1.4.0-SNAPSHOT</version>
+    <relativePath>../../commons/opendaylight</relativePath>
+  </parent>
+
+  <groupId>org.opendaylight.controller</groupId>
+  <artifactId>containermanager.northbound</artifactId>
+  <version>0.4.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.enunciate</groupId>
+        <artifactId>maven-enunciate-plugin</artifactId>
+        <version>${enunciate.version}</version>
+       </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.6</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Export-Package>
+            </Export-Package>
+            <Import-Package>
+              org.opendaylight.controller.appauth.authorization,
+              org.opendaylight.controller.sal.authorization,
+              org.opendaylight.controller.usermanager,
+              org.opendaylight.controller.sal.core,
+              org.opendaylight.controller.sal.utils,
+              org.opendaylight.controller.containermanager,
+              org.opendaylight.controller.northbound.commons,
+              org.opendaylight.controller.northbound.commons.exception,
+              org.opendaylight.controller.northbound.commons.utils,
+              com.sun.jersey.spi.container.servlet,
+              javax.ws.rs,
+              javax.ws.rs.core,
+              javax.xml.bind.annotation,
+              javax.xml.bind,
+              org.slf4j,
+              org.codehaus.jackson.jaxrs,
+              !org.codehaus.enunciate.jaxrs
+            </Import-Package>
+            <Web-ContextPath>/controller/nb/v2/containermanager</Web-ContextPath>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+  <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>containermanager</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>appauth</artifactId>
+      <version>0.4.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>web</artifactId>
+      <version>0.4.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.enunciate</groupId>
+      <artifactId>enunciate-core-annotations</artifactId>
+      <version>${enunciate.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>commons.northbound</artifactId>
+      <version>0.4.0-SNAPSHOT</version>
+    </dependency>
+        <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>com.sun.jersey.jersey-servlet</artifactId>
+      <version>1.17-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+</project>
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 (file)
index 0000000..eecc19f
--- /dev/null
@@ -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> containerConfig;
+
+    //To satisfy JAXB
+    @SuppressWarnings("unused")
+    private ContainerConfigs() {
+
+    }
+
+
+    public ContainerConfigs(List<ContainerConfig> containerconfig) {
+        this.containerConfig = containerconfig;
+    }
+
+
+    public List<ContainerConfig> getcontainerConfig() {
+        return containerConfig;
+    }
+
+    public void setcontainerConfig(List<ContainerConfig> containerConfig) {
+        this.containerConfig = containerConfig;
+    }
+}
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 (file)
index 0000000..511adda
--- /dev/null
@@ -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
+ *
+ * <br>
+ * <br>
+ * Authentication scheme : <b>HTTP Basic</b><br>
+ * Authentication realm : <b>opendaylight</b><br>
+ * Transport : <b>HTTP and HTTPS</b><br>
+ * <br>
+ * HTTPS Authentication is disabled by default. Administrator can enable it in
+ * tomcat-server.xml after adding a proper keystore / SSL certificate from a
+ * trusted authority.<br>
+ * More info :
+ * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ *
+ */
+@Path("/")
+public class ContainerManagerNorthbound {
+    private String username;
+
+    @Context
+    public void setSecurityContext(SecurityContext context) {
+        Principal principal;
+        principal = context.getUserPrincipal();
+        username = principal.getName();
+    }
+
+    protected String getUserName() {
+        return username;
+    }
+
+    private IContainerManager getContainerManager() {
+        IContainerManager containerMgr = (IContainerManager) ServiceHelper.getGlobalInstance(IContainerManager.class, this);
+        if (containerMgr == null) {
+            throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
+        }
+        return containerMgr;
+    }
+
+    private void handleNameMismatch(String name, String nameinURL) {
+        if (name == null || nameinURL == null) {
+            throw new BadRequestException(RestMessages.INVALIDJSON.toString());
+        }
+
+        if (name.equalsIgnoreCase(nameinURL)) {
+            return;
+        }
+        throw new BadRequestException(RestMessages.INVALIDJSON.toString());
+    }
+
+
+
+    /**
+     * Get all the containers configured in the system
+     *
+     * @return a List of all {@link org.opendaylight.controller.containermanager.ContainerConfig}
+     *
+     *         <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/all
+     *
+     * Response Payload in XML:
+     * &lt;container-config-list&gt;
+     *    &#x20;&#x20;&#x20;&lt;container-config&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;container&gt;black&lt;/container&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;staticVlan&gt;10&lt;/staticVlan&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|23@OF|00:00:00:00:00:00:20:21&lt;/nodeConnectors&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;flowSpecs&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;tcp&lt;/name&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;TCP&lt;/protocol&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/flowSpecs&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&lt;/container-config&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&lt;container-config&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;container&gt;red&lt;/container&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;staticVlan&gt;20&lt;/staticVlan&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|23@OF|00:00:00:00:00:00:20:21&lt;/nodeConnectors&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;flowSpecs&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;udp&lt;/name&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;UDP&lt;/protocol&gt;
+     *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/flowSpecs&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;/container-config&gt;
+     * &lt;/container-config-list&gt;
+     *
+     * Response Payload in JSON:
+     * { "container-config" : [ { "name" : "black", "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|23@OF|00:00:00:00:00:00:20:21"], "staticVlan" : "10", "flowSpecs : [{ "name": "udp", "protocol": "UDP" }] } ] }
+     * { "container-config" : [ { "name" : "red", "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|23@OF|00:00:00:00:00:00:20:21"], "staticVlan" : "20", "flowSpecs": [{ "name": "tcp", "protocol": "TCP" }] } ] }
+     *
+     * </pre>
+     */
+    @Path("/all")
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @TypeHint(ContainerConfigs.class)
+    @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 401, condition = "User is not authorized to perform this operation"),
+            @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
+    public ContainerConfigs viewAllContainers() {
+
+        handleNetworkAuthorization(getUserName());
+
+        IContainerManager containerManager = getContainerManager();
+
+        return new ContainerConfigs(containerManager.getContainerConfigList());
+    }
+
+    /**
+     * Get the container configuration for container name requested
+     *
+     * @param container
+     *            name of the Container (eg. blue)
+     * @return a List of {@link org.opendaylight.controller.containermanager.ContainerConfig}
+     *
+     *         <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/blue
+     *
+     * Response Payload in XML:
+     * &lt;container-config&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;container&gt;blue&lt;/container&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;staticVlan&gt;10&lt;/staticVlan&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|23@OF|00:00:00:00:00:00:20:21&lt;/nodeConnectors&gt;
+     * &lt;/container-config&gt;
+     *
+     * Response Payload in JSON:
+     * { "container" : "blue", "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|23@OF|00:00:00:00:00:00:20:21"], "staticVlan" : "10" }
+     *
+     * </pre>
+     */
+    @Path("/container/{container}")
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @TypeHint(ContainerConfig.class)
+    @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 401, condition = "User is not authorized to perform this operation"),
+            @ResponseCode(code = 403, condition = "Operation forbidden on default"),
+            @ResponseCode(code = 404, condition = "The container is not found") })
+    public ContainerConfigs viewContainer(@PathParam(value = "container") String container) {
+
+        handleContainerAuthorization(container, getUserName());
+        handleForbiddenOnDefault(container);
+
+        handleContainerNotExists(container);
+
+        IContainerManager containerManager = getContainerManager();
+        List<ContainerConfig> containerConfigs = new ArrayList<ContainerConfig>();
+        containerConfigs.add(containerManager.getContainerConfig(container));
+        return new ContainerConfigs(containerConfigs);
+    }
+
+    /**
+     * Create a container
+     *
+     * @param uriInfo
+     * @param container
+     *            name of the Container (eg. yellow)
+     * @param containerConfig
+     *            details of the container as specified by:
+     *            {@link org.opendaylight.controller.containermanager.ContainerConfig}
+     * @return Response as dictated by the HTTP Response Status code
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/yellow
+     *
+     * Request Payload in XML:
+     * &lt;container-config&gt;
+     *   &#x20;&#x20;&#x20;&#x20;&lt;container&gt;yellow&lt;/container&gt;
+     *   &#x20;&#x20;&#x20;&#x20;&lt;staticVlan&gt;10&lt;/staticVlan&gt;
+     *   &#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;&lt;/nodeConnectors&gt;
+     * &lt;/container-config&gt;
+     *
+     * Request Payload in JSON:
+     * { "container" : "yellow", "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|23@OF|00:00:00:00:00:00:20:21"], "staticVlan" : "10"}
+     *
+     * </pre>
+     */
+    @Path("/container/{container}")
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @StatusCodes({ @ResponseCode(code = 201, condition = "Container created successfully"),
+            @ResponseCode(code = 400, condition = "Invalid Container configuration."),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+            @ResponseCode(code = 403, condition = "Operation forbidden on default"),
+            @ResponseCode(code = 404, condition = "Container Name is not found"),
+            @ResponseCode(code = 409, condition = "Failed to create Container due to Conflicting Name"),
+            @ResponseCode(code = 500, condition = "Failure Reason included in HTTP Error response") })
+    public Response createContainer(@Context UriInfo uriInfo,
+            @PathParam(value = "container") String container,
+            @TypeHint(ContainerConfig.class) ContainerConfig containerConfig) {
+
+        handleAdminAuthorization(getUserName());
+        handleContainerExists(container);
+
+        handleNameMismatch(containerConfig.getContainerName(), container);
+        handleForbiddenOnDefault(container);
+
+        IContainerManager containerManager = getContainerManager();
+        Status status = containerManager.addContainer(containerConfig);
+        if (status.isSuccess()) {
+            NorthboundUtils.auditlog("Container", username, "added", container);
+            return Response.created(uriInfo.getRequestUri()).build();
+        }
+        return NorthboundUtils.getResponse(status);
+    }
+
+    /**
+     * Delete a container
+     *
+     * @param container
+     *            name of the Container (eg. green)
+     * @return Response as dictated by the HTTP Response code
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/green
+     *
+     * </pre>
+     */
+    @Path("/container/{container}")
+    @DELETE
+    @StatusCodes({
+            @ResponseCode(code = 204, condition = "Container deleted successfully"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+            @ResponseCode(code = 403, condition = "Operation forbidden on default"),
+            @ResponseCode(code = 404, condition = "The container is not found") })
+    public Response removeContainer(@PathParam(value = "container") String container) {
+
+        handleAdminAuthorization(getUserName());
+        handleForbiddenOnDefault(container);
+        handleContainerNotExists(container);
+        IContainerManager containerManager = getContainerManager();
+        Status status = containerManager.removeContainer(container);
+        if (status.isSuccess()) {
+            NorthboundUtils.auditlog("Container", username, "removed", container);
+            return Response.noContent().build();
+        }
+        return NorthboundUtils.getResponse(status);
+    }
+
+    /**
+     * Get flowspec within a given container
+     *
+     * @param container
+     *            name of the Container (eg. green)
+     * @param name
+     *            name of the flowspec (eg. ssh)
+     * @return flowspec detail as specified by:
+     *         {@link org.opendaylight.controller.containermanager.ContainerFlowConfig}
+     *
+     *         <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/green/flowspec/ssh
+     *
+     * Response Payload in XML:
+     * &lt;container-flowconfig&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;name&gt;ssh&lt;/name&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;nwSrc&gt;10.0.0.101&lt;/nwSrc&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;nwDst&gt;10.0.0.102&lt;/nwDst&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;IPv4&lt;/protocol&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;tpSrc&gt;80&lt;/tpSrc&gt;
+     *  &#x20;&#x20;&#x20;&#x20;&lt;tpDst&gt;100&lt;/tpDst&gt;
+     * &lt;/container-flowconfig&gt;
+     *
+     * Response Payload in JSON:
+     * { "protocol" : "IPv4", "nwDst" : "10.0.0.102", "name" : "ssh", "nwSrc" : "10.0.0.101", "tpSrc" : "80", "tpDst" : "100" }
+     *
+     * </pre>
+     */
+    @Path("/container/{container}/flowspec/{flowspec}")
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @TypeHint(ContainerFlowConfig.class)
+    @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+            @ResponseCode(code = 404, condition = "The container is not found"),
+            @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
+    public ContainerFlowConfig viewContainerFlowSpec(@PathParam(value = "container") String container,
+            @PathParam(value = "flowspec") String flowspec) {
+
+        handleContainerAuthorization(container, getUserName());
+        handleForbiddenOnDefault(container);
+
+        handleContainerNotExists(container);
+        IContainerManager containerManager = getContainerManager();
+        List<ContainerFlowConfig> flowSpecs = containerManager.getContainerFlows(container);
+
+        for (ContainerFlowConfig containerFlowConfig : flowSpecs) {
+            if (containerFlowConfig.equalsByName(flowspec)) {
+                return containerFlowConfig;
+            }
+        }
+        throw new ResourceNotFoundException("Flow Spec not found");
+    }
+
+    /**
+     * Get all the flowspec in a given container
+     *
+     * @param container
+     *            name of the Container (eg. red)
+     * @return list of all flowspec configured for a container. Flowspec as
+     *         specified by:
+     *         {@link org.opendaylight.controller.containermanager.ContainerFlowConfig}
+     *
+     *         <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/red/flowspec
+     *
+     * Response Payload in XML:
+     * &lt;container-flowconfigs&gt;
+     *   &#x20;&#x20;&#x20;&#x20;&lt;container-flowconfig&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;ssh&lt;/name&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nwSrc&gt;10.0.0.101&lt;/nwSrc&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nwDst&gt;10.0.0.102&lt;/nwDst&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;IPv4&lt;/protocol&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tpSrc&gt;23&lt;/tpSrc&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tpDst&gt;100&lt;/tpDst&gt;
+     *   &#x20;&#x20;&#x20;&#x20;&lt;/container-flowconfig&gt;
+     *   &#x20;&#x20;&#x20;&#x20;&lt;container-flowconfig&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;http2&lt;/name&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nwSrc&gt;10.0.0.201&lt;/nwSrc&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nwDst&gt;10.0.0.202&lt;/nwDst&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;&lt;/protocol&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tpSrc&gt;80&lt;/tpSrc&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tpDst&gt;100&lt;/tpDst&gt;
+     *   &#x20;&#x20;&#x20;&#x20;&lt;/container-flowconfig&gt;
+     * &lt;/container-flowconfigs&gt;
+     *
+     * Response Payload in JSON:
+     * { "protocol" : "IPv4", "nwDst" : "10.0.0.102", "name" : "ssh" , "nwSrc" : "10.0.0.101", "tpSrc" : "23", "tpDst" : "100" }
+     * { "protocol" : "", "nwDst" : "10.0.0.202", "name" : "http" , "nwSrc" : "10.0.0.201", "tpSrc" : "80", "tpDst" : "100" }
+     *
+     * </pre>
+     */
+    @Path("/container/{container}/flowspec")
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @TypeHint(FlowSpecConfigs.class)
+    @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 404, condition = "The container is not found"),
+            @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
+    public FlowSpecConfigs viewContainerFlowSpecs(@PathParam(value = "container") String container) {
+
+        handleContainerAuthorization(container, getUserName());
+        handleForbiddenOnDefault(container);
+
+        handleContainerNotExists(container);
+
+        IContainerManager containerManager = getContainerManager();
+
+        return new FlowSpecConfigs(containerManager.getContainerFlows(container));
+    }
+
+    /**
+     * Add flowspec to a container
+     *
+     * @param container
+     *            name of the container (eg. purple)
+     * @param name
+     *            name of the flowspec (eg. http)
+     * @param flowspec
+     *            configuration as specified by:
+     *            {@link org.opendaylight.controller.containermanager.ContainerFlowConfig}
+     *
+     * @return Response as dictated by the HTTP Response code
+     *
+     *         <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/purple/flowspec/http
+     *
+     * Request Payload in XML:
+     *   &lt;container-flowconfig&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&lt;name&gt;http&lt;/name&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&lt;nwSrc&gt;10.0.0.101&lt;/nwSrc&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&lt;nwDst&gt;10.0.0.102&lt;/nwDst&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;&lt;/protocol&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&lt;tpSrc&gt;80&lt;/tpSrc&gt;
+     *     &#x20;&#x20;&#x20;&#x20;&lt;tpDst&gt;100&lt;/tpDst&gt;
+     *   &lt;/container-flowconfig&gt;
+     *
+     * Request Payload in JSON:
+     * { "protocol" : "", "nwDst" : "10.0.0.102", "name" : "http", "nwSrc" : "10.0.0.101", "tpSrc" : "80", "tpDst" : "100" }
+     *
+     * </pre>
+     */
+    @Path("/container/{container}/flowspec/{flowspec}")
+    @PUT
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @StatusCodes({
+            @ResponseCode(code = 201, condition = "FlowSpec created successfully"),
+            @ResponseCode(code = 400, condition = "Invalid flowspec configuration"),
+            @ResponseCode(code = 404, condition = "The container is not found"),
+            @ResponseCode(code = 409, condition = "Container Entry already exists"),
+            @ResponseCode(code = 500, condition = "Failed to create Flow specifications. Failure Reason included in HTTP Error response") })
+    public Response createFlowSpec(@Context UriInfo uriInfo,
+            @PathParam(value = "container") String container,
+            @PathParam(value = "flowspec") String flowspec,
+            @TypeHint(ContainerFlowConfig.class) ContainerFlowConfig containerFlowConfig) {
+
+        handleAdminAuthorization(getUserName());
+        handleForbiddenOnDefault(container);
+
+        handleContainerNotExists(container);
+        handleNameMismatch(containerFlowConfig.getName(), flowspec);
+
+        IContainerManager containerManager = getContainerManager();
+        List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
+        list.add(containerFlowConfig);
+        Status status = containerManager.addContainerFlows(container, list);
+        if (status.isSuccess()) {
+            NorthboundUtils.auditlog("Flow Spec", username, "added", containerFlowConfig.getName());
+            return Response.created(uriInfo.getRequestUri()).build();
+        }
+        return NorthboundUtils.getResponse(status);
+    }
+
+    /**
+     * Remove flowspec from a container
+     *
+     * @param name
+     *            name of the flowspec (eg. telnet)
+     * @param container
+     *            name of the Container (eg. black)
+     * @return Response as dictated by the HTTP Response code
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/black/flowspec/telnet
+     *
+     * </pre>
+     */
+    @Path("/container/{container}/flowspec/{flowspec}")
+    @DELETE
+    @StatusCodes({
+            @ResponseCode(code = 204, condition = "Flow Spec deleted successfully"),
+            @ResponseCode(code = 400, condition = "Invalid flowspec configuration"),
+            @ResponseCode(code = 404, condition = "Container or Container Entry not found"),
+            @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
+            @ResponseCode(code = 500, condition = "Failed to delete Flowspec. Failure Reason included in HTTP Error response"),
+            @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
+    public Response removeFlowSpec(@PathParam(value = "container") String container,
+            @PathParam(value = "flowspec") String flowspec) {
+
+        handleAdminAuthorization(getUserName());
+        handleForbiddenOnDefault(container);
+
+        handleContainerNotExists(container);
+
+        IContainerManager containerManager = getContainerManager();
+        Set<String> set = new HashSet<String>();
+        set.add(flowspec);
+        Status status = containerManager.removeContainerFlows(container, set);
+        if (status.isSuccess()) {
+            NorthboundUtils.auditlog("Flow Spec", username, "added", flowspec);
+            return Response.noContent().build();
+        }
+        return NorthboundUtils.getResponse(status);
+    }
+
+    /**
+     * Add node connectors to a container
+     *
+     * @param container
+     *            name of the container (eg. green)
+     * @param list
+     *            The list of strings each representing a node connector in the form "<Port Type>|<Port id>@<Node Type>|<Node id>", as "OF|1@OF|00:00:00:ab:00:00:00:01"
+     * @return response as dictated by the HTTP Status code
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/green/nodeconnector
+     *
+     * Request Payload in XML:
+     * &lt;list&gt;
+     *     &lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
+     *     &lt;nodeConnectors&gt;OF|2@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
+     *     &lt;nodeConnectors&gt;OF|3@OF|00:00:00:00:00:00:00:22&lt;/nodeConnectors&gt;
+     *     &lt;nodeConnectors&gt;OF|4@OF|00:00:00:00:00:00:00:22&lt;/nodeConnectors&gt;
+     * &lt;/list&gt;
+     *
+     * Request Payload in JSON:
+     * { "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|2@OF|00:00:00:00:00:00:00:01", "OF|3@OF|00:00:00:00:00:00:00:22", "OF|4@OF|00:00:00:00:00:00:00:22" }
+     *
+     * </pre>
+     */
+    @Path("/container/{container}/nodeconnector/")
+    @PUT
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @TypeHint(Response.class)
+    @StatusCodes({
+            @ResponseCode(code = 200, condition = "NodeConnectors added successfully"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+            @ResponseCode(code = 403, condition = "Operation forbidden on default"),
+            @ResponseCode(code = 404, condition = "The Container is not found"),
+            @ResponseCode(code = 409, condition = "Container Entry already exists"),
+            @ResponseCode(code = 500, condition = "Failed to create nodeconnectors. Failure Reason included in HTTP Error response") })
+    public Response addNodeConnectors(@PathParam(value = "container") String container,
+            @TypeHint(StringList.class) StringList list) {
+
+        handleAdminAuthorization(getUserName());
+        handleForbiddenOnDefault(container);
+        handleContainerNotExists(container);
+
+        IContainerManager containerManager = getContainerManager();
+        Status status = containerManager.addContainerEntry(container, list.getList());
+        if (status.isSuccess()) {
+            NorthboundUtils.auditlog("Node ", username, "added", " Ports:" + list.getList());
+        }
+        return NorthboundUtils.getResponse(status);
+    }
+
+    /**
+     * Remove node connectors from a container
+     *
+     * @param container
+     *            name of the container (eg. red)
+     * @param list
+     *            The list of strings each representing a node connector in the form "<Port Type>|<Port id>@<Node Type>|<Node id>", as "OF|1@OF|00:00:00:ab:00:00:00:01"
+     * @return response as dictated by the HTTP Status code
+     *
+     *         <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/containermanager/container/red/nodeconnector
+     *
+     * Request Payload in XML:
+     * &lt;list&gt;
+     *     &lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
+     *     &lt;nodeConnectors&gt;OF|2@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
+     *     &lt;nodeConnectors&gt;OF|3@OF|00:00:00:00:00:00:00:22&lt;/nodeConnectors&gt;
+     *     &lt;nodeConnectors&gt;OF|4@OF|00:00:00:00:00:00:00:22&lt;/nodeConnectors&gt;
+     * &lt;/list&gt;
+     *
+     * Request Payload in JSON:
+     * { "nodeConnectors" : ["OF|1@OF|00:00:00:00:00:00:00:01", "OF|2@OF|00:00:00:00:00:00:00:01", "OF|3@OF|00:00:00:00:00:00:00:22", "OF|4@OF|00:00:00:00:00:00:00:22" }
+     *
+     * </pre>
+     */
+    @Path("/container/{container}/nodeconnector/")
+    @DELETE
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @StatusCodes({
+            @ResponseCode(code = 204, condition = "Container Entry deleted successfully"),
+            @ResponseCode(code = 400, condition = "Invalid Container Entry configuration"),
+            @ResponseCode(code = 404, condition = "The Container is not found"),
+            @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
+            @ResponseCode(code = 500, condition = "Failed to delete node connector. Failure Reason included in HTTP Error response") })
+    public Response removeNodeConnectors(@PathParam(value = "container") String container,
+            @TypeHint(StringList.class) StringList portList) {
+
+        handleAdminAuthorization(getUserName());
+        handleForbiddenOnDefault(container);
+        handleContainerNotExists(container);
+
+        IContainerManager containerManager = getContainerManager();
+        Status status = containerManager.removeContainerEntry(container, portList.getList());
+        if (status.isSuccess()) {
+            NorthboundUtils.auditlog("Node", username, "removed", " Ports:" + portList.getList());
+            return Response.noContent().build();
+        }
+        return NorthboundUtils.getResponse(status);
+    }
+
+    /*
+     * Check If the function is not allowed on default container, Throw a
+     * ResourceForbiddenException exception if forbidden
+     */
+    private void handleForbiddenOnDefault(String container) {
+        if (container.equalsIgnoreCase(GlobalConstants.DEFAULT.toString())) {
+            throw new ResourceForbiddenException(RestMessages.NODEFAULT.toString() + ": " + container);
+        }
+    }
+
+    /*
+     * Check if container exists, Throw a ResourceNotFoundException exception if it
+     * does not exist
+     */
+    private void handleContainerNotExists(String container) {
+        IContainerManager containerManager = getContainerManager();
+        if (!containerManager.doesContainerExist(container)) {
+            throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString() + ": " + container);
+        }
+    }
+
+    private void handleContainerExists(String container) {
+        IContainerManager containerManager = getContainerManager();
+        if (containerManager.doesContainerExist(container)) {
+            throw new ResourceConflictException(RestMessages.RESOURCECONFLICT.toString() + ": " + container);
+        }
+    }
+
+    private void handleAdminAuthorization(String userName) {
+        IUserManager usrMgr = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
+
+        UserLevel level = usrMgr.getUserLevel(userName);
+        if (level.ordinal() <= UserLevel.NETWORKADMIN.ordinal()) {
+            return;
+        }
+
+        throw new UnauthorizedException("User is not authorized to perform this operation");
+    }
+
+    private void handleNetworkAuthorization(String userName) {
+        IUserManager usrMgr = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
+
+        UserLevel level = usrMgr.getUserLevel(userName);
+        if (level.ordinal() <= UserLevel.NETWORKOPERATOR.ordinal()) {
+            return;
+        }
+        throw new UnauthorizedException("User is not authorized to perform this operation");
+    }
+
+    private void handleContainerAuthorization(String container, String userName) {
+        IContainerAuthorization auth = (IContainerAuthorization) ServiceHelper.getGlobalInstance(
+                IContainerAuthorization.class, this);
+
+        UserLevel level = auth.getUserLevel(userName);
+        if (level.ordinal() <= UserLevel.NETWORKOPERATOR.ordinal()) {
+            return;
+        }
+
+        Privilege current = (auth == null) ? Privilege.NONE : auth.getResourcePrivilege(userName, container);
+
+        if (current.ordinal() > Privilege.NONE.ordinal()) {
+            return;
+        }
+        throw new UnauthorizedException("User is not authorized to perform this operation");
+    }
+
+}
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 (file)
index 0000000..10559a2
--- /dev/null
@@ -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<Class<?>> getClasses() {
+        Set<Class<?>> classes = new HashSet<Class<?>>();
+        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 (file)
index 0000000..cc75055
--- /dev/null
@@ -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> containerFlowConfig;
+
+    // To satisfy JAXB
+    @SuppressWarnings("unused")
+    private FlowSpecConfigs() {
+
+    }
+
+    public FlowSpecConfigs(List<ContainerFlowConfig> containerFlowConfig) {
+        this.containerFlowConfig = containerFlowConfig;
+    }
+
+    public List<ContainerFlowConfig> getContainerFlowConfig() {
+        return containerFlowConfig;
+    }
+
+    public void setContainerFlowConfig(List<ContainerFlowConfig> containerFlowConfig) {
+        this.containerFlowConfig = containerFlowConfig;
+    }
+}
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 (file)
index 0000000..7cba528
--- /dev/null
@@ -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<String> list;
+
+    public StringList() {
+    }
+
+    public StringList(List<String> list) {
+        this.list = list;
+    }
+
+    public List<String> 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 (file)
index 0000000..93db02e
--- /dev/null
@@ -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 (file)
index 0000000..6aeb8ca
--- /dev/null
@@ -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 (file)
index 0000000..acf748a
--- /dev/null
@@ -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 (file)
index 0000000..057d834
--- /dev/null
@@ -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 (file)
index 0000000..6ace7ec
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
+http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+  version="2.4">
+<servlet>
+      <servlet-name>JAXRSContainerManager</servlet-name>
+      <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+      <init-param>
+        <param-name>javax.ws.rs.Application</param-name>
+        <param-value>org.opendaylight.controller.containermanager.northbound.ContainerManagerNorthboundRSApplication</param-value>
+      </init-param>
+      <load-on-startup>1</load-on-startup>
+</servlet>
+    <servlet-mapping>
+      <servlet-name>JAXRSContainerManager</servlet-name>
+      <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-role>
+        <role-name>System-Admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>Network-Admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>Network-Operator</role-name>
+    </security-role>
+    <security-role>
+        <role-name>Container-User</role-name>
+    </security-role>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>opendaylight</realm-name>
+    </login-config>
+</web-app>
index 960c835..2c729ba 100644 (file)
@@ -71,7 +71,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index d39ae17..c1f6598 100644 (file)
@@ -77,7 +77,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 90b36de..ffbce7b 100644 (file)
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
-      <artifactId>containermanager.implementation</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <artifactId>containermanager.it.implementation</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 38336c1..59065fc 100644 (file)
@@ -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(),
index b7da964..a8d81a7 100644 (file)
@@ -94,7 +94,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.enunciate</groupId>
index 62f5b42..eb285f2 100644 (file)
@@ -77,7 +77,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index eb2bf58..76129f4 100644 (file)
@@ -80,7 +80,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 4715664..c3ad783 100644 (file)
@@ -92,7 +92,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 6b320b1..a416414 100644 (file)
@@ -77,7 +77,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index b2b7b9c..803a280 100644 (file)
@@ -79,7 +79,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 5ec7202..2ccb440 100644 (file)
@@ -77,7 +77,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index a755f83..bddcc2a 100644 (file)
@@ -52,7 +52,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 9b3dd0e..8be254e 100644 (file)
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index ebe9365..527dd0a 100644 (file)
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
-      <artifactId>containermanager.implementation</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <artifactId>containermanager.it.implementation</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 43c297d..457e99d 100644 (file)
@@ -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(),
index d057c8e..d571d9d 100644 (file)
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
-      <artifactId>containermanager.implementation</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <artifactId>containermanager.it.implementation</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index eb8a07e..2a40eb0 100644 (file)
@@ -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")
index c5f78ab..eb4bea5 100644 (file)
@@ -71,7 +71,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index fb96928..25e558e 100644 (file)
@@ -75,7 +75,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 64e8ff5..b322f58 100644 (file)
@@ -97,7 +97,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 52b3ef3..0f1f0cf 100644 (file)
@@ -97,7 +97,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index bc5c73a..98c740f 100644 (file)
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index f2f7a3e..c4c66e4 100644 (file)
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
index 38b8249..2be78c4 100644 (file)
@@ -99,7 +99,7 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>containermanager</artifactId>
-      <version>0.4.0-SNAPSHOT</version>
+      <version>0.5.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>