Merge "Declare a property for commons-lang version in poms and use it."
authorGiovanni Meo <gmeo@cisco.com>
Thu, 12 Sep 2013 14:41:27 +0000 (14:41 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 12 Sep 2013 14:41:27 +0000 (14:41 +0000)
105 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/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/ICoordinatorChangeAware.java
opendaylight/clustering/services/src/main/java/org/opendaylight/controller/clustering/services/IListenRoleChange.java
opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClassResolver.java [new file with mode: 0644]
opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClusterManager.java
opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationAwareCommon.java
opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationServiceCommon.java
opendaylight/configuration/integrationtest/pom.xml
opendaylight/configuration/integrationtest/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationIT.java
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.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/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IForwardingStaticRouting.java
opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/IStaticRoutingAware.java
opendaylight/forwarding/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/internal/StaticRoutingImplementation.java
opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/PortGroupProvider.java
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/hosttracker_new/api/src/main/java/org/opendaylight/controller/hosttracker/Entity.java
opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/Device.java
opendaylight/hosttracker_new/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/DeviceManagerImpl.java
opendaylight/northbound/commons/pom.xml
opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/types/StringList.java [new file with mode: 0644]
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/enunciate.xml
opendaylight/northbound/flowprogrammer/pom.xml
opendaylight/northbound/flowprogrammer/src/main/java/org/opendaylight/controller/flowprogrammer/northbound/FlowProgrammerNorthbound.java
opendaylight/northbound/hosttracker/pom.xml
opendaylight/northbound/hosttracker/src/main/java/org/opendaylight/controller/hosttracker/northbound/HostTrackerNorthbound.java
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/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/northbound/StaticRoutingNorthbound.java
opendaylight/northbound/statistics/pom.xml
opendaylight/northbound/statistics/src/main/java/org/opendaylight/controller/statistics/northbound/StatisticsNorthbound.java
opendaylight/northbound/subnets/enunciate.xml
opendaylight/northbound/subnets/pom.xml
opendaylight/northbound/subnets/src/main/java/org/opendaylight/controller/subnets/northbound/SubnetsNorthbound.java
opendaylight/northbound/switchmanager/enunciate.xml
opendaylight/northbound/switchmanager/pom.xml
opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthbound.java
opendaylight/northbound/topology/enunciate.xml
opendaylight/northbound/topology/pom.xml
opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java
opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/DiscoveryService.java
opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/InventoryServiceShim.java
opendaylight/sal/yang-prototype/sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java [new file with mode: 0644]
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/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.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/root/src/main/java/org/opendaylight/controller/web/ClusterNodeBean.java
opendaylight/web/root/src/main/java/org/opendaylight/controller/web/DaylightWebAdmin.java
opendaylight/web/root/src/main/resources/js/open.js
opendaylight/web/topology/pom.xml
opendaylight/web/troubleshoot/pom.xml
third-party/openflowj/src/main/java/org/openflow/protocol/OFError.java
third-party/openflowj/src/main/java/org/openflow/protocol/action/ActionVendorOutputNextHop.java

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 50d6f5c7d5f88a632ca36ad5c401f0d63c6cd7e2..0fadb128901cd16749511f32c58e2ea71973fc75 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 8cbebe5134c288bf8ce23cd599cc8e39f7100a3e..5c8b096b3e89fcfb16ecab627303f4cd934ceffd 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 4a92aca6049d76bd1e1694667cb1c80bf73d4ae8..5d0edd8d009ea03280fc1ce3f70027bf4b722644 100644 (file)
@@ -28,5 +28,5 @@ public interface ICoordinatorChangeAware {
      * Function that will be called when there is the event of
      * coordinator change in the cluster.
      */
-    public void coordinatorChanged();
+    void coordinatorChanged();
 }
index e5c7a8839e9ef4c76e6c7be6e36584b096cdeb10..b8798e6705b48997cf561acea0db707e25c8665c 100644 (file)
@@ -37,5 +37,5 @@ public interface IListenRoleChange {
      * active-standby milestone is reached, after will be removed.
      *
      */
-    public void newActiveAvailable();
+    void newActiveAvailable();
 }
diff --git a/opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClassResolver.java b/opendaylight/clustering/services_implementation/src/main/java/org/opendaylight/controller/clustering/services_implementation/internal/ClassResolver.java
new file mode 100644 (file)
index 0000000..521a773
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.clustering.services_implementation.internal;
+
+import java.lang.ref.WeakReference;
+
+import org.jboss.marshalling.ContextClassResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ClassResolver extends ContextClassResolver {
+    private WeakReference<ClassLoader> osgiClassLoader = null;
+    private static final Logger logger = LoggerFactory.getLogger(ClassResolver.class);
+
+    public ClassResolver() {
+        ClassLoader cl = this.getClass()
+                .getClassLoader();
+        if (cl != null) {
+            this.osgiClassLoader = new WeakReference<ClassLoader>(cl);
+            logger.trace("Acquired weak reference to OSGi classLoader {}", cl);
+        }
+    }
+
+    @Override
+    protected ClassLoader getClassLoader() {
+        ClassLoader ret = null;
+        if (this.osgiClassLoader != null) {
+            ret = this.osgiClassLoader.get();
+            if (ret != null) {
+                if (logger.isTraceEnabled()) {
+                    logger.trace("Returning OSGi class loader {}", ret);
+                }
+                return ret;
+            }
+        }
+
+        logger.warn("Could not resolve classloader!");
+        return ret;
+    }
+}
index f5c655a4eae8fb52afcefad92507177ff1cc479a..cd3a29579194c9a6a9831f4e44d7e67150b69f6d 100644 (file)
@@ -33,6 +33,9 @@ import javax.transaction.TransactionManager;
 
 import org.infinispan.Cache;
 import org.infinispan.configuration.cache.Configuration;
+import org.infinispan.configuration.global.GlobalConfigurationBuilder;
+import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
+import org.infinispan.configuration.parsing.ParserRegistry;
 import org.infinispan.manager.DefaultCacheManager;
 import org.infinispan.manager.EmbeddedCacheManager;
 import org.infinispan.notifications.Listener;
@@ -247,8 +250,14 @@ public class ClusterManager implements IClusterServices, IContainerAware {
         }
         logger.info("Starting the ClusterManager");
         try {
-            //FIXME keeps throwing FileNotFoundException
-            this.cm = new DefaultCacheManager("config/infinispan-config.xml");
+            ParserRegistry parser = new ParserRegistry(this.getClass()
+                    .getClassLoader());
+            ConfigurationBuilderHolder holder = parser.parseFile("config/infinispan-config.xml");
+            GlobalConfigurationBuilder globalBuilder = holder.getGlobalConfigurationBuilder();
+            globalBuilder.serialization()
+                    .classResolver(new ClassResolver())
+                    .build();
+            this.cm = new DefaultCacheManager(holder, false);
             logger.debug("Allocated ClusterManager");
             if (this.cm != null) {
                 this.cm.start();
index 57a648bf69663da84a0a26f28c60252420c6f808..f06dcb3296dfb4ef402e49a7e86e07e5d5e0232f 100644 (file)
@@ -22,5 +22,5 @@ public interface IConfigurationAwareCommon {
     /**
      * Trigger from configuration component to persist the configuration state.
      */
-    public Status saveConfiguration();
+    Status saveConfiguration();
 }
index fc9c5ac9777d20b55618e310b592b7170b1a2531..c76e3770264aecbf32499eb876502ea9b6daccf1 100644 (file)
@@ -17,5 +17,5 @@ import org.opendaylight.controller.sal.utils.Status;
  *
  */
 public interface IConfigurationServiceCommon {
-    public Status saveConfigurations();
+    Status saveConfigurations();
 }
index 51ff2a67030e743cf19aec292a5f6e4f4bf362d0..19a1e66422a581fd0dcb66be4d446d962bad1534 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 a2f22973255f7e4bfb10ce911e0d1d8682a00923..b0e351ae16e29c029b13e29b4fe52c22ad216613 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 9cbddbb13046202e482a6765c810bfd4e3507bd3..7097958126aaec02cf680265bad8322b6526ae5e 100644 (file)
@@ -352,16 +352,21 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene
         String controller = ci.nextArgument();
         if (controller == null) {
             ci.println("Nodes connected to this controller : ");
-            if (this.getLocalNodes() == null) ci.println("None");
-            else ci.println(this.getLocalNodes().toString());
+            if (this.getLocalNodes() == null) {
+                ci.println("None");
+            } else {
+                ci.println(this.getLocalNodes().toString());
+            }
             return;
         }
         try {
             InetAddress address = InetAddress.getByName(controller);
             ci.println("Nodes connected to controller "+controller);
-            if (this.getNodes(address) == null) ci.println("None");
-            else ci.println(this.getNodes(address).toString());
-            return;
+            if (this.getNodes(address) == null) {
+                ci.println("None");
+            } else {
+                ci.println(this.getNodes(address).toString());
+            }
         } catch (UnknownHostException e) {
            logger.error("An error occured",e);
         }
index f4c7bd2ff77fcda8874ca39db7d7c404ccf04aa7..06b72219f7fbddc5b5b17357f421f003691a36f7 100644 (file)
@@ -10,8 +10,6 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
-import javax.transaction.SystemException;
-
 import org.opendaylight.controller.clustering.services.CacheConfigException;
 import org.opendaylight.controller.clustering.services.CacheExistException;
 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
@@ -128,7 +126,6 @@ public abstract class AbstractScheme {
         return controllerNodesMap.get(controller);
     }
 
-    @SuppressWarnings("deprecation")
     public Set<Node> getNodes() {
         return getNodes(clusterServices.getMyAddress());
     }
@@ -142,7 +139,6 @@ public abstract class AbstractScheme {
         return nodeConnections;
     }
 
-    @SuppressWarnings("deprecation")
     public boolean isLocal(Node node) {
         if (nodeConnections == null) return false;
         InetAddress myController = clusterServices.getMyAddress();
@@ -150,7 +146,6 @@ public abstract class AbstractScheme {
         return (controllers != null && controllers.contains(myController));
     }
 
-    @SuppressWarnings("deprecation")
     public Status removeNode (Node node) {
         return removeNodeFromController(node, clusterServices.getMyAddress());
     }
index 65adf68642668691c6eb046b618040cdc3f9dd4b..2b207aa797aa76be72d20e3cc5e108aa20c03e98 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 9300921be9c424dbc2b10146926a9d42dcb81f9c..5daaf00ece4fb4956695c74349d53d471270895a 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 f6909ca61b5f23a4f64bf66dc792eb8d024f9c48..5af8e2265c94dccc540bf430b961ddcad906bca1 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 9caa62072cc88c3b50cfa5a670dc53635d14b5d0..aab07afa9e5d36bed64e22840cc169c3c2b2fee5 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 15f939d39bb853e7f411d3d71814fd21c0d8166b..1dfab887af4779039e677f8a40d9a9d30de9f5ac 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 d47aca8135e69a0037d82080e94ffa69771b33a5..da301ba8c04a9f96669d77e271cf6663c52825c7 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 d44cfc5c0e150c9a51320ddbc80025d3b3995700..56ca0c951be5a0c2a0664a70e7f6ac5aa4ada061 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 a85c3f8860c45c348ff71cc84caac2fe66ea21d2..92a718bac3e386c94b099ab3fdb35590ab851b81 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 0166d279f66528135322f3c8d250ec381bd2de1a..03d9453126d89c8c5d9074bc7ec7b643c089d9a3 100644 (file)
@@ -30,13 +30,13 @@ public interface IForwardingStaticRouting {
      * @param ipAddress (InetAddress) the IP address
      * @return StaticRoute
      */
-    public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress);
+    StaticRoute getBestMatchStaticRoute(InetAddress ipAddress);
 
     /**
      * Returns all the StaticRouteConfig
      * @return all the StaticRouteConfig
      */
-    public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs();
+    ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs();
 
     /**
      * Adds a StaticRouteConfig
@@ -44,7 +44,7 @@ public interface IForwardingStaticRouting {
      * @return a text string indicating the result of the operation..
      * If the operation is successful, the return string will be "SUCCESS"
      */
-    public Status addStaticRoute(StaticRouteConfig config);
+    Status addStaticRoute(StaticRouteConfig config);
 
     /**
      * Removes  the named StaticRouteConfig
@@ -52,5 +52,5 @@ public interface IForwardingStaticRouting {
      * @return a text string indicating the result of the operation.
      * If the operation is successful, the return string will be "SUCCESS"
      */
-    public Status removeStaticRoute(String name);
+    Status removeStaticRoute(String name);
 }
index f650ee9e7748cc1a02a0a73191f9a2f22f0aea46..fb4863b1e04e2c0674068f224b65348d202044cd 100644 (file)
@@ -21,5 +21,5 @@ public interface IStaticRoutingAware {
      * @param s: StaticRoute
      * @param added: boolean true if the static route is added,
      */
-    public void staticRouteUpdate(StaticRoute s, boolean added);
+    void staticRouteUpdate(StaticRoute s, boolean added);
 }
index 1b2128957e9352ffd93a0a0991abf91ec3a354ea..89d24192455231487b926ce1b727b9cb76c4c71d 100644 (file)
@@ -63,7 +63,6 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
     private static Logger log = LoggerFactory
             .getLogger(StaticRoutingImplementation.class);
     private static String ROOT = GlobalConstants.STARTUPHOME.toString();
-    private static final String SAVE = "Save";
     ConcurrentMap<String, StaticRoute> staticRoutes;
     ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs;
     private IfIptoHost hostTracker;
index 7b53eb0b8aa5b157555c87577eed9b9c27cf1eb0..dc4f13f497f5e5a0fce7b8035b71cd40512834b1 100644 (file)
@@ -30,7 +30,7 @@ public interface PortGroupProvider {
      *            New PortGroupConfig object created by user Configuration.
      * @return true if successful. false otherwise.
      */
-    public boolean createPortGroupConfig(PortGroupConfig config);
+    boolean createPortGroupConfig(PortGroupConfig config);
 
     /**
      * This method is invoked by the Controller towards the Provider when an
@@ -40,7 +40,7 @@ public interface PortGroupProvider {
      *            Existing Port Group Configuration deleted by the user.
      * @return true if successful. false otherwise.
      */
-    public boolean deletePortGroupConfig(PortGroupConfig config);
+    boolean deletePortGroupConfig(PortGroupConfig config);
 
     /**
      * Returns the complete mapping database corresponds to a PortGroup
@@ -54,7 +54,7 @@ public interface PortGroupProvider {
      * @return Database of Switch-Id to PortGroup mapping that corresponds to
      *         the Port Group User Configuration.
      */
-    public Map<Node, PortGroup> getPortGroupData(PortGroupConfig config);
+    Map<Node, PortGroup> getPortGroupData(PortGroupConfig config);
 
     /**
      * Returns PortGroup data for a given Switch and user Configuration. Its the
@@ -70,7 +70,7 @@ public interface PortGroupProvider {
      * @return PortGroup data for a given Openflow switch.
      * @see PortGroup
      */
-    public PortGroup getPortGroupData(PortGroupConfig config, long matrixSwitchId);
+    PortGroup getPortGroupData(PortGroupConfig config, long matrixSwitchId);
 
     /**
      * Registers a Listener for Port Group membership changes based on Custom
@@ -80,7 +80,7 @@ public interface PortGroupProvider {
      *            A Controller module that listens to events from the Custom
      *            Port Grouping Application.
      */
-    public void registerPortGroupChange(PortGroupChangeListener listener);
+    void registerPortGroupChange(PortGroupChangeListener listener);
 
     /**
      * Application returns an Usage string for the Match Criteria User
@@ -91,7 +91,7 @@ public interface PortGroupProvider {
      *
      * @return Usage string.
      */
-    public String getApplicationDrivenMatchCriteriaUsage();
+    String getApplicationDrivenMatchCriteriaUsage();
 
     /**
      * Returns the name of the Custom Application that implements
@@ -99,7 +99,7 @@ public interface PortGroupProvider {
      *
      * @return Provider Name
      */
-    public String getProviderName();
+    String getProviderName();
 
     /**
      * Controller uses this method to check with the Provider supports the
@@ -109,5 +109,5 @@ public interface PortGroupProvider {
      * @return true if the Provider supports the matchCriteria String. false
      *         otherwise.
      */
-    public boolean isMatchCriteriaSupported(String matchCriteria);
+    boolean isMatchCriteriaSupported(String matchCriteria);
 }
index b0ffaacec864aecfe65c20da8a80cc2b3fa3c34a..77792435991ce414a6edf14590b7f9fe071a9b56 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 4fc3afc726e1b5fdcd4c6f0d6399210cb4b4a66f..ef2e0fa01d3b14c98fa1fe51bdf4f9168e5f4f60 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 351e31b11e6d4aefcbdbdb164ed0b92c8e95ff04..81551650c34fe2a0eb49d0afeb2405ec75de4549 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 57e2714ff1c23bf3aded4c40d4b98ae96ed0135c..4281d0db1610f9b5cb50054d248b1df977f41e6a 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(),
index 924d0717e966aa4731aa340e67a54efe18cbf991..64f4c7ef1e1d65bddcafd7310dccfe6b44278741 100644 (file)
@@ -244,10 +244,12 @@ public class Entity implements Comparable<Entity> {
     @Override
     public int compareTo(Entity o) {
         int r;
-        if (port == null)
+        if (port == null) {
             r = o.port == null ? 0 : -1;
-        else if (o.port == null)
+        }
+        else if (o.port == null) {
             r = 1;
+        }
         else {
             // XXX - the node id is only defined as an object rather
             // than something useful. We're just going to have to
index fb81cddc964c3887ad30ee030f8600140faea922..b2180297da3ca1438a522bc345aa491cccbdcc54 100755 (executable)
@@ -181,7 +181,8 @@ public class Device implements IDevice {
      * @param newEntity
      *            the entity to add. newEntity must be have the same entity
      *            class as device
-     * @param if positive indicates the index in the entities array were the new
+     * @param insertionpoint
+     *        if positive indicates the index in the entities array were the new
      *        entity should be inserted. If negative we will compute the correct
      *        insertion point
      */
@@ -240,10 +241,11 @@ public class Device implements IDevice {
 
         TreeSet<Short> vals = new TreeSet<Short>();
         for (Entity e : entities) {
-            if (e.getVlan() == null)
+            if (e.getVlan() == null) {
                 vals.add((short) -1);
-            else
+            } else {
                 vals.add(e.getVlan());
+            }
         }
         return vals.toArray(new Short[vals.size()]);
     }
@@ -313,15 +315,16 @@ public class Device implements IDevice {
             return false;
 
         for (AttachmentPoint ap : apList) {
-            if (ap.getLastSeen() + AttachmentPoint.INACTIVITY_INTERVAL < System
-                    .currentTimeMillis())
-                expiredAPs.add(ap);
+            if (ap.getLastSeen() + AttachmentPoint.INACTIVITY_INTERVAL < System.currentTimeMillis()) {
+               expiredAPs.add(ap);
+            }
         }
         if (expiredAPs.size() > 0) {
             apList.removeAll(expiredAPs);
             return true;
-        } else
+        } else {
             return false;
+        }
     }
 
     /**
@@ -410,7 +413,6 @@ public class Device implements IDevice {
      * any change to the list of attachment points for the device -- which
      * indicates a device move.
      *
-     * @param sw
      * @param port
      * @param lastSeen
      * @return
@@ -525,7 +527,6 @@ public class Device implements IDevice {
     /**
      * Delete (sw,port) from the list of list of attachment points and oldAPs.
      *
-     * @param sw
      * @param port
      * @return
      */
@@ -703,10 +704,12 @@ public class Device implements IDevice {
         TreeSet<Short> vals = new TreeSet<Short>();
         for (Entity e : entities) {
             if (e.getPort().equals(swp.getPort())) {
-                if (e.getVlan() == null)
+                if (e.getVlan() == null) {
                     vals.add(VLAN_UNTAGGED);
-                else
+                }
+                else  {
                     vals.add(e.getVlan());
+                }
             }
         }
         return vals.toArray(new Short[vals.size()]);
index 95d33ceef90fcaf019cead9ffe9a2a97858e8e47..03909076164561b84b7685771479e515d0dbc3f4 100755 (executable)
@@ -385,11 +385,11 @@ public class DeviceManagerImpl implements IDeviceService, IEntityClassListener,
             long newDomain = 0;
             boolean newBD = false;
 
-            if (oldDomain < newDomain)
-                return -1;
-            else if (oldDomain > newDomain)
+            if (oldDomain < newDomain) {
+               return -1;
+            } else if (oldDomain > newDomain) {
                 return 1;
-
+            }
             // Give preference to OFPP_LOCAL always
             if (!oldAP.getPort().getType().equals(NodeConnectorIDType.SWSTACK)
                     && newAP.getPort().getType()
index 6031ddea35837765eb0897c7e031c996db905802..b88375ffdc9390c8dba9801f65fd69076f68a588 100644 (file)
           <instructions>
             <Export-Package>
               org.opendaylight.controller.northbound.commons.exception,
+              org.opendaylight.controller.northbound.commons.types,
               org.opendaylight.controller.northbound.commons.utils,
               org.opendaylight.controller.northbound.commons
             </Export-Package>
             <Import-Package>
               javax.ws.rs,
               javax.ws.rs.core,
+              javax.xml.bind.annotation,
               org.opendaylight.controller.sal.utils,
               org.opendaylight.controller.sal.authorization,
               org.opendaylight.controller.containermanager,
diff --git a/opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/types/StringList.java b/opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/types/StringList.java
new file mode 100644 (file)
index 0000000..62d1608
--- /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.northbound.commons.types;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Represents a list of string values
+ */
+@XmlRootElement(name = "list")
+@XmlAccessorType(XmlAccessType.NONE)
+public class StringList {
+    @XmlElement(name = "item")
+    private List<String> list;
+
+    public StringList() {
+    }
+
+    public StringList(List<String> list) {
+        this.list = list;
+    }
+
+    public List<String> getList() {
+        return list;
+    }
+
+}
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 90bf6de1d67e13b542fb4d6a2e1a6896995d0e8e..3ac676907bae795ff14404db66cc77dbe5bf5c09 100644 (file)
@@ -3,7 +3,7 @@
     xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd">
 
   <services>
-    <rest defaultRestSubcontext="/controller/nb/v2/flow"/>
+    <rest defaultRestSubcontext="/controller/nb/v2/flowprogrammer"/>
   </services>
 
   <modules>
index 7aef0652fe2411637fb69c777a35ba37c835fa23..2c729ba115d2ae0477d1158388f82a104316255c 100644 (file)
@@ -60,7 +60,7 @@
             </Import-Package>
             <Export-Package>
             </Export-Package>
-            <Web-ContextPath>/controller/nb/v2/flow</Web-ContextPath>
+            <Web-ContextPath>/controller/nb/v2/flowprogrammer</Web-ContextPath>
           </instructions>
           <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
         </configuration>
@@ -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 257fbbda670486582949bdb3eab5dd4297ba7d88..ea3f748dcc02e820c6b88aab5f2a10a73fef0cdf 100644 (file)
@@ -48,7 +48,7 @@ import org.opendaylight.controller.sal.utils.Status;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
 
 /**
- * Flow Configuration Northbound API
+ * Flow Configuration Northbound API provides capabilities to program flows.
  *
  * <br>
  * <br>
@@ -56,11 +56,7 @@ import org.opendaylight.controller.switchmanager.ISwitchManager;
  * Authentication realm : <b>opendaylight</b><br>
  * Transport : <b>HTTP and HTTPS</b><br>
  * <br>
- * HTTPS Authentication is disabled by default. Administrator can enable it in
- * tomcat-server.xml after adding a proper keystore / SSL certificate from a
- * trusted authority.<br>
- * More info :
- * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ * HTTPS Authentication is disabled by default.
  *
  */
 @Path("/")
@@ -158,7 +154,7 @@ public class FlowProgrammerNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/flow/default
+     * http://localhost:8080/controller/nb/v2/flowprogrammer/default
      *
      * Response in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
@@ -223,7 +219,7 @@ public class FlowProgrammerNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01
+     * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01
      *
      * Response in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
@@ -296,7 +292,7 @@ public class FlowProgrammerNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+     * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
      *
      * Response in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
@@ -320,7 +316,7 @@ public class FlowProgrammerNorthbound {
      *
      * </pre>
      */
-    @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+    @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
     @GET
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @TypeHint(FlowConfig.class)
@@ -357,7 +353,8 @@ public class FlowProgrammerNorthbound {
     }
 
     /**
-     * Add a flow configuration
+     * Add a flow configuration. If a flow by the given name already
+     * exists, this method will respond with a non-successful status response.
      *
      * @param containerName
      *            Name of the Container (Eg. 'default')
@@ -376,7 +373,7 @@ public class FlowProgrammerNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+     * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
      *
      * Request in XML:
      * &lt;flowConfig&gt;
@@ -400,7 +397,7 @@ public class FlowProgrammerNorthbound {
      * </pre>
      */
 
-    @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+    @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
     @PUT
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
@@ -476,16 +473,16 @@ public class FlowProgrammerNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+     * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
      *
      * </pre>
      */
 
-    @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+    @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
     @DELETE
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
-            @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
+            @ResponseCode(code = 204, condition = "Flow Config deleted successfully"),
             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
             @ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
@@ -523,6 +520,7 @@ public class FlowProgrammerNorthbound {
         Status status = frm.removeStaticFlow(name, node);
         if (status.isSuccess()) {
             NorthboundUtils.auditlog("Flow", username, "removed", name, containerName);
+            return Response.noContent().build();
         }
         return NorthboundUtils.getResponse(status);
     }
@@ -545,11 +543,11 @@ public class FlowProgrammerNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+     * http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/00:00:00:00:00:00:00:01/staticFlow/flow1
      *
      * </pre>
      */
-    @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+    @Path("/{containerName}/node/{nodeType}/{nodeId}/staticFlow/{name}")
     @POST
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
index 3757ea1f36c770304e3d3f6806f5a1a8a713dc6e..c1f6598a8a5b5e977cbede6bf750b1911bee6303 100644 (file)
@@ -61,7 +61,7 @@
               org.codehaus.jackson.jaxrs,
               !org.codehaus.enunciate.jaxrs
             </Import-Package>
-            <Web-ContextPath>/controller/nb/v2/host</Web-ContextPath>
+            <Web-ContextPath>/controller/nb/v2/hosttracker</Web-ContextPath>
           </instructions>
           <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
         </configuration>
@@ -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 f0bdf5d891153fff7271c5853526ae7fd3211a25..836bfa2d60fdd59b62e14d8d8a2c2d9fd247e898 100644 (file)
@@ -62,11 +62,7 @@ import org.opendaylight.controller.switchmanager.ISwitchManager;
  * Authentication realm : <b>opendaylight</b><br>
  * Transport : <b>HTTP and HTTPS</b><br>
  * <br>
- * HTTPS Authentication is disabled by default. Administrator can enable it in
- * tomcat-server.xml after adding a proper keystore / SSL certificate from a
- * trusted authority.<br>
- * More info :
- * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ * HTTPS Authentication is disabled by default.
  *
  */
 
@@ -137,7 +133,7 @@ public class HostTrackerNorthbound {
      *
      * RequestURL:
      *
-     * http://localhost:8080/controller/nb/v2/host/default
+     * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/active
      *
      * Response in XML
      *
@@ -192,7 +188,7 @@ public class HostTrackerNorthbound {
      * }
      * </pre>
      */
-    @Path("/{containerName}")
+    @Path("/{containerName}/hosts/active")
     @GET
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @TypeHint(Hosts.class)
@@ -224,7 +220,7 @@ public class HostTrackerNorthbound {
      *
      * RequestURL:
      *
-     * http://localhost:8080/controller/nb/v2/host/default/inactive
+     * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/inactive
      *
      * Response in XML
      *
@@ -279,7 +275,7 @@ public class HostTrackerNorthbound {
      * }
      * </pre>
      */
-    @Path("/{containerName}/inactive")
+    @Path("/{containerName}/hosts/inactive")
     @GET
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @TypeHint(Hosts.class)
@@ -312,7 +308,7 @@ public class HostTrackerNorthbound {
      *
      * RequestURL:
      *
-     * http://localhost:8080/controller/nb/v2/host/default/1.1.1.1
+     * http://localhost:8080/controller/nb/v2/hosttracker/default/address/1.1.1.1
      *
      * Response in XML
      *
@@ -341,7 +337,7 @@ public class HostTrackerNorthbound {
      * }
      * </pre>
      */
-    @Path("/{containerName}/{networkAddress}")
+    @Path("/{containerName}/address/{networkAddress}")
     @GET
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @TypeHint(HostConfig.class)
@@ -374,7 +370,8 @@ public class HostTrackerNorthbound {
     }
 
     /**
-     * Add a Static Host configuration
+     * Add a Static Host configuration. If a host by the given address already
+     * exists, this method will respond with a non-successful status response.
      *
      * @param containerName
      *            Name of the Container. The Container name for the base
@@ -391,7 +388,7 @@ public class HostTrackerNorthbound {
      *
      * RequestURL:
      *
-     * http://localhost:8080/controller/nb/v2/host/default/1.1.1.1
+     * http://localhost:8080/controller/nb/v2/hosttracker/default/address/1.1.1.1
      *
      * Request in XML
      *
@@ -421,7 +418,7 @@ public class HostTrackerNorthbound {
      * </pre>
      */
 
-    @Path("/{containerName}/{networkAddress}")
+    @Path("/{containerName}/address/{networkAddress}")
     @PUT
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
@@ -477,7 +474,7 @@ public class HostTrackerNorthbound {
      * @return Response as dictated by the HTTP Response code.
      */
 
-    @Path("/{containerName}/{networkAddress}")
+    @Path("/{containerName}/address/{networkAddress}")
     @DELETE
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
index 1db1a49f7fc80397a90fad0fe050ae02b07e5a45..f0c04ae5e2bde9d3264dbb3a256b834239a70d16 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 27a50c015dfce73040afd8453f54bb02b26139d9..59065fc7c11da8626ca486c1c399af1cdf5c173b 100644 (file)
@@ -293,7 +293,7 @@ public class NorthboundIT {
     @Test
     public void testSubnetsNorthbound() throws JSONException {
         System.out.println("Starting Subnets JAXB client.");
-        String baseURL = "http://127.0.0.1:8080/controller/nb/v2/subnet/";
+        String baseURL = "http://127.0.0.1:8080/controller/nb/v2/subnetservice/";
 
         String name1 = "testSubnet1";
         String subnet1 = "1.1.1.1/24";
@@ -313,7 +313,7 @@ public class NorthboundIT {
         nodePortsJson3_1.append(nodePortsJson3).append(",").append(nodePortsJson2);
 
         // Test GET subnets in default container
-        String result = getJsonResult(baseURL + "default/subnet/all");
+        String result = getJsonResult(baseURL + "default/subnets");
         JSONTokener jt = new JSONTokener(result);
         JSONObject json = new JSONObject(jt);
         JSONArray subnetConfigs = json.getJSONArray("subnetConfig");
@@ -326,7 +326,7 @@ public class NorthboundIT {
         // Test POST subnet1
         JSONObject jo = new JSONObject().put("name", name1).put("subnet", subnet1);
         // execute HTTP request and verify response code
-        result = getJsonResult(baseURL + "default/subnet/" + name1, "POST", jo.toString());
+        result = getJsonResult(baseURL + "default/subnet/" + name1, "PUT", jo.toString());
         Assert.assertTrue(httpResponseCode == 201);
 
         // Test GET subnet1
@@ -340,31 +340,31 @@ public class NorthboundIT {
         // Test POST subnet2
         JSONObject jo2 = new JSONObject().put("name", name2).put("subnet", subnet2);
         // execute HTTP request and verify response code
-        result = getJsonResult(baseURL + "default/subnet/" + name2, "POST", jo2.toString());
+        result = getJsonResult(baseURL + "default/subnet/" + name2, "PUT", jo2.toString());
         Assert.assertEquals(201, httpResponseCode.intValue());
         // Test POST nodePorts
         jo2.append("nodePorts", nodePortsJson2);
         // execute HTTP request and verify response code
-        result = getJsonResult(baseURL + "default/subnet/" + name2 + "/node-ports", "POST", jo2.toString());
+        result = getJsonResult(baseURL + "default/subnet/" + name2 + "/nodePorts", "PUT", jo2.toString());
         Assert.assertEquals(200, httpResponseCode.intValue());
         // Test POST subnet3
         JSONObject jo3 = new JSONObject().put("name", name3).put("subnet", subnet3);
         // execute HTTP request and verify response code
-        result = getJsonResult(baseURL + "default/subnet/" + name3, "POST", jo3.toString());
+        result = getJsonResult(baseURL + "default/subnet/" + name3, "PUT", jo3.toString());
         Assert.assertEquals(201, httpResponseCode.intValue());
         // Test POST nodePorts
         jo3.append("nodePorts", nodePortsJson3);
         // execute HTTP request and verify response code
-        result = getJsonResult(baseURL + "default/subnet/" + name3 + "/node-ports", "POST", jo3.toString());
+        result = getJsonResult(baseURL + "default/subnet/" + name3 + "/nodePorts", "PUT", jo3.toString());
         Assert.assertEquals(200, httpResponseCode.intValue());
         // Test PUT nodePorts
         jo3.remove("nodePorts");
         jo3.append("nodePorts", nodePortsJson3_1);
-        result = getJsonResult(baseURL + "default/subnet/" + name3 + "/node-ports", "PUT", jo3.toString());
+        result = getJsonResult(baseURL + "default/subnet/" + name3 + "/nodePorts", "POST", jo3.toString());
         Assert.assertEquals(200, httpResponseCode.intValue());
 
         // Test GET all subnets in default container
-        result = getJsonResult(baseURL + "default/subnet/all");
+        result = getJsonResult(baseURL + "default/subnets");
         jt = new JSONTokener(result);
         json = new JSONObject(jt);
         JSONArray subnetConfigArray = json.getJSONArray("subnetConfig");
@@ -419,7 +419,7 @@ public class NorthboundIT {
         String nextHop2 = "1.1.1.1";
 
         // Test GET static routes in default container, expecting no results
-        String result = getJsonResult(baseURL + "default");
+        String result = getJsonResult(baseURL + "default/routes");
         JSONTokener jt = new JSONTokener(result);
         JSONObject json = new JSONObject(jt);
         JSONArray staticRoutes = json.getJSONArray("staticRoute");
@@ -428,15 +428,15 @@ public class NorthboundIT {
         // Test insert static route
         String requestBody = "{\"name\":\"" + name1 + "\", \"prefix\":\"" + prefix1 + "\", \"nextHop\":\"" + nextHop1
                 + "\"}";
-        result = getJsonResult(baseURL + "default/route/" + name1, "POST", requestBody);
+        result = getJsonResult(baseURL + "default/route/" + name1, "PUT", requestBody);
         Assert.assertEquals(201, httpResponseCode.intValue());
 
         requestBody = "{\"name\":\"" + name2 + "\", \"prefix\":\"" + prefix2 + "\", \"nextHop\":\"" + nextHop2 + "\"}";
-        result = getJsonResult(baseURL + "default/route/" + name2, "POST", requestBody);
+        result = getJsonResult(baseURL + "default/route/" + name2, "PUT", requestBody);
         Assert.assertEquals(201, httpResponseCode.intValue());
 
         // Test Get all static routes
-        result = getJsonResult(baseURL + "default");
+        result = getJsonResult(baseURL + "default/routes");
         jt = new JSONTokener(result);
         json = new JSONObject(jt);
         JSONArray staticRouteArray = json.getJSONArray("staticRoute");
@@ -475,9 +475,9 @@ public class NorthboundIT {
 
         // Test delete static route
         result = getJsonResult(baseURL + "default/route/" + name1, "DELETE");
-        Assert.assertEquals(200, httpResponseCode.intValue());
+        Assert.assertEquals(204, httpResponseCode.intValue());
 
-        result = getJsonResult(baseURL + "default");
+        result = getJsonResult(baseURL + "default/routes");
         jt = new JSONTokener(result);
         json = new JSONObject(jt);
 
@@ -490,7 +490,7 @@ public class NorthboundIT {
     @Test
     public void testSwitchManager() throws JSONException {
         System.out.println("Starting SwitchManager JAXB client.");
-        String baseURL = "http://127.0.0.1:8080/controller/nb/v2/switch/default/";
+        String baseURL = "http://127.0.0.1:8080/controller/nb/v2/switchmanager/default/";
 
         // define Node/NodeConnector attributes for test
         int nodeId_1 = 51966;
@@ -599,7 +599,7 @@ public class NorthboundIT {
         // Delete state property of nodeconnector1
         result = getJsonResult(baseURL + "nodeconnector/STUB/" + nodeId_1 + "/STUB/" + nodeConnectorId_1
                 + "/property/state", "DELETE");
-        Assert.assertEquals(200, httpResponseCode.intValue());
+        Assert.assertEquals(204, httpResponseCode.intValue());
 
         result = getJsonResult(baseURL + "node/STUB/" + nodeId_1);
         jt = new JSONTokener(result);
@@ -613,7 +613,7 @@ public class NorthboundIT {
         // Delete capabilities property of nodeconnector2
         result = getJsonResult(baseURL + "nodeconnector/STUB/" + nodeId_2 + "/STUB/" + nodeConnectorId_2
                 + "/property/capabilities", "DELETE");
-        Assert.assertEquals(200, httpResponseCode.intValue());
+        Assert.assertEquals(204, httpResponseCode.intValue());
 
         result = getJsonResult(baseURL + "node/STUB/" + nodeId_2);
         jt = new JSONTokener(result);
@@ -834,19 +834,19 @@ public class NorthboundIT {
     @Test
     public void testFlowProgrammer() throws JSONException {
         System.out.println("Starting FlowProgrammer JAXB client.");
-        String baseURL = "http://127.0.0.1:8080/controller/nb/v2/flow/default/";
+        String baseURL = "http://127.0.0.1:8080/controller/nb/v2/flowprogrammer/default/";
         // Attempt to get a flow that doesn't exit. Should return 404
         // status.
-        String result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "GET");
+        String result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test1", "GET");
         Assert.assertTrue(result.equals("404"));
 
         // test add flow1
         String fc = "{\"name\":\"test1\", \"node\":{\"id\":\"51966\",\"type\":\"STUB\"}, \"actions\":[\"DROP\"]}";
-        result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "PUT", fc);
+        result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test1", "PUT", fc);
         Assert.assertTrue(httpResponseCode == 201);
 
         // test get returns flow that was added.
-        result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "GET");
+        result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test1", "GET");
         // check that result came out fine.
         Assert.assertTrue(httpResponseCode == 200);
         JSONTokener jt = new JSONTokener(result);
@@ -860,17 +860,17 @@ public class NorthboundIT {
         Assert.assertEquals(node.getString("id"), "51966");
         // test adding same flow again fails due to repeat name..return 409
         // code
-        result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "PUT", fc);
+        result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test1", "PUT", fc);
         Assert.assertTrue(result.equals("409"));
 
         fc = "{\"name\":\"test2\", \"node\":{\"id\":\"51966\",\"type\":\"STUB\"}, \"actions\":[\"DROP\"]}";
-        result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "PUT", fc);
+        result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test2", "PUT", fc);
         // test should return 409 for error due to same flow being added.
         Assert.assertTrue(result.equals("409"));
 
         // add second flow that's different
         fc = "{\"name\":\"test2\", \"nwSrc\":\"1.1.1.1\", \"node\":{\"id\":\"51966\",\"type\":\"STUB\"}, \"actions\":[\"DROP\"]}";
-        result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "PUT", fc);
+        result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test2", "PUT", fc);
         Assert.assertTrue(httpResponseCode == 201);
 
         // check that request returns both flows given node.
@@ -892,10 +892,10 @@ public class NorthboundIT {
         Assert.assertTrue(count == 2);
 
         // delete a flow, check that it's no longer in list.
-        result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "DELETE");
-        Assert.assertTrue(httpResponseCode == 200);
+        result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test2", "DELETE");
+        Assert.assertTrue(httpResponseCode == 204);
 
-        result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "GET");
+        result = getJsonResult(baseURL + "node/STUB/51966/staticFlow/test2", "GET");
         Assert.assertTrue(result.equals("404"));
     }
 
@@ -970,7 +970,7 @@ public class NorthboundIT {
         Integer nodeConnectorId_2 = 34;
         String vlan_2 = "123";
 
-        String baseURL = "http://127.0.0.1:8080/controller/nb/v2/host/default";
+        String baseURL = "http://127.0.0.1:8080/controller/nb/v2/hosttracker/default";
 
         // test PUT method: addHost()
         JSONObject fc_json = new JSONObject();
@@ -983,7 +983,7 @@ public class NorthboundIT {
         fc_json.put("staticHost", "true");
         fc_json.put("networkAddress", networkAddress_1);
 
-        String result = getJsonResult(baseURL + "/" + networkAddress_1, "PUT", fc_json.toString());
+        String result = getJsonResult(baseURL + "/address/" + networkAddress_1, "PUT", fc_json.toString());
         Assert.assertTrue(httpResponseCode == 201);
 
         fc_json = new JSONObject();
@@ -996,7 +996,7 @@ public class NorthboundIT {
         fc_json.put("staticHost", "true");
         fc_json.put("networkAddress", networkAddress_2);
 
-        result = getJsonResult(baseURL + "/" + networkAddress_2 , "PUT", fc_json.toString());
+        result = getJsonResult(baseURL + "/address/" + networkAddress_2 , "PUT", fc_json.toString());
         Assert.assertTrue(httpResponseCode == 201);
 
         // define variables for decoding returned strings
@@ -1005,7 +1005,7 @@ public class NorthboundIT {
 
         // the two hosts should be in inactive host DB
         // test GET method: getInactiveHosts()
-        result = getJsonResult(baseURL + "/inactive", "GET");
+        result = getJsonResult(baseURL + "/hosts/inactive", "GET");
         Assert.assertTrue(httpResponseCode == 200);
 
         JSONTokener jt = new JSONTokener(result);
@@ -1041,7 +1041,7 @@ public class NorthboundIT {
         }
 
         // test GET method: getActiveHosts() - no host expected
-        result = getJsonResult(baseURL, "GET");
+        result = getJsonResult(baseURL + "/hosts/active", "GET");
         Assert.assertTrue(httpResponseCode == 200);
 
         jt = new JSONTokener(result);
@@ -1063,7 +1063,7 @@ public class NorthboundIT {
 
         // verify the host shows up in active host DB
 
-        result = getJsonResult(baseURL, "GET");
+        result = getJsonResult(baseURL + "/hosts/active", "GET");
         Assert.assertTrue(httpResponseCode == 200);
 
         jt = new JSONTokener(result);
@@ -1073,7 +1073,7 @@ public class NorthboundIT {
 
         // test GET method for getHostDetails()
 
-        result = getJsonResult(baseURL + "/" + networkAddress_1, "GET");
+        result = getJsonResult(baseURL + "/address/" + networkAddress_1, "GET");
         Assert.assertTrue(httpResponseCode == 200);
 
         jt = new JSONTokener(result);
@@ -1091,13 +1091,13 @@ public class NorthboundIT {
 
         // test DELETE method for deleteFlow()
 
-        result = getJsonResult(baseURL + "/" + networkAddress_1, "DELETE");
+        result = getJsonResult(baseURL + "/address/" + networkAddress_1, "DELETE");
         Assert.assertTrue(httpResponseCode == 204);
 
         // verify host_1 removed from active host DB
         // test GET method: getActiveHosts() - no host expected
 
-        result = getJsonResult(baseURL, "GET");
+        result = getJsonResult(baseURL + "/hosts/active", "GET");
         Assert.assertTrue(httpResponseCode == 200);
 
         jt = new JSONTokener(result);
@@ -1246,11 +1246,11 @@ public class NorthboundIT {
                 .put("dstNodeConnector",
                         nodeConnectorType_2 + "|" + nodeConnectorId_2 + "@" + nodeType_2 + "|" + nodeId_2);
         // execute HTTP request and verify response code
-        result = getJsonResult(baseURL + "/user-link", "PUT", jo.toString());
+        result = getJsonResult(baseURL + "/userLink/userLink_1", "PUT", jo.toString());
         Assert.assertTrue(httpResponseCode == 201);
 
         // === test GET method for getUserLinks()
-        result = getJsonResult(baseURL + "/user-link", "GET");
+        result = getJsonResult(baseURL + "/userLinks", "GET");
         Assert.assertTrue(httpResponseCode == 200);
         if (debugMsg) {
             System.out.println("result:" + result);
@@ -1295,12 +1295,12 @@ public class NorthboundIT {
 
         // === test DELETE method for deleteUserLink()
         String userName = "userLink_1";
-        result = getJsonResult(baseURL + "/user-link/" + userName, "DELETE");
-        Assert.assertTrue(httpResponseCode == 200);
+        result = getJsonResult(baseURL + "/userLink/" + userName, "DELETE");
+        Assert.assertTrue(httpResponseCode == 204);
 
         // execute another getUserLinks() request to verify that userLink_1 is
         // removed
-        result = getJsonResult(baseURL + "/user-link", "GET");
+        result = getJsonResult(baseURL + "/userLinks", "GET");
         Assert.assertTrue(httpResponseCode == 200);
         if (debugMsg) {
             System.out.println("result:" + result);
@@ -1355,7 +1355,7 @@ public class NorthboundIT {
                 mavenBundle("org.opendaylight.controller", "configuration").versionAsInProject(),
                 mavenBundle("org.opendaylight.controller", "configuration.implementation").versionAsInProject(),
                 mavenBundle("org.opendaylight.controller", "containermanager").versionAsInProject(),
-                mavenBundle("org.opendaylight.controller", "containermanager.implementation").versionAsInProject(),
+                mavenBundle("org.opendaylight.controller", "containermanager.it.implementation").versionAsInProject(),
                 mavenBundle("org.opendaylight.controller", "clustering.services").versionAsInProject(),
                 mavenBundle("org.opendaylight.controller", "clustering.services-implementation").versionAsInProject(),
                 mavenBundle("org.opendaylight.controller", "security").versionAsInProject().noStart(),
index b7da9641fafa0af6e40970a95aaffe940d902294..a8d81a73673786f8bb7083ff0c01676cead8f1f3 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 62f5b42699dd20e829d2e722ab633f6d326f4dfa..eb285f2e818805179b6253cb8db4f5ea8f384e27 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 65e68aab7d9be627154aebf61856b1b07fe5d68d..07f125dae3f79a8428d7609a18c1f4194b2553a1 100644 (file)
@@ -16,6 +16,7 @@ import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -113,7 +114,7 @@ public class StaticRoutingNorthbound {
      * Example:
      *
      * Request URL:
-     * GET http://localhost:8080/controller/nb/v2/staticroute/default
+     * GET http://localhost:8080/controller/nb/v2/staticroute/default/routes
      *
      * Response in XML:
      *  &lt;list&gt;
@@ -129,7 +130,7 @@ public class StaticRoutingNorthbound {
      *
      * </pre>
      */
-    @Path("/{containerName}")
+    @Path("/{containerName}/routes")
     @GET
     @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @TypeHint(StaticRoutes.class)
@@ -204,7 +205,8 @@ public class StaticRoutingNorthbound {
 
     /**
      *
-     * Add a new Static Route
+     * Add a new Static Route. If a route by the given name already exists, this
+     * method will return a non-successful status response.
      *
      * @param containerName Name of the Container. The Container name for the base controller is "default".
      * @param route Name of the Static Route configuration
@@ -214,7 +216,7 @@ public class StaticRoutingNorthbound {
      * Example:
      *
      * Request URL:
-     * POST http://localhost:8080/controller/nb/v2/staticroute/default/route/route-1
+     * PUT http://localhost:8080/controller/nb/v2/staticroute/default/route/route-1
      *
      * Request payload in JSON:
      * {"name":"route-1","prefix":"10.10.1.0/24","nextHop":"1.1.1.1"}
@@ -222,7 +224,7 @@ public class StaticRoutingNorthbound {
      * </pre>
      */
     @Path("/{containerName}/route/{route}")
-    @POST
+    @PUT
     @Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes( {
             @ResponseCode(code = 201, condition = "Created Static Route successfully"),
@@ -284,7 +286,7 @@ public class StaticRoutingNorthbound {
     @Path("/{containerName}/route/{route}")
     @DELETE
     @StatusCodes( {
-            @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 204, condition = "Static route removed successfully"),
             @ResponseCode(code = 404, condition = "Container Name or Configuration Name not found"),
             @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active") })
     public Response removeStaticRoute(
@@ -311,9 +313,9 @@ public class StaticRoutingNorthbound {
         Status status = staticRouting.removeStaticRoute(route);
         if (status.isSuccess()) {
             NorthboundUtils.auditlog("Static Route", username, "removed", route, containerName);
-            return Response.ok().build();
+            return Response.noContent().build();
         }
-        throw new ResourceNotFoundException(status.getDescription());
+        return NorthboundUtils.getResponse(status);
     }
 
     private void handleDefaultDisabled(String containerName) {
index eb2bf587de4006c6f61118b76698673e2bbdb582..76129f4cb141eece18d37abb0c7c5bd66a4f1b98 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 a07f6435fe0e92e96d53eea61dd5abdffab921a3..a47bfa70b77e9fa2d0657529df2e676f8ab98f2a 100644 (file)
@@ -50,11 +50,7 @@ import org.opendaylight.controller.switchmanager.ISwitchManager;
  * Authentication realm : <b>opendaylight</b><br>
  * Transport : <b>HTTP and HTTPS</b><br>
  * <br>
- * HTTPS Authentication is disabled by default. Administrator can enable it in
- * tomcat-server.xml after adding a proper keystore / SSL certificate from a
- * trusted authority.<br>
- * More info :
- * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ * HTTPS Authentication is disabled by default.
  *
  */
 @Path("/")
index 5ff13ffe805381e9abc83a08a71e67ae0eaaf6c5..abdc40a174225cef3073fb45b5c19a7cfb462f43 100644 (file)
@@ -3,10 +3,10 @@
     xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd">
 
   <services>
-    <rest defaultRestSubcontext="/controller/nb/v2/subnet"/>
+    <rest defaultRestSubcontext="/controller/nb/v2/subnetservice"/>
   </services>
 
   <modules>
-    <docs docsDir="rest" title="REST API" includeExampleXml="true" includeExampleJson="true"/>
+    <docs docsDir="rest" title="Subnet Service REST API" includeExampleXml="true" includeExampleJson="true"/>
   </modules>
 </enunciate>
index 9feb95fce08d2fead13ecec697ad03b406c7a746..c3ad783773c34f040b48eab10a73d8ec3402fa00 100644 (file)
@@ -76,7 +76,7 @@
             </Import-Package>
             <Export-Package>
             </Export-Package>
-            <Web-ContextPath>/controller/nb/v2/subnet</Web-ContextPath>
+            <Web-ContextPath>/controller/nb/v2/subnetservice</Web-ContextPath>
           </instructions>
           <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
         </configuration>
@@ -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 50024fd7392706bcf2f935685dfa97ec68a11100..b16c8f9ea67d0b58f169a02733915005147ad261 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.controller.subnets.northbound;
 
 import java.util.HashSet;
+
 import java.util.List;
 import java.util.Set;
 
@@ -45,6 +46,19 @@ import org.opendaylight.controller.switchmanager.SubnetConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * This class provides REST APIs to manage subnets.
+ *
+ * <br>
+ * <br>
+ * Authentication scheme : <b>HTTP Basic</b><br>
+ * Authentication realm : <b>opendaylight</b><br>
+ * Transport : <b>HTTP and HTTPS</b><br>
+ * <br>
+ * HTTPS Authentication is disabled by default.
+ *
+ */
+
 @Path("/")
 public class SubnetsNorthbound {
     protected static final Logger logger = LoggerFactory.getLogger(SubnetsNorthbound.class);
@@ -100,7 +114,7 @@ public class SubnetsNorthbound {
      *         <pre>
      * Example:
      *
-     * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/all
+     * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnets
      *
      * Response in XML:
      * &lt;subnetConfig&gt;
@@ -131,7 +145,7 @@ public class SubnetsNorthbound {
      * }
      * </pre>
      */
-    @Path("/{containerName}/subnet/all")
+    @Path("/{containerName}/subnets")
     @GET
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@@ -165,7 +179,7 @@ public class SubnetsNorthbound {
      *         <pre>
      * Example:
      *
-     * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
+     * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1
      *
      * Response in XML:
      * &lt;subnetConfig&gt;
@@ -213,7 +227,8 @@ public class SubnetsNorthbound {
     }
 
     /**
-     * Add a subnet to a container
+     * Add a subnet to a container. If a subnet by the given name already exists
+     * this method will return a non-successful response.
      *
      * @param containerName
      *            name of the container to which subnet needs to be added
@@ -227,7 +242,7 @@ public class SubnetsNorthbound {
      *         <pre>
      * Example:
      *
-     * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
+     * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1
      *
      * Request XML:
      *  &lt;subnetConfig&gt;
@@ -244,7 +259,7 @@ public class SubnetsNorthbound {
      */
 
     @Path("/{containerName}/subnet/{subnetName}")
-    @POST
+    @PUT
     @StatusCodes({ @ResponseCode(code = 201, condition = "Subnet created successfully"),
             @ResponseCode(code = 400, condition = "Invalid data passed"),
             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@@ -297,7 +312,7 @@ public class SubnetsNorthbound {
      *
      *         <pre>
      * Example:
-     *            Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1
+     *            Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1
      *
      * </pre>
      */
@@ -345,7 +360,7 @@ public class SubnetsNorthbound {
      *         <pre>
      * Example:
      *
-     * Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
+     * Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1/nodePorts
      *
      *  Request in XML:
      *  &lt;subnetConfig&gt;
@@ -364,8 +379,8 @@ public class SubnetsNorthbound {
      * }
      * </pre>
      */
-    @Path("/{containerName}/subnet/{subnetName}/node-ports")
-    @PUT
+    @Path("/{containerName}/subnet/{subnetName}/nodePorts")
+    @POST
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
             @ResponseCode(code = 200, condition = "Ports replaced successfully"),
@@ -442,7 +457,7 @@ public class SubnetsNorthbound {
     }
 
     /**
-     * Add ports to a subnet in the container
+     * Add ports to a subnet in the container.
      *
      * @param containerName
      *            name of the container that has the subnet to which node ports
@@ -455,7 +470,7 @@ public class SubnetsNorthbound {
      *
      *         <pre>
      * Example:
-     *            Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
+     *            Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1/nodePorts
      *
      * Request XML:
      *  &lt;subnetConfig&gt;
@@ -475,8 +490,8 @@ public class SubnetsNorthbound {
      *
      * </pre>
      */
-    @Path("/{containerName}/subnet/{subnetName}/node-ports")
-    @POST
+    @Path("/{containerName}/subnet/{subnetName}/nodePorts")
+    @PUT
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
             @ResponseCode(code = 200, condition = "Added node ports to subnet successfully"),
@@ -557,7 +572,7 @@ public class SubnetsNorthbound {
      *
      *         <pre>
      * Example:
-     *            Request URL: http://localhost:8080/controller/nb/v2/subnet/default/subnet/subnet1/node-ports
+     *            Request URL: http://localhost:8080/controller/nb/v2/subnetservice/default/subnet/subnet1/nodePorts
      *
      * Request XML:
      *  &lt;subnetConfig&gt;
@@ -573,7 +588,7 @@ public class SubnetsNorthbound {
      *
      * </pre>
      */
-    @Path("/{containerName}/subnet/{subnetName}/node-ports")
+    @Path("/{containerName}/subnet/{subnetName}/nodePorts")
     @DELETE
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
@@ -635,7 +650,7 @@ public class SubnetsNorthbound {
             }
         }
         if (successful) {
-            return Response.status(Response.Status.NO_CONTENT).build();
+            return Response.noContent().build();
         }
         throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
     }
index 8a8bfdb02999941afa232187ea4c02a37bf030d7..6f2dd4d0e76892dc566a55411ee6aea1ebcc47e5 100644 (file)
@@ -3,7 +3,7 @@
     xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd">
 
   <services>
-    <rest defaultRestSubcontext="/controller/nb/v2/switch"/>
+    <rest defaultRestSubcontext="/controller/nb/v2/switchmanager"/>
   </services>
 
   <modules>
index 965075a271aa2f5357fdab6933872f1e68dfa654..a416414d06607208b430b2d14932e986937b847b 100644 (file)
@@ -60,7 +60,7 @@
               org.codehaus.jackson.jaxrs,
               !org.codehaus.enunciate.jaxrs
             </Import-Package>
-            <Web-ContextPath>/controller/nb/v2/switch</Web-ContextPath>
+            <Web-ContextPath>/controller/nb/v2/switchmanager</Web-ContextPath>
           </instructions>
           <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
         </configuration>
@@ -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 1edd945dfa708f7d68f5f6a081c3795f3bcefff8..f4d302b2d1b9cab1f96ea7501367ef532d7ff309 100644 (file)
@@ -119,7 +119,7 @@ public class SwitchNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/switch/default/nodes
+     * http://localhost:8080/controller/nb/v2/switchmanager/default/nodes
      *
      * Response in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
@@ -194,7 +194,7 @@ public class SwitchNorthbound {
         List<NodeProperties> res = new ArrayList<NodeProperties>();
         Set<Node> nodes = switchManager.getNodes();
         if (nodes == null) {
-            return null;
+            return new Nodes(res);
         }
 
         for (Node node : nodes) {
@@ -212,7 +212,9 @@ public class SwitchNorthbound {
     }
 
     /**
-     * Add a Description, Tier and Forwarding mode property to a node.
+     * Add a Description, Tier and Forwarding mode property to a node. This
+     * method returns a non-successful response if a node by that name already
+     * exists.
      *
      * @param containerName
      *            Name of the Container (Eg. 'default')
@@ -237,7 +239,7 @@ public class SwitchNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:00:00:03/property/description/Switch3
+     * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:03/property/description/Switch3
      *
      * </pre>
      */
@@ -316,7 +318,7 @@ public class SwitchNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:00:00:03/property/forwarding
+     * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:03/property/forwarding
      *
      * </pre>
      */
@@ -325,7 +327,7 @@ public class SwitchNorthbound {
     @DELETE
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
-            @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 204, condition = "Property removed successfully"),
             @ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
@@ -369,7 +371,8 @@ public class SwitchNorthbound {
                 SwitchConfig newSwitchConfig = new SwitchConfig(node.toString(), nodeProperties);
                 status = switchManager.updateNodeConfig(newSwitchConfig);
                 if(status.isSuccess()){
-                    NorthboundUtils.auditlog("Static Route", username, "updated", nodeId, containerName);
+                    NorthboundUtils.auditlog("Node Property", username, "removed", propertyName  + " from " + nodeId, containerName);
+                    return Response.noContent().build();
                 }
             }
         }
@@ -401,7 +404,7 @@ public class SwitchNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:00:00:01
+     * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:01
      *
      * Response in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
@@ -489,7 +492,9 @@ public class SwitchNorthbound {
     }
 
     /**
-     * Add Bandwidth property to a node connector
+     * Add node-connector property to a node connector. This method returns a
+     * non-successful response if a node connector by the given name already
+     * exists.
      *
      * @param containerName
      *            Name of the Container (Eg. 'default')
@@ -521,7 +526,7 @@ public class SwitchNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/switch/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth/1
+     * http://localhost:8080/controller/nb/v2/switchmanager/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth/1
      *
      * </pre>
      */
@@ -610,7 +615,7 @@ public class SwitchNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/switch/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth
+     * http://localhost:8080/controller/nb/v2/switchmanager/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth
      *
      * </pre>
      */
@@ -619,7 +624,7 @@ public class SwitchNorthbound {
     @DELETE
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
-            @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 204, condition = "Property removed successfully"),
             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
@@ -657,7 +662,7 @@ public class SwitchNorthbound {
         Status ret = switchManager.removeNodeConnectorProp(nc, propertyName);
         if (ret.isSuccess()) {
             NorthboundUtils.auditlog("Node Connector Property", username, "removed", nc + " from " + nodeConnectorId, containerName);
-            return Response.ok().build();
+            return Response.noContent().build();
         }
         throw new ResourceNotFoundException(ret.getDescription());
     }
@@ -674,11 +679,11 @@ public class SwitchNorthbound {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/switch/default/switch-config
+     * http://localhost:8080/controller/nb/v2/switchmanager/default/save
      *
      * </pre>
      */
-    @Path("/{containerName}/switch-config")
+    @Path("/{containerName}/save")
     @POST
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
index b7c451c8e6855e09c772b42e4f7f0e52948eec6d..fed0f68e3086b735d3ff66d284a6c7af5039aa33 100644 (file)
@@ -7,6 +7,6 @@
   </services>
 
   <modules>
-    <docs docsDir="rest" title="REST API" includeExampleXml="true" includeExampleJson="true"/>
+    <docs docsDir="rest" title="Topology REST API" includeExampleXml="true" includeExampleJson="true"/>
   </modules>
 </enunciate>
index b2b7b9c85f53ac4332757227e1de0a24880b169f..803a28079e47939a2b09bdf62db76769e4855349 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 508ffafe6fdf8f1a1a5fff4466ee54e0f0862da9..ff26bfd21f30a6b67f683607924b366ef32a1011 100644 (file)
@@ -52,11 +52,7 @@ import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig;
  * Authentication realm : <b>opendaylight</b><br>
  * Transport : <b>HTTP and HTTPS</b><br>
  * <br>
- * HTTPS Authentication is disabled by default. Administrator can enable it in
- * tomcat-server.xml after adding a proper keystore / SSL certificate from a
- * trusted authority.<br>
- * More info :
- * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ * HTTPS Authentication is disabled by default.
  */
 
 @Path("/")
@@ -81,7 +77,7 @@ public class TopologyNorthboundJAXRS {
      *            The container for which we want to retrieve the topology (Eg.
      *            'default')
      *
-     * @return A List of EdgeProps each EdgeProp represent an Edge of the grap
+     * @return A List of EdgeProps each EdgeProp represent an Edge of the graph
      *         with the corresponding properties attached to it.
      *
      *         <pre>
@@ -222,7 +218,7 @@ public class TopologyNorthboundJAXRS {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/topology/default/user-link
+     * http://localhost:8080/controller/nb/v2/topology/default/userLinks
      *
      * Response in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
@@ -241,7 +237,7 @@ public class TopologyNorthboundJAXRS {
      *
      * </pre>
      */
-    @Path("/{containerName}/user-link")
+    @Path("/{containerName}/userLinks")
     @GET
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @TypeHint(TopologyUserLinks.class)
@@ -278,6 +274,8 @@ public class TopologyNorthboundJAXRS {
      *
      * @param containerName
      *            Name of the Container (Eg. 'default')
+     * @param name
+     *            Name of the user link
      * @param TopologyUserLinkConfig
      *            in JSON or XML format
      * @return Response as dictated by the HTTP Response Status code
@@ -287,7 +285,7 @@ public class TopologyNorthboundJAXRS {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/topology/default/user-link
+     * http://localhost:8080/controller/nb/v2/topology/default/userLink/link1
      *
      * Request in XML:
      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
@@ -303,7 +301,7 @@ public class TopologyNorthboundJAXRS {
      *
      * </pre>
      */
-    @Path("/{containerName}/user-link")
+    @Path("/{containerName}/userLink/{name}")
     @PUT
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@@ -315,6 +313,7 @@ public class TopologyNorthboundJAXRS {
             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
     public Response addUserLink(
             @PathParam(value = "containerName") String containerName,
+            @PathParam(value = "name") String name,
             @TypeHint(TopologyUserLinkConfig.class) TopologyUserLinkConfig userLinkConfig) {
 
         if (!NorthboundUtils.isAuthorized(
@@ -352,16 +351,16 @@ public class TopologyNorthboundJAXRS {
      * Example:
      *
      * RequestURL:
-     * http://localhost:8080/controller/nb/v2/topology/default/user-link/config1
+     * http://localhost:8080/controller/nb/v2/topology/default/userLink/config1
      *
      * </pre>
      */
-    @Path("/{containerName}/user-link/{name}")
+    @Path("/{containerName}/userLink/{name}")
     @DELETE
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
-            @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 204, condition = "User link removed successfully"),
             @ResponseCode(code = 404, condition = "The Container Name or Link Configuration Name was not found"),
             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
     public Response deleteUserLink(
@@ -384,8 +383,8 @@ public class TopologyNorthboundJAXRS {
         Status ret = topologyManager.deleteUserLink(name);
         if (ret.isSuccess()) {
             NorthboundUtils.auditlog("User Link", username, "removed", name, containerName);
-            return Response.ok().build();
+            return Response.noContent().build();
         }
-        throw new ResourceNotFoundException(ret.getDescription());
+        return NorthboundUtils.getResponse(ret);
     }
 }
index 0895d9af27c85132965a98ea23cd166784a3e538..869d36d9cbf815142b430e093a72fb22aedfd439 100644 (file)
@@ -33,17 +33,11 @@ import org.opendaylight.controller.protocol_plugin.openflow.IInventoryProvider;
 import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
-import org.openflow.protocol.OFPhysicalPort;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
 import org.opendaylight.controller.sal.core.Config;
 import org.opendaylight.controller.sal.core.ConstructionException;
-import org.opendaylight.controller.sal.core.Edge;
 import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.Edge;
 import org.opendaylight.controller.sal.core.IContainerListener;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
@@ -61,6 +55,11 @@ import org.opendaylight.controller.sal.utils.HexEncode;
 import org.opendaylight.controller.sal.utils.NetUtils;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
 import org.opendaylight.controller.sal.utils.NodeCreator;
+import org.openflow.protocol.OFPhysicalPort;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * The class describes neighbor discovery service for an OpenFlow network.
@@ -561,10 +560,11 @@ public class DiscoveryService implements IInventoryShimExternalListener, IDataPa
     private void addDiscovery(Node node) {
         Map<Long, ISwitch> switches = controller.getSwitches();
         ISwitch sw = switches.get(node.getID());
-        List<OFPhysicalPort> ports = sw.getEnabledPorts();
-        if (ports == null) {
+        if (sw == null) {
+            //switch could be removed by now, stop propagation
             return;
         }
+        List<OFPhysicalPort> ports = sw.getEnabledPorts();
         for (OFPhysicalPort port : ports) {
             NodeConnector nodeConnector = NodeConnectorCreator.createOFNodeConnector(port.getPortNumber(), node);
             if (!readyListHi.contains(nodeConnector)) {
index 9e0179680612c52a46a194544471d1c0a88c19ca..92eec43b4ce58487465ae243c46aa3c0f87b6027 100644 (file)
@@ -482,8 +482,7 @@ public class InventoryServiceShim implements IContainerListener,
         Set<Property> props = new HashSet<Property>();
         Long sid = (Long) node.getID();
 
-        Date connectedSince = controller.getSwitches().get(sid)
-                .getConnectedDate();
+        Date connectedSince = sw.getConnectedDate();
         Long connectedSinceTime = (connectedSince == null) ? 0 : connectedSince
                 .getTime();
         props.add(new TimeStamp(connectedSinceTime, "connectedSince"));
diff --git a/opendaylight/sal/yang-prototype/sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java b/opendaylight/sal/yang-prototype/sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java
new file mode 100644 (file)
index 0000000..86dcba9
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+ * @author mirehak
+ *
+ */
+public class RpcErrors {
+    
+    /**
+     * @param applicationTag
+     * @param tag
+     * @param info
+     * @param severity
+     * @param message
+     * @param errorType 
+     * @param cause 
+     * @return {@link RpcError} implementation
+     */
+    public static RpcError getRpcError(String applicationTag, String tag, String info,
+            ErrorSeverity severity, String message, ErrorType errorType, Throwable cause) {
+        RpcErrorTO ret = new RpcErrorTO(applicationTag, tag, info, severity, message, 
+                errorType, cause);
+        return ret;
+    }
+
+    private static class RpcErrorTO implements RpcError {
+
+        private final String applicationTag;
+        private final String tag;
+        private final String info;
+        private final ErrorSeverity severity;
+        private final String message;
+        private final ErrorType errorType;
+        private final Throwable cause;
+
+        /**
+         * @param applicationTag
+         * @param tag
+         * @param info
+         * @param severity
+         * @param message
+         * @param errorType
+         * @param cause
+         */
+        protected RpcErrorTO(String applicationTag, String tag, String info,
+                ErrorSeverity severity, String message, ErrorType errorType, Throwable cause) {
+            super();
+            this.applicationTag = applicationTag;
+            this.tag = tag;
+            this.info = info;
+            this.severity = severity;
+            this.message = message;
+            this.errorType = errorType;
+            this.cause = cause;
+        }
+
+        @Override
+        public String getApplicationTag() {
+            return applicationTag;
+        }
+
+        @Override
+        public String getInfo() {
+            return info;
+        }
+
+        @Override
+        public String getMessage() {
+            return message;
+        }
+
+        @Override
+        public ErrorSeverity getSeverity() {
+            return severity;
+        }
+
+        @Override
+        public String getTag() {
+            return tag;
+        }
+
+        @Override
+        public Throwable getCause() {
+            return cause;
+        }
+        
+        @Override
+        public ErrorType getErrorType() {
+            return errorType;
+        }
+    }
+}
index 5ec72024c29eea8362b04e2d23772bfd161d0ed2..2ccb4409520557ef750e3c0d1861c4f9e558898c 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 a755f835600c266bebe6f59ec5363a6f2d831f32..bddcc2ae0eff9a14d8a4999d30484c83b17dcfaf 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 9b3dd0e8e294ef5274b6598251615fbfb1d8acc6..8be254e6b5eef24bae8eaf087289a81fbf3a851e 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 ebe9365320e6c05510714791e5aa2662dbeb2cf9..527dd0a02b3671451d6e612d3df25d69c7b0d1ba 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 43c297da1827f66fb98cb0d78ba6387b04ad6b22..457e99dcd415b951747bff05077a9d18853212dd 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 55a7ecb9cdd861a96c0f431b86823810d7a548c4..1deda7c9d0d73ff3250dbcbcb2d08c215f624954 100644 (file)
@@ -156,16 +156,20 @@ public class Subnet implements Cloneable, Serializable {
     }
 
     public boolean isSubnetOf(InetAddress ip) {
-        if (ip == null)
+        if (ip == null) {
             return false;
+        }
         InetAddress thisPrefix = getPrefixForAddress(this.networkAddress);
         InetAddress otherPrefix = getPrefixForAddress(ip);
-        if ((thisPrefix == null) || (otherPrefix == null))
+        if ((thisPrefix == null) || (otherPrefix == null)) {
             return false;
-        if (thisPrefix.equals(otherPrefix))
+        }
+        if (thisPrefix.equals(otherPrefix)) {
             return true;
-        else
+        }
+        else {
             return false;
+        }
     }
 
     public short getVlan() {
index d057c8e3ab71ae9b1e59a7d5c9e8d1506f991d67..d571d9df532d682650033bdf0ae94eca349d176a 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 eb8a07e70773ca0dbcbf42958d7a03c105ac8e2f..2a40eb0ff8cb0290ac9cd4cf078519f384e17843 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 c5f78ab56c09c2d8f9a073a1de963f1d21398ece..eb4bea584c5efa8d4737ca4b6b7b8de173aad25e 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 fb96928745978c4e5e427a48bbb9b249a40996ed..25e558eed962764acb357664ccaefd5cada33410 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 64e8ff5e0156924b6cef3f45bbbb0f275e54be36..b322f5845f6907379fb2c7ef13ad5fc4c5d23e5c 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 52b3ef3de3a25e8b6ea01c292c2efbf034362cae..0f1f0cf13503d0e3cf307dd9d2af223a6df6a81c 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 bc5c73a7d804c63b422295297685886a3636d368..98c740fa2a32d014bcca49d389a94f8cfa0eddfd 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 5e4f22afe2edc823afa29a45fcfa4c8b44da90c0..e59379916af8fba35f17da398458968fa01e8270 100644 (file)
@@ -11,6 +11,7 @@ public class ClusterNodeBean {
     private final String name;
     private final Boolean me;
     private final Boolean coordinator;
+    private final Integer numConnectedNodes;
 
     public static class Builder {
         // required params
@@ -20,6 +21,7 @@ public class ClusterNodeBean {
         // optional params
         private Boolean me = null;
         private Boolean coordinator = null;
+        private Integer numConnectedNodes = null;
 
         public Builder(InetAddress address) {
             this.address = address.getAddress();
@@ -36,6 +38,11 @@ public class ClusterNodeBean {
             return this;
         }
 
+        public Builder nodesConnected(int numNodes) {
+            this.numConnectedNodes = numNodes;
+            return this;
+        }
+
         public ClusterNodeBean build() {
             return new ClusterNodeBean(this);
         }
@@ -46,5 +53,6 @@ public class ClusterNodeBean {
         this.name = builder.name;
         this.me = builder.me;
         this.coordinator = builder.coordinator;
+        this.numConnectedNodes = builder.numConnectedNodes;
     }
 }
\ No newline at end of file
index 3b0b85c065cc8cf2de88205525aaaf6f89e84868..13a3a10667906c571d69ef1d9dac0229b7c85a15 100644 (file)
@@ -55,15 +55,29 @@ public class DaylightWebAdmin {
         if (clusterServices == null) {
             return null;
         }
+        IConnectionManager connectionManager = (IConnectionManager) ServiceHelper.getGlobalInstance(
+                IConnectionManager.class, this);
+        if (connectionManager == null) {
+            return null;
+        }
 
         List<ClusterNodeBean> clusterNodes = new ArrayList<ClusterNodeBean>();
 
         List<InetAddress> controllers = clusterServices.getClusteredControllers();
         for (InetAddress controller : controllers) {
             ClusterNodeBean.Builder clusterBeanBuilder = new ClusterNodeBean.Builder(controller);
+
+            //get number of connected nodes
+            Set<Node> connectedNodes = connectionManager.getNodes(controller);
+            int numNodes = connectedNodes == null ? 0 : connectedNodes.size();
+            clusterBeanBuilder.nodesConnected(numNodes);
+
+            //determine if this is the executing controller
             if (controller.equals(clusterServices.getMyAddress())) {
                 clusterBeanBuilder.highlightMe();
             }
+
+            //determine whether this is coordinator
             if (clusterServices.getCoordinatorAddress().equals(controller)) {
                 clusterBeanBuilder.iAmCoordinator();
             }
index 85e4d472ef0d5f89e3e274543e4040a3b2e22ae2..43a7dfdc443ab7447fb472dc31afa1b83402f9d7 100644 (file)
 one.main = {};
 
 one.main.constants = {
-    address : {
-        menu : "/web.json",
-        prefix : "/controller/web",
-        save : "/save"
-    }
+  address : {
+    menu : "/web.json",
+    prefix : "/controller/web",
+    save : "/save"
+  }
 }
 
 one.main.menu = {
-    load : function() {
-        one.main.menu.ajax(function(data) {
-            // reparse the ajax data
-            var result = one.main.menu.data.menu(data);
-            // transform into list to append to menu
-            var $div = one.main.menu.menu(result);
-            // append to menu
-            $("#menu .nav").append($div.children());
-            // binding all menu items
-            var $menu = $("#menu .nav a");
-            $menu.click(function() {
-                var href = $(this).attr('href').substring(1);
-                one.main.page.load(href);
-                var $li = $(this).parent();
-                // reset all other active
-                $menu.each(function(index, value) {
-                    $(value).parent().removeClass('active');
-                });
-                $li.addClass('active');
-            });
-            // reset or go to first menu item by default
-            var currentLocation = location.hash;
-            if (data[currentLocation.substring(1)] == undefined) {
-                $($menu[0]).click();
-            } else {
-                $menu.each(function(index, value) {
-                    var menuLocation = $(value).attr('href');
-                    if (currentLocation == menuLocation) {
-                        $($menu[index]).click();
-                        return;
-                    }
-                });
-            }
-        });
-    },
-    menu : function(result) {
-        var $div = $(document.createElement('div'));
-        $(result).each(function(index, value) {
-            if (value != undefined) {
-                var $li = $(document.createElement('li'));
-                var $a = $(document.createElement('a'));
-                $a.text(value['name']);
-                $a.attr('href', '#' + value['id']);
-                $li.append($a);
-                $div.append($li);
-            }
+  registry : {
+    load : false
+  },
+  load : function() {
+    one.main.menu.ajax(function(data) {
+      // reparse the ajax data
+      var result = one.main.menu.data.menu(data);
+      // transform into list to append to menu
+      var $div = one.main.menu.menu(result);
+      // append to menu
+      $("#menu .nav").append($div.children());
+      // binding all menu items
+      var $menu = $("#menu .nav a");
+      $menu.click(function() {
+        if (one.main.menu.registry.load === true) {
+          return false;
+        }
+        one.main.menu.registry.load = true;
+        var href = $(this).attr('href').substring(1);
+        one.main.page.load(href);
+        var $li = $(this).parent();
+        // reset all other active
+        $menu.each(function(index, value) {
+          $(value).parent().removeClass('active');
         });
-        return $div;
-    },
-    ajax : function(successCallback) {
-        $.getJSON(one.main.constants.address.menu, function(data) {
-            successCallback(data);
+        $li.addClass('active');
+      });
+      // reset or go to first menu item by default
+      var currentLocation = location.hash;
+      if (data[currentLocation.substring(1)] == undefined) {
+        $($menu[0]).click();
+      } else {
+        $menu.each(function(index, value) {
+          var menuLocation = $(value).attr('href');
+          if (currentLocation == menuLocation) {
+            $($menu[index]).click();
+            return;
+          }
         });
-    },
-    data : {
-        menu : function(data) {
-            var result = [];
-            $.each(data, function(key, value) {
-                var order = value['order'];
-                if (order >= 0) {
-                    var name = value['name'];
-                    var entry = {
-                        'name' : name,
-                        'id' : key
-                    };
-                    result[order] = entry;
-                }
-            });
-            return result;
+      }
+    });
+  },
+  menu : function(result) {
+    var $div = $(document.createElement('div'));
+    $(result).each(function(index, value) {
+      if (value != undefined) {
+        var $li = $(document.createElement('li'));
+        var $a = $(document.createElement('a'));
+        $a.text(value['name']);
+        $a.attr('href', '#' + value['id']);
+        $li.append($a);
+        $div.append($li);
+      }
+    });
+    return $div;
+  },
+  ajax : function(successCallback) {
+    $.getJSON(one.main.constants.address.menu, function(data) {
+      successCallback(data);
+    });
+  },
+  data : {
+    menu : function(data) {
+      var result = [];
+      $.each(data, function(key, value) {
+        var order = value['order'];
+        if (order >= 0) {
+          var name = value['name'];
+          var entry = {
+            'name' : name,
+        'id' : key
+          };
+          result[order] = entry;
         }
+      });
+      return result;
     }
+  }
 }
 
 one.main.page = {
-    load : function(page) {
-        if (one.f !== undefined && one.f.cleanUp !== undefined) {
-            one.f.cleanUp();
-        }
-        // clear page related
-        delete one.f;
-        $('.dashlet', '#main').empty();
-        $('.nav', '#main').empty();
-        // fetch page's js
-        $.getScript(one.main.constants.address.prefix + "/" + page
-                + "/js/page.js");
-
-        $.ajaxSetup({
-            data : {
-                'x-page-url' : page
-            }
-        });
-    },
-    dashlet : function($nav, dashlet) {
-        var $li = $(document.createElement('li'));
-        var $a = $(document.createElement('a'));
-        $a.text(dashlet.name);
-        $a.attr('id', dashlet.id);
-        $a.attr('href', '#');
-        $li.append($a);
-        $nav.append($li);
+  load : function(page) {
+    if (one.f !== undefined && one.f.cleanUp !== undefined) {
+      one.f.cleanUp();
     }
+    // clear page related
+    delete one.f;
+    $('.dashlet', '#main').empty();
+    $('.nav', '#main').empty();
+    // fetch page's js
+    $.getScript(one.main.constants.address.prefix+"/"+page+"/js/page.js")
+      .success(function() {
+        one.main.menu.registry.load = false;
+      });
+
+    $.ajaxSetup({
+      data : {
+        'x-page-url' : page
+      }
+    });
+  },
+  dashlet : function($nav, dashlet) {
+    var $li = $(document.createElement('li'));
+    var $a = $(document.createElement('a'));
+    $a.text(dashlet.name);
+    $a.attr('id', dashlet.id);
+    $a.attr('href', '#');
+    $li.append($a);
+    $nav.append($li);
+  }
 }
 
 one.main.admin = {
-    id : {
-        modal : {
-            main : "one_main_admin_id_modal_main",
-            close : "one_main_admin_id_modal_close",
-            user : "one_main_admin_id_modal_user",
-            add : {
-                user : "one_main_admin_id_modal_add_user",
-                close : "one_main_admin_id_modal_add_close",
-                form : {
-                    name : "one_main_admin_id_modal_add_form_name",
-                    role : "one_main_admin_id_modal_add_form_role",
-                    password : "one_main_admin_id_modal_add_form_password",
-                                       verify : "one_main_admin_id_modal_add_form_verify"
-                }
-            },
-            remove : {
-                user : "one_main_admin_id_modal_remove_user",
-                close : "one_main_admin_id_modal_remove_close",
-                               password : 'one_main_admin_id_modal_remove_password'
-            },
-                       password : {
-                               modal : 'one_main_admin_id_modal_password_modal',
-                               submit : 'one_main_admin_id_modal_password_submit',
-                               cancel : 'one_main_admin_id_modal_password_cancel',
-                               form : {
-                                       old : 'one_main_admin_id_modal_password_form_old',
-                                       set : 'one_main_admin_id_modal_password_form_new',
-                                       verify : 'one_main_admin_id_modal_password_form_verify'
-                               }
-                       }
-        },
-        add : {
-            user : "one_main_admin_id_add_user"
+  id : {
+    modal : {
+      main : "one_main_admin_id_modal_main",
+      close : "one_main_admin_id_modal_close",
+      user : "one_main_admin_id_modal_user",
+      add : {
+        user : "one_main_admin_id_modal_add_user",
+        close : "one_main_admin_id_modal_add_close",
+        form : {
+          name : "one_main_admin_id_modal_add_form_name",
+          role : "one_main_admin_id_modal_add_form_role",
+          password : "one_main_admin_id_modal_add_form_password",
+          verify : "one_main_admin_id_modal_add_form_verify"
+        }
+      },
+      remove : {
+        user : "one_main_admin_id_modal_remove_user",
+        close : "one_main_admin_id_modal_remove_close",
+        password : 'one_main_admin_id_modal_remove_password'
+      },
+      password : {
+        modal : 'one_main_admin_id_modal_password_modal',
+        submit : 'one_main_admin_id_modal_password_submit',
+        cancel : 'one_main_admin_id_modal_password_cancel',
+        form : {
+          old : 'one_main_admin_id_modal_password_form_old',
+          set : 'one_main_admin_id_modal_password_form_new',
+          verify : 'one_main_admin_id_modal_password_form_verify'
         }
+      }
     },
-    address : {
-        root : "/admin",
-        users : "/users",
-               password : '/admin/users/password/'
+    add : {
+      user : "one_main_admin_id_add_user"
+    }
+  },
+  address : {
+    root : "/admin",
+    users : "/users",
+    password : '/admin/users/password/'
+  },
+  modal : {
+    initialize : function(callback) {
+      var h3 = "Welcome " + $('#admin').text();
+      var footer = one.main.admin.modal.footer();
+      var $modal = one.lib.modal.spawn(one.main.admin.id.modal.main, h3,
+          '', footer);
+
+      // close binding
+      $('#' + one.main.admin.id.modal.close, $modal).click(function() {
+        $modal.modal('hide');
+      });
+
+      // body inject
+      one.main.admin.ajax.users(function($body) {
+        one.lib.modal.inject.body($modal, $body);
+      });
+
+      // modal show callback
+      callback($modal);
     },
-    modal : {
-        initialize : function(callback) {
-            var h3 = "Welcome " + $('#admin').text();
-            var footer = one.main.admin.modal.footer();
-            var $modal = one.lib.modal.spawn(one.main.admin.id.modal.main, h3,
-                    '', footer);
-
-            // close binding
-            $('#' + one.main.admin.id.modal.close, $modal).click(function() {
-                $modal.modal('hide');
-            });
+    footer : function() {
+      var footer = [];
+      var closeButton = one.lib.dashlet.button.single('Close', one.main.admin.id.modal.close, '', '');
+      var $closeButton = one.lib.dashlet.button.button(closeButton);
+      footer.push($closeButton);
+      return footer;
+    }
+  },
+  ajax : {
+    users : function(callback) {
+      $.getJSON(one.main.admin.address.root
+          + one.main.admin.address.users, function(data) {
+            var body = one.main.admin.data.users(data);
+            var $body = one.main.admin.body.users(body);
+            callback($body);
+          });
+    }
+  },
+  data : {
+    users : function(data) {
+      var body = [];
+      $(data).each(function(index, value) {
+        var tr = {};
+        var entry = [];
+        entry.push(value['user']);
+        entry.push(value['roles']);
+        tr['entry'] = entry;
+        tr['id'] = value['user'];
+        body.push(tr);
+      });
+      return body;
+    }
+  },
+  body : {
+    users : function(body) {
+      var $div = $(document.createElement('div'));
+      var $h5 = $(document.createElement('h5'));
+      $h5.append("Manage Users");
+      var attributes = [ "table-striped", "table-bordered",
+          "table-hover", "table-cursor" ];
+      var $table = one.lib.dashlet.table.table(attributes);
+      var headers = [ "User", "Role" ];
+      var $thead = one.lib.dashlet.table.header(headers);
+      var $tbody = one.lib.dashlet.table.body(body);
+      $table.append($thead).append($tbody);
+
+      // bind table
+      if (one.role < 2) {
+        $table.find('tr').click(function() {
+          var id = $(this).data('id');
+          one.main.admin.remove.modal.initialize(id);
+        });
+      }
 
-            // body inject
-            one.main.admin.ajax.users(function($body) {
-                one.lib.modal.inject.body($modal, $body);
-            });
+      // append to div
+      $div.append($h5).append($table);
 
-            // modal show callback
-            callback($modal);
-        },
-        footer : function() {
-            var footer = [];
-            var closeButton = one.lib.dashlet.button.single('Close', one.main.admin.id.modal.close, '', '');
-            var $closeButton = one.lib.dashlet.button.button(closeButton);
-            footer.push($closeButton);
-            return footer;
-        }
+      if (one.role < 2) {
+        var addUserButton = one.lib.dashlet.button.single("Add User",
+            one.main.admin.id.add.user, "btn-primary", "btn-mini");
+        var $addUserButton = one.lib.dashlet.button
+          .button(addUserButton);
+        $div.append($addUserButton);
+
+        // add user binding
+        $addUserButton.click(function() {
+          one.main.admin.add.modal.initialize();
+        });
+      }
+
+      return $div;
+    }
+  },
+  remove : {
+    modal : {
+      initialize : function(id) {
+        var h3 = "Edit User";
+        var footer = one.main.admin.remove.footer();
+        var $body = one.main.admin.remove.body();
+        var $modal = one.lib.modal.spawn(one.main.admin.id.modal.user,
+            h3, $body, footer);
+        // close binding
+        $('#'+one.main.admin.id.modal.remove.close, $modal).click(function() {
+          $modal.modal('hide');
+        });
+        // remove binding
+        $('#' + one.main.admin.id.modal.remove.user, $modal).click(function() {
+          one.main.admin.remove.modal.ajax(id, function(result) {
+            if (result == 'Success') {
+              $modal.modal('hide');
+              // body inject
+              var $admin = $('#'+one.main.admin.id.modal.main);
+              one.main.admin.ajax.users(function($body) {
+                one.lib.modal.inject.body($admin, $body);
+              });
+            } else {
+              alert("Failed to remove user: " + result);
+            }
+          });
+        });
+        // change password binding
+        $('#' + one.main.admin.id.modal.remove.password, $modal).click(function() {
+          one.main.admin.password.initialize(id, function() {
+            $modal.modal('hide');
+          });
+        });
+        $modal.modal();
+      },
+      ajax : function(id, callback) {
+        $.post(one.main.admin.address.root + one.main.admin.address.users + '/' + id, function(data) {
+          callback(data);
+        });
+      },
     },
-    ajax : {
-        users : function(callback) {
-            $.getJSON(one.main.admin.address.root
-                    + one.main.admin.address.users, function(data) {
-                var body = one.main.admin.data.users(data);
-                var $body = one.main.admin.body.users(body);
-                callback($body);
-            });
-        }
+    footer : function() {
+      var footer = [];
+      var removeButton = one.lib.dashlet.button.single("Remove User",
+          one.main.admin.id.modal.remove.user, "btn-danger", "");
+      var $removeButton = one.lib.dashlet.button.button(removeButton);
+      footer.push($removeButton);
+      var change = one.lib.dashlet.button.single('Change Password',
+          one.main.admin.id.modal.remove.password, 'btn-success', '');
+      var $change = one.lib.dashlet.button.button(change);
+      footer.push($change);
+      var closeButton = one.lib.dashlet.button.single("Close",
+          one.main.admin.id.modal.remove.close, "", "");
+      var $closeButton = one.lib.dashlet.button.button(closeButton);
+      footer.push($closeButton);
+      return footer;
     },
-    data : {
-        users : function(data) {
-            var body = [];
-            $(data).each(function(index, value) {
-                var tr = {};
-                var entry = [];
-                entry.push(value['user']);
-                entry.push(value['roles']);
-                tr['entry'] = entry;
-                tr['id'] = value['user'];
-                body.push(tr);
-            });
-            return body;
-        }
+    body : function() {
+      var $p = $(document.createElement('p'));
+      $p.append('Select an action');
+      return $p;
     },
-    body : {
-        users : function(body) {
-            var $div = $(document.createElement('div'));
-            var $h5 = $(document.createElement('h5'));
-            $h5.append("Manage Users");
-            var attributes = [ "table-striped", "table-bordered",
-                    "table-hover", "table-cursor" ];
-            var $table = one.lib.dashlet.table.table(attributes);
-            var headers = [ "User", "Role" ];
-            var $thead = one.lib.dashlet.table.header(headers);
-            var $tbody = one.lib.dashlet.table.body(body);
-            $table.append($thead).append($tbody);
-
-            // bind table
-            if (one.role < 2) {
-                $table.find('tr').click(function() {
-                    var id = $(this).data('id');
-                    one.main.admin.remove.modal.initialize(id);
-                });
+  },
+  add : {
+    modal : {
+      initialize : function() {
+        var h3 = "Add User";
+        var footer = one.main.admin.add.footer();
+        var $body = one.main.admin.add.body();
+        var $modal = one.lib.modal.spawn(one.main.admin.id.modal.user,
+            h3, $body, footer);
+        // close binding
+        $('#' + one.main.admin.id.modal.add.close, $modal).click(function() {
+          $modal.modal('hide');
+        });
+        // add binding
+        $('#' + one.main.admin.id.modal.add.user, $modal).click(function() {
+          one.main.admin.add.modal.add($modal, function(result) {
+            if (result == 'Success') {
+              $modal.modal('hide');
+              // body inject
+              var $admin = $('#'+one.main.admin.id.modal.main);
+              one.main.admin.ajax.users(function($body) {
+                one.lib.modal.inject.body($admin, $body);
+              });
+            } else {
+              alert("Failed to add user: "+result);
             }
+          });
+        });
+        $modal.modal();
+      },
+      add : function($modal, callback) {
+        var user = {};
+        user['user'] = $modal.find(
+            '#' + one.main.admin.id.modal.add.form.name).val();
+        user['password'] = $modal.find(
+            '#' + one.main.admin.id.modal.add.form.password).val();
+        roles = new Array();
+        roles[0] = $modal.find(
+            '#' + one.main.admin.id.modal.add.form.role).find(
+              'option:selected').attr('value');
+        user['roles'] = roles;
+
+        // password check
+        var verify = $('#'+one.main.admin.id.modal.add.form.verify).val();
+        if (user.password != verify) {
+          alert('Passwords do not match');
+          return false;
+        }
 
-            // append to div
-            $div.append($h5).append($table);
+        var resource = {};
+        resource['json'] = JSON.stringify(user);
+        resource['action'] = 'add'
 
-            if (one.role < 2) {
-                var addUserButton = one.lib.dashlet.button.single("Add User",
-                        one.main.admin.id.add.user, "btn-primary", "btn-mini");
-                var $addUserButton = one.lib.dashlet.button
-                        .button(addUserButton);
-                $div.append($addUserButton);
+          one.main.admin.add.modal.ajax(resource, callback);
+      },
+      ajax : function(data, callback) {
+        $.post(one.main.admin.address.root
+            + one.main.admin.address.users, data, function(data) {
+              callback(data);
+            });
+      }
+    },
+    body : function() {
+      var $form = $(document.createElement('form'));
+      var $fieldset = $(document.createElement('fieldset'));
+      // user
+      var $label = one.lib.form.label('Username');
+      var $input = one.lib.form.input('Username');
+      $input.attr('id', one.main.admin.id.modal.add.form.name);
+      $fieldset.append($label).append($input);
+      // password
+      var $label = one.lib.form.label('Password');
+      var $input = one.lib.form.input('Password');
+      $input.attr('id', one.main.admin.id.modal.add.form.password);
+      $input.attr('type', 'password');
+      $fieldset.append($label).append($input);
+      // password verify
+      var $label = one.lib.form.label('Verify Password');
+      var $input = one.lib.form.input('Verify Password');
+      $input.attr('id', one.main.admin.id.modal.add.form.verify);
+      $input.attr('type', 'password');
+      $fieldset.append($label).append($input);
+      // roles
+      var $label = one.lib.form.label('Roles');
+      var options = {
+        "Network-Admin" : "Network Administrator",
+        "Network-Operator" : "Network Operator"
+      };
+      var $select = one.lib.form.select.create(options);
+      $select.attr('id', one.main.admin.id.modal.add.form.role);
+      $fieldset.append($label).append($select);
+      $form.append($fieldset);
+      return $form;
+    },
+    footer : function() {
+      var footer = [];
 
-                // add user binding
-                $addUserButton.click(function() {
-                    one.main.admin.add.modal.initialize();
-                });
-            }
+      var addButton = one.lib.dashlet.button.single("Add User",
+          one.main.admin.id.modal.add.user, "btn-primary", "");
+      var $addButton = one.lib.dashlet.button.button(addButton);
+      footer.push($addButton);
 
-            return $div;
-        }
+      var closeButton = one.lib.dashlet.button.single("Close",
+          one.main.admin.id.modal.add.close, "", "");
+      var $closeButton = one.lib.dashlet.button.button(closeButton);
+      footer.push($closeButton);
+
+      return footer;
+    }
+  },
+  password : {
+    initialize : function(id, successCallback) {
+      var h3 = 'Change Password';
+      var footer = one.main.admin.password.footer();
+      var $body = one.main.admin.password.body(id);;
+      var $modal = one.lib.modal.spawn(one.main.admin.id.modal.password.modal,
+          h3, $body, footer);
+
+      // cancel binding
+      $('#'+one.main.admin.id.modal.password.cancel, $modal).click(function() {
+        $modal.modal('hide');
+      });
+
+      // change password binding
+      $('#'+one.main.admin.id.modal.password.submit, $modal).click(function() {
+        one.main.admin.password.submit(id, $modal, function(result) {
+          if (result.code == 'SUCCESS') {
+            $modal.modal('hide');
+            successCallback();
+          } else {
+            alert(result.code+': '+result.description);
+          }
+        });
+      });
+
+      $modal.modal();
     },
-    remove : {
-        modal : {
-            initialize : function(id) {
-                var h3 = "Edit User";
-                var footer = one.main.admin.remove.footer();
-                var $body = one.main.admin.remove.body();
-                var $modal = one.lib.modal.spawn(one.main.admin.id.modal.user,
-                        h3, $body, footer);
-                // close binding
-                $('#'+one.main.admin.id.modal.remove.close, $modal).click(function() {
-                                       $modal.modal('hide');
-                               });
-                // remove binding
-                $('#' + one.main.admin.id.modal.remove.user, $modal).click(function() {
-                                       one.main.admin.remove.modal.ajax(id, function(result) {
-                                               if (result == 'Success') {
-                                                       $modal.modal('hide');
-                                                       // body inject
-                                                       var $admin = $('#'+one.main.admin.id.modal.main);
-                                                       one.main.admin.ajax.users(function($body) {
-                                                               one.lib.modal.inject.body($admin, $body);
-                                                       });
-                                               } else {
-                                                       alert("Failed to remove user: " + result);
-                                               }
-                                       });
-                               });
-                               // change password binding
-                               $('#' + one.main.admin.id.modal.remove.password, $modal).click(function() {
-                                       one.main.admin.password.initialize(id, function() {
-                                               $modal.modal('hide');
-                                       });
-                               });
-                $modal.modal();
-            },
-            ajax : function(id, callback) {
-                $.post(one.main.admin.address.root + one.main.admin.address.users + '/' + id, function(data) {
-                       callback(data);
-                });
-            },
-        },
-        footer : function() {
-            var footer = [];
-            var removeButton = one.lib.dashlet.button.single("Remove User",
-                    one.main.admin.id.modal.remove.user, "btn-danger", "");
-            var $removeButton = one.lib.dashlet.button.button(removeButton);
-            footer.push($removeButton);
-                       var change = one.lib.dashlet.button.single('Change Password',
-                                       one.main.admin.id.modal.remove.password, 'btn-success', '');
-                       var $change = one.lib.dashlet.button.button(change);
-                       footer.push($change);
-            var closeButton = one.lib.dashlet.button.single("Close",
-                    one.main.admin.id.modal.remove.close, "", "");
-            var $closeButton = one.lib.dashlet.button.button(closeButton);
-            footer.push($closeButton);
-            return footer;
-        },
-        body : function() {
-            var $p = $(document.createElement('p'));
-            $p.append('Select an action');
-            return $p;
-        },
+    submit : function(id, $modal, callback) {
+      var resource = {};
+      resource.newPassword = $('#'+one.main.admin.id.modal.password.form.set, $modal).val();
+
+      // verify password
+      var verify = $('#'+one.main.admin.id.modal.password.form.verify, $modal).val();
+      if (verify != resource.newPassword) {
+        alert('Passwords do not match');
+        return false;
+      }
+
+      resource.currentPassword = $('#'+one.main.admin.id.modal.password.form.old, $modal).val();
+
+      $.post(one.main.admin.address.password+id, resource, function(data) {
+        callback(data);
+      });
     },
-    add : {
-        modal : {
-            initialize : function() {
-                var h3 = "Add User";
-                var footer = one.main.admin.add.footer();
-                var $body = one.main.admin.add.body();
-                var $modal = one.lib.modal.spawn(one.main.admin.id.modal.user,
-                        h3, $body, footer);
-                // close binding
-                $('#' + one.main.admin.id.modal.add.close, $modal).click(function() {
-                                       $modal.modal('hide');
-                               });
-                // add binding
-                $('#' + one.main.admin.id.modal.add.user, $modal).click(function() {
-                                       one.main.admin.add.modal.add($modal, function(result) {
-                                               if (result == 'Success') {
-                                                       $modal.modal('hide');
-                                                       // body inject
-                                                       var $admin = $('#'+one.main.admin.id.modal.main);
-                                                       one.main.admin.ajax.users(function($body) {
-                                                               one.lib.modal.inject.body($admin, $body);
-                                                       });
-                                               } else {
-                                                       alert("Failed to add user: "+result);
-                                               }
-                                       });
-                               });
-                $modal.modal();
-            },
-            add : function($modal, callback) {
-                var user = {};
-                user['user'] = $modal.find(
-                        '#' + one.main.admin.id.modal.add.form.name).val();
-                user['password'] = $modal.find(
-                        '#' + one.main.admin.id.modal.add.form.password).val();
-                roles = new Array();
-                roles[0] = $modal.find(
-                        '#' + one.main.admin.id.modal.add.form.role).find(
-                        'option:selected').attr('value');
-                user['roles'] = roles;
-
-                               // password check
-                               var verify = $('#'+one.main.admin.id.modal.add.form.verify).val();
-                               if (user.password != verify) {
-                                       alert('Passwords do not match');
-                                       return false;
-                               }
-
-                var resource = {};
-                resource['json'] = JSON.stringify(user);
-                resource['action'] = 'add'
-
-                one.main.admin.add.modal.ajax(resource, callback);
-            },
-            ajax : function(data, callback) {
-                $.post(one.main.admin.address.root
-                        + one.main.admin.address.users, data, function(data) {
-                    callback(data);
-                });
-            }
-        },
-        body : function() {
-            var $form = $(document.createElement('form'));
-            var $fieldset = $(document.createElement('fieldset'));
-            // user
-            var $label = one.lib.form.label('Username');
-            var $input = one.lib.form.input('Username');
-            $input.attr('id', one.main.admin.id.modal.add.form.name);
-            $fieldset.append($label).append($input);
-            // password
-            var $label = one.lib.form.label('Password');
-            var $input = one.lib.form.input('Password');
-            $input.attr('id', one.main.admin.id.modal.add.form.password);
-            $input.attr('type', 'password');
-            $fieldset.append($label).append($input);
-                       // password verify
-                       var $label = one.lib.form.label('Verify Password');
-                       var $input = one.lib.form.input('Verify Password');
-                       $input.attr('id', one.main.admin.id.modal.add.form.verify);
-                       $input.attr('type', 'password');
-                       $fieldset.append($label).append($input);
-            // roles
-            var $label = one.lib.form.label('Roles');
-            var options = {
-                "Network-Admin" : "Network Administrator",
-                "Network-Operator" : "Network Operator"
-            };
-            var $select = one.lib.form.select.create(options);
-            $select.attr('id', one.main.admin.id.modal.add.form.role);
-            $fieldset.append($label).append($select);
-            $form.append($fieldset);
-            return $form;
-        },
-        footer : function() {
-            var footer = [];
-
-            var addButton = one.lib.dashlet.button.single("Add User",
-                    one.main.admin.id.modal.add.user, "btn-primary", "");
-            var $addButton = one.lib.dashlet.button.button(addButton);
-            footer.push($addButton);
-
-            var closeButton = one.lib.dashlet.button.single("Close",
-                    one.main.admin.id.modal.add.close, "", "");
-            var $closeButton = one.lib.dashlet.button.button(closeButton);
-            footer.push($closeButton);
-
-            return footer;
-        }
+    body : function(id) {
+      var $form = $(document.createElement('form'));
+      var $fieldset = $(document.createElement('fieldset'));
+      // user
+      var $label = one.lib.form.label('Username');
+      var $input = one.lib.form.input('');
+      $input.attr('disabled', 'disabled');
+      $input.val(id);
+      $fieldset.append($label)
+        .append($input);
+      // old password
+      var $label = one.lib.form.label('Old Password');
+      var $input = one.lib.form.input('Old Password');
+      $input.attr('id', one.main.admin.id.modal.password.form.old);
+      $input.attr('type', 'password');
+      $fieldset.append($label).append($input);
+      // new password
+      var $label = one.lib.form.label('New Password');
+      var $input = one.lib.form.input('New Password');
+      $input.attr('id', one.main.admin.id.modal.password.form.set);
+      $input.attr('type', 'password');
+      $fieldset.append($label).append($input);
+      // verify new password
+      var $label = one.lib.form.label('Verify Password');
+      var $input = one.lib.form.input('Verify Password');
+      $input.attr('id', one.main.admin.id.modal.password.form.verify);
+      $input.attr('type', 'password');
+      $fieldset.append($label).append($input);
+      // return
+      $form.append($fieldset);
+      return $form;
     },
-       password : {
-               initialize : function(id, successCallback) {
-                       var h3 = 'Change Password';
-                       var footer = one.main.admin.password.footer();
-                       var $body = one.main.admin.password.body(id);;
-                       var $modal = one.lib.modal.spawn(one.main.admin.id.modal.password.modal,
-                               h3, $body, footer);
-
-                       // cancel binding
-                       $('#'+one.main.admin.id.modal.password.cancel, $modal).click(function() {
-                               $modal.modal('hide');
-                       });
-
-                       // change password binding
-                       $('#'+one.main.admin.id.modal.password.submit, $modal).click(function() {
-                               one.main.admin.password.submit(id, $modal, function(result) {
-                                       if (result.code == 'SUCCESS') {
-                                               $modal.modal('hide');
-                                               successCallback();
-                                       } else {
-                                               alert(result.code+': '+result.description);
-                                       }
-                               });
-                       });
-
-                       $modal.modal();
-               },
-               submit : function(id, $modal, callback) {
-                       var resource = {};
-                       resource.newPassword = $('#'+one.main.admin.id.modal.password.form.set, $modal).val();
-
-                       // verify password
-                       var verify = $('#'+one.main.admin.id.modal.password.form.verify, $modal).val();
-                       if (verify != resource.newPassword) {
-                               alert('Passwords do not match');
-                               return false;
-                       }
-
-                       resource.currentPassword = $('#'+one.main.admin.id.modal.password.form.old, $modal).val();
-
-                       $.post(one.main.admin.address.password+id, resource, function(data) {
-                               callback(data);
-                       });
-               },
-               body : function(id) {
-                       var $form = $(document.createElement('form'));
-                       var $fieldset = $(document.createElement('fieldset'));
-                       // user
-                       var $label = one.lib.form.label('Username');
-                       var $input = one.lib.form.input('');
-                       $input.attr('disabled', 'disabled');
-                       $input.val(id);
-                       $fieldset.append($label)
-                               .append($input);
-                       // old password
-            var $label = one.lib.form.label('Old Password');
-            var $input = one.lib.form.input('Old Password');
-            $input.attr('id', one.main.admin.id.modal.password.form.old);
-            $input.attr('type', 'password');
-            $fieldset.append($label).append($input);
-                       // new password
-            var $label = one.lib.form.label('New Password');
-            var $input = one.lib.form.input('New Password');
-            $input.attr('id', one.main.admin.id.modal.password.form.set);
-            $input.attr('type', 'password');
-            $fieldset.append($label).append($input);
-                       // verify new password
-                       var $label = one.lib.form.label('Verify Password');
-                       var $input = one.lib.form.input('Verify Password');
-                       $input.attr('id', one.main.admin.id.modal.password.form.verify);
-                       $input.attr('type', 'password');
-                       $fieldset.append($label).append($input);
-                       // return
-                       $form.append($fieldset);
-                       return $form;
-               },
-               footer : function() {
-                       var footer = [];
-                       var submit = one.lib.dashlet.button.single('Submit',
-                               one.main.admin.id.modal.password.submit, 'btn-primary', '');
-                       var $submit = one.lib.dashlet.button.button(submit);
-                       footer.push($submit);
-                       var cancel = one.lib.dashlet.button.single('Cancel',
-                               one.main.admin.id.modal.password.cancel, '', '');
-                       var $cancel = one.lib.dashlet.button.button(cancel);
-                       footer.push($cancel);
-                       return footer;
-               }
-       }
+    footer : function() {
+      var footer = [];
+      var submit = one.lib.dashlet.button.single('Submit',
+          one.main.admin.id.modal.password.submit, 'btn-primary', '');
+      var $submit = one.lib.dashlet.button.button(submit);
+      footer.push($submit);
+      var cancel = one.lib.dashlet.button.single('Cancel',
+          one.main.admin.id.modal.password.cancel, '', '');
+      var $cancel = one.lib.dashlet.button.button(cancel);
+      footer.push($cancel);
+      return footer;
+    }
+  }
 }
 
 one.main.cluster = {
-       id : { // one.main.cluster.id
-               modal : 'one-main-cluster-id-modal',
-               close : 'one-main-cluster-id-close',
-               datagrid : 'one-main-cluster-id-datagrid'
-       },
-       initialize : function() {
-               var h3 = 'Cluster Management';
-               var footer = one.main.cluster.footer();
-               var $body = '';
-               var $modal = one.lib.modal.spawn(one.main.cluster.id.modal, h3, $body, footer); 
-
-               // close
-               $('#'+one.main.cluster.id.close, $modal).click(function() {
-                       $modal.modal('hide');
-               });
-
-               // body
-               $.getJSON('/admin/cluster', function(data) {
-                       var $gridHTML = one.lib.dashlet.datagrid.init(one.main.cluster.id.datagrid, {
-                               searchable: true,
-                               filterable: false,
-                               pagination: true,
-                               flexibleRowsPerPage: true
-                       }, 'table-striped table-condensed table-cursor');
-                       var source = one.main.cluster.data(data);
-                       $gridHTML.datagrid({dataSource : source}).on('loaded', function() {
-                               $(this).find('tbody tr').click(function() {
-                                       var $tr = $(this);
-                                       if ($tr.find('td:nth-child(1)').attr('colspan') === '1') {
-                                               return false;
-                                       }
-                                       var address = $tr.find('.ux-id').text();
-                                       one.main.cluster.nodes.initialize(address);
-                               });
-                       });
-                       one.lib.modal.inject.body($modal, $gridHTML);
-               });
-
-               $modal.modal();
-       },
-       data : function(data) {
-               var tdata = [];
-               var registry = [];
-               $(data).each(function(idx, val) {
-                       var name = val.name;
-                       var address = val.address;
-                       var $registry = $(document.createElement('span'));
-                       $registry
-                               .append(JSON.stringify(address))
-                               .css('display', 'none')
-                               .addClass('ux-id');
-                       name = one.lib.dashlet.label(name, null)[0].outerHTML;
-                       name += $registry[0].outerHTML;
-                       if (val.me === true) {
-                               var me = one.lib.dashlet.label('*', 'label-inverse')[0].outerHTML;
-                               name += '&nbsp;'+me;
-                       }
-                       if (val.coordinator === true) {
-                               var coord = one.lib.dashlet.label('C')[0].outerHTML;
-                               name += '&nbsp;'+coord;
-                       }
-                       tdata.push({
-                               'controller' : name
-                       });
-               });
-               var source = new StaticDataSource({
-                       columns : [
-                               {
-                                       property : 'controller',
-                                       label : 'Controller',
-                                       sortable : true
-                               }
-                       ],
-                       data : tdata,
-                       delay : 0
-               });
-               return source;
-       },
-       footer : function() {
-               var footer = [];
-               var close = one.lib.dashlet.button.single('Close', one.main.cluster.id.close, '', '');
-               var $close = one.lib.dashlet.button.button(close);
-               footer.push($close);
-               return footer;
-       }
+  id : { // one.main.cluster.id
+    modal : 'one-main-cluster-id-modal',
+    close : 'one-main-cluster-id-close',
+    datagrid : 'one-main-cluster-id-datagrid'
+  },
+  initialize : function() {
+    var h3 = 'Cluster Management';
+    var footer = one.main.cluster.footer();
+    var $body = '';
+    var $modal = one.lib.modal.spawn(one.main.cluster.id.modal, h3, $body, footer); 
+
+    // close
+    $('#'+one.main.cluster.id.close, $modal).click(function() {
+      $modal.modal('hide');
+    });
+
+    // body
+    $.getJSON('/admin/cluster', function(data) {
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.main.cluster.id.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, 'table-striped table-condensed table-cursor');
+      var source = one.main.cluster.data(data);
+      $gridHTML.datagrid({dataSource : source}).on('loaded', function() {
+        $(this).find('tbody tr').click(function() {
+          var $tr = $(this);
+          if ($tr.find('td:nth-child(1)').attr('colspan') === '1') {
+            return false;
+          }
+          var address = $tr.find('.ux-id').text();
+          one.main.cluster.nodes.initialize(address);
+        });
+      });
+      one.lib.modal.inject.body($modal, $gridHTML);
+    });
+
+    $modal.modal();
+  },
+  data : function(data) {
+    var tdata = [];
+    var registry = [];
+    $(data).each(function(idx, controller) {
+      var name = controller.name;
+      var address = controller.address;
+      var $registry = $(document.createElement('span'));
+      $registry
+      .append(JSON.stringify(address))
+      .css('display', 'none')
+      .addClass('ux-id');
+    name = one.lib.dashlet.label(name, null)[0].outerHTML;
+    name += $registry[0].outerHTML;
+    if (controller.me === true) {
+      var me = one.lib.dashlet.label('*', 'label-inverse')[0].outerHTML;
+      name += '&nbsp;'+me;
+    }
+    if (controller.coordinator === true) {
+      var coord = one.lib.dashlet.label('C')[0].outerHTML;
+      name += '&nbsp;'+coord;
+    }
+    tdata.push({
+      'controller' : name,
+      'numNodes'   : controller.numConnectedNodes
+    });
+    });
+    var source = new StaticDataSource({
+        columns : [
+            {
+              property : 'controller',
+                label : 'Controller',
+                sortable : true
+            },
+            {
+                property : 'numNodes',
+                label    : 'Nodes',
+                sortable : true
+            }
+        ],
+        data : tdata,
+        delay : 0
+    });
+    return source;
+  },
+  footer : function() {
+    var footer = [];
+    var close = one.lib.dashlet.button.single('Close', one.main.cluster.id.close, '', '');
+    var $close = one.lib.dashlet.button.button(close);
+    footer.push($close);
+    return footer;
+  }
 }
 
 one.main.cluster.nodes = {
-       id : { // one.main.cluster.nodes.id
-               modal : 'one-main-cluster-nodes-id-modal',
-               close : 'one-main-cluster-nodes-id-close',
-               datagrid : 'one-main-cluser-nodes-id-datagrid'
-       },
-       initialize : function(address) { // one.main.cluster.nodes.initialize
-               var h3 = 'Connected Nodes';
-               var footer = one.main.cluster.nodes.footer();
-               var $body = '';
-               var $modal = one.lib.modal.spawn(one.main.cluster.nodes.id.modal, h3, $body, footer);
-
-               // close
-               $('#'+one.main.cluster.nodes.id.close, $modal).click(function() {
-                       $modal.modal('hide');
-               });
-               
-               // body
-               $.getJSON('/admin/cluster/controller/'+address, function(data) {
-                       var $gridHTML = one.lib.dashlet.datagrid.init(one.main.cluster.nodes.id.datagrid, {
-                               searchable: true,
-                               filterable: false,
-                               pagination: true,
-                               flexibleRowsPerPage: true
-                       }, 'table-striped table-condensed');
-                       var source = one.main.cluster.nodes.data(data);
-                       $gridHTML.datagrid({dataSource : source});
-                       one.lib.modal.inject.body($modal, $gridHTML);
-               });
-
-               $modal.modal();
-       },
-       data : function(data) {
-               var tdata = [];
-               $(data).each(function(idx, val) {
-                       tdata.push({
-                               'node' : val.description
-                       });
-               });
-               var source = new StaticDataSource({
-                       columns : [
-                               {
-                                       property : 'node',
-                                       label : 'Node',
-                                       sortable : true
-                               }
-                       ],
-                       data : tdata,
-                       delay : 0
-               });
-               return source;
-       },
-       footer : function() { // one.main.cluster.nodes.footer
-               var footer = [];
-               var close = one.lib.dashlet.button.single('Close', one.main.cluster.nodes.id.close, '', '');
-               var $close = one.lib.dashlet.button.button(close);
-               footer.push($close);
-               return footer;
-       }
+  id : { // one.main.cluster.nodes.id
+    modal : 'one-main-cluster-nodes-id-modal',
+    close : 'one-main-cluster-nodes-id-close',
+    datagrid : 'one-main-cluser-nodes-id-datagrid'
+  },
+  initialize : function(address) { // one.main.cluster.nodes.initialize
+    var h3 = 'Connected Nodes';
+    var footer = one.main.cluster.nodes.footer();
+    var $body = '';
+    var $modal = one.lib.modal.spawn(one.main.cluster.nodes.id.modal, h3, $body, footer);
+
+    // close
+    $('#'+one.main.cluster.nodes.id.close, $modal).click(function() {
+      $modal.modal('hide');
+    });
+
+    // body
+    $.getJSON('/admin/cluster/controller/'+address, function(data) {
+      var $gridHTML = one.lib.dashlet.datagrid.init(one.main.cluster.nodes.id.datagrid, {
+        searchable: true,
+          filterable: false,
+          pagination: true,
+          flexibleRowsPerPage: true
+      }, 'table-striped table-condensed');
+      var source = one.main.cluster.nodes.data(data);
+      $gridHTML.datagrid({dataSource : source});
+      one.lib.modal.inject.body($modal, $gridHTML);
+    });
+
+    $modal.modal();
+  },
+  data : function(data) {
+    var tdata = [];
+    $(data).each(function(idx, val) {
+      tdata.push({
+        'node' : val.description
+      });
+    });
+    var source = new StaticDataSource({
+      columns : [
+    {
+      property : 'node',
+        label : 'Node',
+        sortable : true
+    }
+    ],
+        data : tdata,
+        delay : 0
+    });
+    return source;
+  },
+  footer : function() { // one.main.cluster.nodes.footer
+    var footer = [];
+    var close = one.lib.dashlet.button.single('Close', one.main.cluster.nodes.id.close, '', '');
+    var $close = one.lib.dashlet.button.button(close);
+    footer.push($close);
+    return footer;
+  }
 }
 
 one.main.dashlet = {
-    left : {
-        top : $("#left-top .dashlet"),
-        bottom : $("#left-bottom .dashlet")
-    },
-    right : {
-        bottom : $("#right-bottom .dashlet")
-    }
+  left : {
+    top : $("#left-top .dashlet"),
+    bottom : $("#left-bottom .dashlet")
+  },
+  right : {
+    bottom : $("#right-bottom .dashlet")
+  }
 }
 
 /** BOOTSTRAP */
 $(".modal").on('hidden', function() {
-    $(this).remove();
+  $(this).remove();
 });
 
 $("#alert .close").click(function() {
-    $("#alert").hide();
+  $("#alert").hide();
 });
 
 /** INIT */
@@ -684,39 +699,39 @@ one.role = $('#admin').data('role');
 
 // user admin
 $("#admin").click(function() {
-    one.main.admin.modal.initialize(function($modal) {
-        $modal.modal();
-    });
+  one.main.admin.modal.initialize(function($modal) {
+    $modal.modal();
+  });
 });
 
 // cluster
 $('#cluster').click(function() {
-       one.main.cluster.initialize();
+  one.main.cluster.initialize();
 });
 
 // save
 $("#save").click(function() {
-    $.post(one.main.constants.address.save, function(data) {
-        if (data == "Success") {
-            one.lib.alert("Configuration Saved");
-        } else {
-            one.lib.alert("Unable to save configuration: " + data);
-        }
-    });
+  $.post(one.main.constants.address.save, function(data) {
+    if (data == "Success") {
+      one.lib.alert("Configuration Saved");
+    } else {
+      one.lib.alert("Unable to save configuration: " + data);
+    }
+  });
 });
 
 // logout
 $("#logout").click(function() {
-    location.href = "/logout";
+  location.href = "/logout";
 });
 
 $.ajaxSetup({
-    complete : function(xhr, textStatus) {
-        var mime = xhr.getResponseHeader('Content-Type');
-        if (mime.substring(0, 9) == 'text/html') {
-            location.href = '/';
-        }
+  complete : function(xhr, textStatus) {
+    var mime = xhr.getResponseHeader('Content-Type');
+    if (mime.substring(0, 9) == 'text/html') {
+      location.href = '/';
     }
+  }
 });
 
 /** MAIN PAGE LOAD */
index f2f7a3e59e46b21beca8a621dba16a93356242c3..c4c66e46d5af0bd9d0483807fa50e38339b5c4dd 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 38b82494ea354b087bc989168fb4ca73d807ecde..2be78c49a52a835cf6bd9b22e2bf447617a191f0 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>
index 74e39b225cc58cd312dab0270c428211a819b2b6..361a03b92647f61e51f792c38db0010ee245c0d4 100644 (file)
@@ -128,10 +128,11 @@ public class OFError extends OFMessage implements OFMessageFactoryAware {
         // OVS apparently sends partial messages in errors
         // need to be careful of that AND can't use data.limit() as
         // a packet boundary because there could be more data queued
-        if (messages.size() > 0)
+        if (messages.size() > 0) {
             return messages.get(0);
-        else
+        } else {
             return null;
+        }
     }
 
     /**
index d5e5ab05463eac633e627cdd506909749a0708ca..f26ca51a790cc3439b024949e4f3920991a65831 100644 (file)
@@ -74,11 +74,12 @@ import org.openflow.util.HexString;
                
                public void setNextHop(InetAddress address) {
                        short actionLen;
-                       if (address instanceof Inet4Address) 
-                               actionLen = (short)ONHLength.ONH_LEN_IPV4.getValue();
-                       else
-                               actionLen = (short)ONHLength.ONH_LEN_IPV6.getValue();
-                       super.setLength(actionLen);
+                       if (address instanceof Inet4Address) {
+                actionLen = (short)ONHLength.ONH_LEN_IPV4.getValue();
+            } else {
+                actionLen = (short)ONHLength.ONH_LEN_IPV6.getValue();
+            }
+            super.setLength(actionLen);
                        this.address = address;
                }
                public InetAddress getNextHop() {