XOS integration app

Change-Id: I2fc1cd5bec032ad027cbb685d8f03df732717fcc
diff --git a/apps/pom.xml b/apps/pom.xml
index c78e780..4b88dfa 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -48,6 +48,7 @@
         <module>test</module>
         <module>segmentrouting</module>
         <module>cordfabric</module>
+        <module>xos-integration</module>
     </modules>
 
     <properties>
diff --git a/apps/xos-integration/features.xml b/apps/xos-integration/features.xml
new file mode 100644
index 0000000..65f9c7f
--- /dev/null
+++ b/apps/xos-integration/features.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${short.version}">
+    <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository>
+    <feature name="${project.artifactId}" version="${project.version}"
+             description="${project.description}">
+        <bundle>mvn:com.sun.jersey/jersey-client/1.19</bundle>
+        <bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
+    </feature>
+</features>
diff --git a/apps/xos-integration/pom.xml b/apps/xos-integration/pom.xml
new file mode 100644
index 0000000..8f5aa86
--- /dev/null
+++ b/apps/xos-integration/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2014 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-app-xos-integration</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>ONOS XOS integration application</description>
+
+    <properties>
+        <onos.app.name>org.onosproject.xosintegration</onos.app.name>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-client</artifactId>
+            <version>1.19</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/apps/xos-integration/src/main/java/org/onosproject/xosintegration/OnosXOSIntegrationManager.java b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/OnosXOSIntegrationManager.java
new file mode 100644
index 0000000..d62cbde
--- /dev/null
+++ b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/OnosXOSIntegrationManager.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.xosintegration;
+
+import java.util.Dictionary;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import com.eclipsesource.json.JsonArray;
+import com.eclipsesource.json.JsonObject;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static com.google.common.net.MediaType.JSON_UTF_8;
+import static java.net.HttpURLConnection.HTTP_CREATED;
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * XOS interface application.
+ */
+@Component(immediate = true)
+@Service
+public class OnosXOSIntegrationManager implements VoltTenantService {
+
+    private static final String TEST_XOS_SERVER_ADDRESS = "10.254.1.22";
+    private static final int TEST_XOS_SERVER_PORT = 8000;
+    private static final String XOS_TENANT_BASE_URI = "/xoslib/volttenant/";
+
+    private final Logger log = getLogger(getClass());
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+    @Property(name = "XOSServerAddress",
+              value = TEST_XOS_SERVER_ADDRESS,
+              label = "XOS Server address")
+    protected String xosServerAddress = TEST_XOS_SERVER_ADDRESS;
+    @Property(name = "XOSServerPort",
+              intValue = TEST_XOS_SERVER_PORT,
+              label = "XOS Server port")
+    protected int xosServerPort = TEST_XOS_SERVER_PORT;
+    private ApplicationId appId;
+
+    @Activate
+    public void activate(ComponentContext context) {
+        log.info("XOS app is starting");
+        cfgService.registerProperties(getClass());
+        appId = coreService.registerApplication("org.onosproject.xosintegration");
+        readComponentConfiguration(context);
+
+        log.info("XOS({}) started", appId.id());
+    }
+
+    @Deactivate
+    public void deactivate() {
+        cfgService.unregisterProperties(getClass(), false);
+        log.info("XOS({}) stopped", appId.id());
+    }
+
+    @Modified
+    public void modified(ComponentContext context) {
+        readComponentConfiguration(context);
+    }
+
+    /**
+     * Converts a JSON representation of a tenant into a tenant object.
+     *
+     * @param jsonTenant JSON object representing the tenant
+     * @return volt tenant object
+     */
+    private VoltTenant jsonToTenant(JsonObject jsonTenant) {
+        return VoltTenant.builder()
+                .withHumanReadableName(jsonTenant.get("humanReadableName").asString())
+                .withId(jsonTenant.get("id").asInt())
+                .withProviderService(jsonTenant.get("provider_service").asInt())
+                .withServiceSpecificId(jsonTenant.get("service_specific_id").asString())
+                .withVlanId(jsonTenant.get("vlan_id").asString())
+                .build();
+    }
+
+    /**
+     * Converts a tenant object into a JSON string.
+     *
+     * @param tenant volt tenant object to convert
+     * @return JSON string for the tenant
+     */
+    private String tenantToJson(VoltTenant tenant) {
+        return "{"
+                    + "\"humanReadableName\": \"" + tenant.humanReadableName() + "\","
+                    + "\"id\": \"" + tenant.id() + "\","
+                    + "\"provider_service\": \"" + tenant.providerService() + "\","
+                    + "\"service_specific_id\": \"" + tenant.serviceSpecificId() + "\","
+                    + "\"vlan_id\": \"" + tenant.vlanId() + "\""
+                    + "}";
+    }
+
+    /**
+     * Gets a client web resource builder for the base XOS REST API
+     * with no additional URI.
+     *
+     * @return web resource builder
+     */
+    private WebResource.Builder getClientBuilder() {
+        return getClientBuilder("");
+    }
+
+    /**
+     * Gets a client web resource builder for the base XOS REST API
+     * with an optional additional URI.
+     *
+     * @return web resource builder
+     */
+    private WebResource.Builder getClientBuilder(String uri) {
+        String baseUrl = "http://" + xosServerAddress + ":"
+                + Integer.toString(xosServerPort);
+        Client client = Client.create();
+        client.addFilter(new HTTPBasicAuthFilter("padmin@vicci.org", "letmein"));
+        WebResource resource = client.resource(baseUrl
+                + XOS_TENANT_BASE_URI + uri);
+        return resource.accept(JSON_UTF_8.toString())
+                .type(JSON_UTF_8.toString());
+    }
+
+    /**
+     * Performs a REST GET operation on the base XOS REST URI.
+     *
+     * @return JSON string fetched by the GET operation
+     */
+    private String getRest() {
+        return getRest("");
+    }
+
+    /**
+     * Performs a REST GET operation on the base XOS REST URI with
+     * an optional additional URI.
+     *
+     * @return JSON string fetched by the GET operation
+     */
+    private String getRest(String uri) {
+        WebResource.Builder builder = getClientBuilder(uri);
+        ClientResponse response = builder.get(ClientResponse.class);
+
+        if (response.getStatus() != HTTP_OK) {
+            log.info("REST GET request returned error code {}",
+                    response.getStatus());
+        }
+        String jsonString = response.getEntity(String.class);
+        log.info("JSON read:\n{}", jsonString);
+
+        return jsonString;
+    }
+
+    /**
+     * Performs a REST POST operation of a json string on the base
+     * XOS REST URI with an optional additional URI.
+     *
+     * @param json JSON string to post
+     */
+    private void postRest(String json) {
+        WebResource.Builder builder = getClientBuilder();
+        ClientResponse response = builder.post(ClientResponse.class, json);
+
+        if (response.getStatus() != HTTP_CREATED) {
+            log.info("REST POST request returned error code {}",
+                    response.getStatus());
+        }
+    }
+
+    /**
+     * Performs a REST DELETE operation on the base
+     * XOS REST URI with an optional additional URI.
+     *
+     * @param uri optional additional URI
+     */
+    private void deleteRest(String uri) {
+        WebResource.Builder builder = getClientBuilder(uri);
+        ClientResponse response = builder.delete(ClientResponse.class);
+
+        if (response.getStatus() != HTTP_NO_CONTENT) {
+            log.info("REST DELETE request returned error code {}",
+                    response.getStatus());
+        }
+    }
+
+    /**
+     * Deletes the tenant with the given ID.
+     *
+     * @param tenantId ID of tenant to delete
+     */
+    private void deleteTenant(long tenantId) {
+        deleteRest(Long.toString(tenantId));
+    }
+
+    @Override
+    public Set<VoltTenant> getAllTenants() {
+        String jsonString = getRest();
+
+        JsonArray voltTenantItems = JsonArray.readFrom(jsonString);
+
+        return IntStream.range(0, voltTenantItems.size())
+                .mapToObj(index -> jsonToTenant(voltTenantItems.get(index).asObject()))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public void removeTenant(long id) {
+        deleteTenant(id);
+    }
+
+    @Override
+    public VoltTenant addTenant(VoltTenant newTenant) {
+        String json = tenantToJson(newTenant);
+        postRest(json);
+        return newTenant;
+    }
+
+    @Override
+    public VoltTenant getTenant(long id) {
+        String jsonString = getRest(Long.toString(id));
+        JsonObject jsonTenant = JsonObject.readFrom(jsonString);
+        if (jsonTenant.get("id") != null) {
+            return jsonToTenant(jsonTenant);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Extracts properties from the component configuration context.
+     *
+     * @param context the component context
+     */
+    private void readComponentConfiguration(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+
+        String newXosServerAddress = Tools.get(properties, "XOSServerAddress");
+        if (!isNullOrEmpty(newXosServerAddress)) {
+            xosServerAddress = newXosServerAddress;
+        }
+
+        String newXosServerPortString = Tools.get(properties, "XOSServerPort");
+        if (!isNullOrEmpty(newXosServerPortString)) {
+            xosServerPort = Integer.parseInt(newXosServerPortString);
+        }
+        log.info("XOS URL is now http://{}:{}", xosServerAddress, xosServerPort);
+    }
+}
+
+
diff --git a/apps/xos-integration/src/main/java/org/onosproject/xosintegration/VoltTenant.java b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/VoltTenant.java
new file mode 100644
index 0000000..e35a630
--- /dev/null
+++ b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/VoltTenant.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.xosintegration;
+
+
+import com.google.common.base.MoreObjects;
+
+public final class VoltTenant {
+
+    private final String humanReadableName;
+    private final long id;
+    private final long providerService;
+    private final String serviceSpecificId;
+    private final String vlanId;
+
+    private VoltTenant(String humanReadableName, long id, long providerService,
+                       String serviceSpecificId, String vlanId) {
+        this.humanReadableName = humanReadableName;
+        this.id = id;
+        this.providerService = providerService;
+        this.serviceSpecificId = serviceSpecificId;
+        this.vlanId = vlanId;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public String humanReadableName() {
+        return humanReadableName;
+    }
+
+    public long id() {
+        return id;
+    }
+
+    public long providerService() {
+        return providerService;
+    }
+
+    public String serviceSpecificId() {
+        return serviceSpecificId;
+    }
+
+    public String vlanId() {
+        return vlanId;
+    }
+
+    public static final class Builder {
+        private String humanReadableName = "unknown";
+        private long id = 0;
+        private long providerService = 0;
+        private String serviceSpecificId = "unknown";
+        private String vlanId = "unknown";
+
+        public Builder withHumanReadableName(String humanReadableName) {
+            this.humanReadableName = humanReadableName;
+            return this;
+        }
+
+        public Builder withId(long id) {
+            this.id = id;
+            return this;
+        }
+
+        public Builder withProviderService(long providerService) {
+            this.providerService = providerService;
+            return this;
+        }
+
+        public Builder withServiceSpecificId(String serviceSpecificId) {
+            this.serviceSpecificId = serviceSpecificId;
+            return this;
+        }
+
+        public Builder withVlanId(String vlanId) {
+            this.vlanId = vlanId;
+            return this;
+        }
+
+        public VoltTenant build() {
+            return new VoltTenant(humanReadableName, id, providerService,
+                    serviceSpecificId, vlanId);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("humanReadableName", humanReadableName())
+                .add("id", id())
+                .add("providerService", providerService())
+                .add("serviceSpecificId", serviceSpecificId())
+                .add("vlanId", vlanId())
+                .toString();
+    }
+
+}
diff --git a/apps/xos-integration/src/main/java/org/onosproject/xosintegration/VoltTenantService.java b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/VoltTenantService.java
new file mode 100644
index 0000000..7cf46ea
--- /dev/null
+++ b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/VoltTenantService.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.xosintegration;
+
+import java.util.Set;
+
+public interface VoltTenantService {
+
+    /**
+     * Queries all the tenants.
+     *
+     * @return Set of all of the tenants
+     */
+    Set<VoltTenant> getAllTenants();
+
+    /**
+     * Removes a tenant given its ID.
+     *
+     * @param id if od tenant to remove.
+     */
+    void removeTenant(long id);
+
+    /**
+     * Creates a new tenant and adds it to the XOS instance.
+     *
+     * @param newTenant tenant to add
+     * @return the added tenant
+     */
+    VoltTenant addTenant(VoltTenant newTenant);
+
+    /**
+     * Gets a single tenant for the given ID.
+     *
+     * @param id ID of the tenant to fetch
+     * @return tenant that was fetched
+     */
+    VoltTenant getTenant(long id);
+}
diff --git a/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/TenantIdCompleter.java b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/TenantIdCompleter.java
new file mode 100644
index 0000000..4533079
--- /dev/null
+++ b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/TenantIdCompleter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.xosintegration.cli;
+
+import java.util.List;
+
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.xosintegration.VoltTenant;
+import org.onosproject.xosintegration.VoltTenantService;
+import static java.util.stream.Collectors.toList;
+
+import static org.onosproject.cli.AbstractShellCommand.get;
+
+
+/**
+ * Application command completer.
+ */
+public class TenantIdCompleter extends AbstractChoicesCompleter {
+    @Override
+    public List<String> choices() {
+        VoltTenantService service = get(VoltTenantService.class);
+
+        return service.getAllTenants().stream()
+                .map(VoltTenant::id)
+                .map(Object::toString)
+                .collect(toList());
+
+    }
+
+}
diff --git a/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/VoltRemoveTenantCommand.java b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/VoltRemoveTenantCommand.java
new file mode 100644
index 0000000..0c44eb5
--- /dev/null
+++ b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/VoltRemoveTenantCommand.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.xosintegration.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.xosintegration.VoltTenantService;
+
+/**
+ * CLI command to remove an existing tenant from the system.
+ */
+@Command(scope = "onos", name = "remove-tenant",
+        description = "Removes a tenant")
+public class VoltRemoveTenantCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "tenant",
+            description = "Tenant ID",
+            required = true, multiValued = false)
+    String tenantIdString = null;
+
+    @Override
+    protected void execute() {
+        VoltTenantService service = get(VoltTenantService.class);
+
+        service.removeTenant(Long.parseLong(tenantIdString));
+    }
+}
diff --git a/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/VoltTenantsCreateCommand.java b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/VoltTenantsCreateCommand.java
new file mode 100644
index 0000000..76605df
--- /dev/null
+++ b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/VoltTenantsCreateCommand.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.xosintegration.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.xosintegration.VoltTenant;
+import org.onosproject.xosintegration.VoltTenantService;
+
+/**
+ * CLI command to create a new tenant.
+ */
+@Command(scope = "onos", name = "add-tenant",
+        description = "Lists the inventory of VOLT tenants and their contents")
+public class VoltTenantsCreateCommand extends AbstractShellCommand {
+    @Argument(index = 0, name = "provider service",
+            description = "Tenant ID",
+            required = true, multiValued = false)
+    long providerService;
+
+    @Argument(index = 1, name = "service specific ID",
+            description = "service specific ID",
+            required = true, multiValued = false)
+    String serviceSpecificId;
+
+    @Argument(index = 2, name = "vlan ID",
+            description = "vlan ID",
+            required = true, multiValued = false)
+    String vlanId;
+
+    @Override
+    protected void execute() {
+        VoltTenantService service = get(VoltTenantService.class);
+
+        VoltTenant newTenant = VoltTenant.builder()
+                .withProviderService(providerService)
+                .withServiceSpecificId(serviceSpecificId)
+                .withVlanId(vlanId)
+                .build();
+        service.addTenant(newTenant);
+    }
+}
diff --git a/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/VoltTenantsListCommand.java b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/VoltTenantsListCommand.java
new file mode 100644
index 0000000..e1e621b
--- /dev/null
+++ b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/cli/VoltTenantsListCommand.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.xosintegration.cli;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.xosintegration.VoltTenant;
+import org.onosproject.xosintegration.VoltTenantService;
+
+/**
+ * CLI command for listing VOLT tenant objects.
+ */
+
+/**
+ * CLI command to list the existing tenants.
+ */
+@Command(scope = "onos", name = "tenants",
+        description = "Lists the inventory of VOLT tenants and their contents")
+public class VoltTenantsListCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "tenantId",
+            description = "Tenant ID",
+            required = false, multiValued = false)
+    private String tenantId = null;
+
+    @Override
+    protected void execute() {
+        VoltTenantService service = get(VoltTenantService.class);
+
+        if (tenantId != null) {
+            VoltTenant tenant = service.getTenant(Long.parseLong(tenantId));
+            if (tenant != null) {
+                print(tenant.toString());
+            } else {
+                error("Tenant not found {}", tenantId);
+            }
+        } else {
+            Set<VoltTenant> tenants = service.getAllTenants();
+            for (VoltTenant tenant : tenants) {
+                print(tenant.toString());
+            }
+        }
+    }
+
+}
diff --git a/apps/xos-integration/src/main/java/org/onosproject/xosintegration/package-info.java b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/package-info.java
new file mode 100644
index 0000000..41a1ad3
--- /dev/null
+++ b/apps/xos-integration/src/main/java/org/onosproject/xosintegration/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test Application that calls a REST API. One dat it might call XOS to
+ * launch a VM.
+ */
+package org.onosproject.xosintegration;
diff --git a/apps/xos-integration/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/xos-integration/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..9c95117
--- /dev/null
+++ b/apps/xos-integration/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,37 @@
+<!--
+  ~ Copyright 2014 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.onosproject.xosintegration.cli.VoltTenantsListCommand"/>
+            <completers>
+                <ref component-id="tenantIdCompleter"/>
+                <null/>
+            </completers>
+        </command>
+        <command>
+            <action class="org.onosproject.xosintegration.cli.VoltTenantsCreateCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.xosintegration.cli.VoltRemoveTenantCommand"/>
+            <completers>
+                <ref component-id="tenantIdCompleter"/>
+                <null/>
+            </completers>
+        </command>
+    </command-bundle>
+    <bean id="tenantIdCompleter" class="org.onosproject.xosintegration.cli.TenantIdCompleter"/>
+</blueprint>
diff --git a/tools/package/etc/samples/org.onosproject.xos.XOS.cfg b/tools/package/etc/samples/org.onosproject.xos.XOS.cfg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/package/etc/samples/org.onosproject.xos.XOS.cfg