Merge remote-tracking branch 'origin/master' into dev/murrelet
diff --git a/cli/src/main/java/org/onosproject/cli/PrettyXml.java b/cli/src/main/java/org/onosproject/cli/PrettyXml.java
new file mode 100644
index 0000000..9b3635f
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/PrettyXml.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.cli;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.util.XmlString;
+
+import com.google.common.io.CharStreams;
+
+/**
+ * Pretty print previous command output XML.
+ */
+@Command(scope = "onos", name = "ppxml",
+         description = "Pretty print XML output from previous command")
+public class PrettyXml extends AbstractShellCommand {
+
+    @Override
+    protected void execute() {
+        try {
+            String xmlS = CharStreams.toString(new InputStreamReader(System.in));
+
+            print("%s", XmlString.prettifyXml(xmlS));
+        } catch (IOException e) {
+            log.error("Failed parsing XML.", e);
+            print("%s", e);
+        }
+    }
+
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index bc33931..27e2b4f 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -26,6 +26,10 @@
         </command>
 
         <command>
+            <action class="org.onosproject.cli.PrettyXml"/>
+        </command>
+
+        <command>
             <action class="org.onosproject.cli.SummaryCommand"/>
         </command>
         <command>
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
index bcaed68..9647bc9 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.config.basics;
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.ImmutableSet;
 import org.onlab.packet.IpAddress;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.HostId;
@@ -40,7 +41,7 @@
         this.locations();
         this.ipAddresses();
         return hasOnlyFields(ALLOWED, NAME, LOC_TYPE, LATITUDE, LONGITUDE,
-                GRID_Y, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATIONS);
+                             GRID_Y, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATIONS);
     }
 
     @Override
@@ -57,21 +58,29 @@
     /**
      * Returns the location of the host.
      *
-     * @return location of the host
-     * @throws IllegalArgumentException if not specified with correct format
+     * @return location of the host or null if none specified
+     * @throws IllegalArgumentException if locations are set but empty or not
+     *                                  specified with correct format
      */
     public Set<HostLocation> locations() {
-        HashSet<HostLocation> locations = new HashSet<>();
-        if (object.has(LOCATIONS)) {
-            ArrayNode locationNodes = (ArrayNode) object.path(LOCATIONS);
-            locationNodes.forEach(n -> {
-                ConnectPoint cp = ConnectPoint.deviceConnectPoint((n.asText()));
-                locations.add(new HostLocation(cp, 0));
-            });
+        if (!object.has(LOCATIONS)) {
+            return null; //no locations are specified
         }
+
+        ImmutableSet.Builder<HostLocation> locationsSetBuilder = ImmutableSet.<HostLocation>builder();
+
+        ArrayNode locationNodes = (ArrayNode) object.path(LOCATIONS);
+        locationNodes.forEach(n -> {
+            ConnectPoint cp = ConnectPoint.deviceConnectPoint((n.asText()));
+            locationsSetBuilder.add(new HostLocation(cp, 0));
+        });
+
+
+        Set<HostLocation> locations = locationsSetBuilder.build();
         if (locations.isEmpty()) {
             throw new IllegalArgumentException("Host should have at least one location");
         }
+
         return locations;
     }
 
@@ -110,4 +119,4 @@
     public BasicHostConfig setIps(Set<IpAddress> ipAddresses) {
         return (BasicHostConfig) setOrClear(IPS, ipAddresses);
     }
-}
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java b/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java
index 0931aa8..21450c0 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java
@@ -364,6 +364,7 @@
                     (k, v) -> new InternalApplicationHolder(
                             v.app(), ACTIVATED, v.permissions()));
             appActivationTopic.publish(vAppHolder.value().app());
+            appActivationTopic.publish(null); // FIXME: Once ONOS-6977 is fixed
         }
     }
 
@@ -439,10 +440,12 @@
     private class AppActivator implements Consumer<Application> {
         @Override
         public void accept(Application app) {
-            String appName = app.id().name();
-            installAppIfNeeded(app);
-            setActive(appName);
-            notifyDelegate(new ApplicationEvent(APP_ACTIVATED, app));
+            if (app != null) { // FIXME: Once ONOS-6977 is fixed
+                String appName = app.id().name();
+                installAppIfNeeded(app);
+                setActive(appName);
+                notifyDelegate(new ApplicationEvent(APP_ACTIVATED, app));
+            }
         }
     }
 
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeHandshaker.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeHandshaker.java
index 82a9b85..ee070eb 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeHandshaker.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeHandshaker.java
@@ -55,7 +55,7 @@
 
         String serverAddr = data.value("p4runtime_ip");
         int serverPort = Integer.valueOf(data.value("p4runtime_port"));
-        int p4DeviceId = Integer.valueOf(data.value("p4runtime_deviceId"));
+        long p4DeviceId = Long.parseUnsignedLong(data.value("p4runtime_deviceId"));
 
         ManagedChannelBuilder channelBuilder = NettyChannelBuilder
                 .forAddress(serverAddr, serverPort)
diff --git a/incubator/protobuf/api/BUCK b/incubator/protobuf/api/BUCK
new file mode 100644
index 0000000..9ae1ccf
--- /dev/null
+++ b/incubator/protobuf/api/BUCK
@@ -0,0 +1,15 @@
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//incubator/grpc-dependencies:grpc-core-repkg-1.3.0'
+]
+
+TEST_DEPS = [
+    '//lib:TEST_ADAPTERS',
+]
+
+osgi_jar_with_tests (
+    name = 'onos-grpc-api',
+    deps = COMPILE_DEPS,
+    test_deps = TEST_DEPS,
+    visibility = ['PUBLIC'],
+)
diff --git a/incubator/protobuf/api/pom.xml b/incubator/protobuf/api/pom.xml
new file mode 100644
index 0000000..ead8e3c
--- /dev/null
+++ b/incubator/protobuf/api/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017-present Open Networking Foundation
+  ~
+  ~ 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/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>onos-incubator-protobuf</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.12.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-incubator-grpc-api</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>ONOS gRPC service registry API</description>
+    <url>http://onosproject.org</url>
+
+    <properties>
+    </properties>
+
+    <build>
+
+        <plugins>
+
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.onosproject</groupId>
+                <artifactId>onos-maven-plugin</artifactId>
+            </plugin>
+
+        </plugins>
+    </build>
+
+</project>
diff --git a/incubator/protobuf/api/src/main/java/org/onosproject/protobuf/api/GrpcServiceRegistry.java b/incubator/protobuf/api/src/main/java/org/onosproject/protobuf/api/GrpcServiceRegistry.java
new file mode 100644
index 0000000..5e7e4bc
--- /dev/null
+++ b/incubator/protobuf/api/src/main/java/org/onosproject/protobuf/api/GrpcServiceRegistry.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.protobuf.api;
+
+import com.google.common.annotations.Beta;
+import io.grpc.BindableService;
+
+/**
+ * A service that allows for de/registration of gRPC services, and determining
+ * whether a service is present.
+ */
+@Beta
+public interface GrpcServiceRegistry {
+    /**
+     * Register a gRPC service with this registry.
+     * @param service the service to be registered
+     * @return true if the service was added and server successfully started,
+     * false otherwise
+     */
+    boolean register(BindableService service);
+
+    /**
+     * Unregister a gRPC service with this registry.
+     * @param service the service to be unregistered
+     * @return true if the service was removed and the server successfully
+     * started, false otherwise
+     */
+    boolean unregister(BindableService service);
+
+    /**
+     * Checks if an instance of the  provided serviceClass is currently
+     * registered with this registry.
+     * @param serviceClass the class being queries
+     * @return true if an instance of this specified class has been registered,
+     * false otherwise
+     */
+    boolean containsService(Class<BindableService> serviceClass);
+}
diff --git a/incubator/protobuf/api/src/main/java/org/onosproject/protobuf/api/package-info.java b/incubator/protobuf/api/src/main/java/org/onosproject/protobuf/api/package-info.java
new file mode 100644
index 0000000..0541b49
--- /dev/null
+++ b/incubator/protobuf/api/src/main/java/org/onosproject/protobuf/api/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Abstractions for interacting with the gRPC subsystem.
+ */
+package org.onosproject.protobuf.api;
diff --git a/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/ApplicationEnumsProtoTranslator.java b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/ApplicationEnumsProtoTranslator.java
new file mode 100644
index 0000000..d82f93e
--- /dev/null
+++ b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/ApplicationEnumsProtoTranslator.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.protobuf.models.core;
+
+import org.onosproject.app.ApplicationState;
+import org.onosproject.core.ApplicationRole;
+import org.onosproject.grpc.app.models.ApplicationEnumsProto.ApplicationRoleProto;
+import org.onosproject.grpc.app.models.ApplicationEnumsProto.ApplicationStateProto;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+/**
+ * gRPC ApplicationEnumsProto message to equivalent ONOS Application Enums conversion related utilities.
+ */
+public final class ApplicationEnumsProtoTranslator {
+
+    private static final Logger log = LoggerFactory.getLogger(ApplicationEnumsProtoTranslator.class);
+
+    /**
+     * Translates {@link ApplicationRole} to gRPC ApplicationRole.
+     *
+     * @param role {@link ApplicationRole}
+     * @return gRPC message
+     */
+    public static ApplicationRoleProto translate(ApplicationRole role) {
+
+        switch (role) {
+            case USER:
+                return ApplicationRoleProto.USER;
+            case ADMIN:
+                return ApplicationRoleProto.ADMIN;
+            case UNSPECIFIED:
+                return ApplicationRoleProto.UNSPECIFIED;
+
+            default:
+                log.warn("Unexpected application role: {}", role);
+                return ApplicationRoleProto.UNSPECIFIED;
+        }
+    }
+
+    /**
+     * Translates gRPC ApplicationRole to {@link ApplicationRole}.
+     *
+     * @param roleProto gRPC message
+     * @return {@link ApplicationRole}
+     */
+    public static Optional<Object> translate(ApplicationRoleProto roleProto) {
+
+        switch (roleProto) {
+            case USER:
+                return Optional.of(ApplicationRole.USER);
+            case ADMIN:
+                return Optional.of(ApplicationRole.ADMIN);
+            case UNSPECIFIED:
+                return Optional.of(ApplicationRole.UNSPECIFIED);
+
+            default:
+                log.warn("Unexpected application role proto: {}", roleProto);
+                return Optional.empty();
+        }
+    }
+
+    /**
+     * Translate {@link ApplicationState} to gRPC ApplicationState.
+     *
+     * @param state {@link ApplicationState}
+     * @return gRPC message
+     */
+    public static ApplicationStateProto translate(ApplicationState state) {
+
+        switch (state) {
+            case ACTIVE:
+                return ApplicationStateProto.ACTIVE;
+            case INSTALLED:
+                return ApplicationStateProto.INSTALLED;
+
+            default:
+                log.warn("Unexpected application state: {}", state);
+                return ApplicationStateProto.INSTALLED;
+        }
+    }
+
+    /**
+     * Translate gRPC ApplicationState to {@link ApplicationState}.
+     *
+     * @param stateProto gRPC message
+     * @return {@link ApplicationState}
+     */
+    public static Optional<Object> translate(ApplicationStateProto stateProto) {
+
+        switch (stateProto) {
+            case ACTIVE:
+                return Optional.of(ApplicationState.ACTIVE);
+            case INSTALLED:
+                return Optional.of(ApplicationState.INSTALLED);
+
+            default:
+                log.warn("Unexpected application state proto: {}", stateProto);
+                return Optional.empty();
+        }
+    }
+
+    // Utility class not intended for instantiation.
+    private ApplicationEnumsProtoTranslator() {}
+}
diff --git a/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/ApplicationIdProtoTranslator.java b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/ApplicationIdProtoTranslator.java
new file mode 100644
index 0000000..e74ad7f
--- /dev/null
+++ b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/ApplicationIdProtoTranslator.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.protobuf.models.core;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.grpc.core.models.ApplicationIdProtoOuterClass.ApplicationIdProto;
+
+import static org.onosproject.grpc.core.models.ApplicationIdProtoOuterClass.ApplicationIdProto.getDefaultInstance;
+
+/**
+ * gRPC ApplicationIdProto message to equivalent ONOS ApplicationId conversion related utilities.
+ */
+public final class ApplicationIdProtoTranslator {
+
+    /**
+     * Translates gRPC ApplicationId to {@link ApplicationId}.
+     *
+     * @param applicationId gRPC message
+     * @return {@link ApplicationId}
+     */
+    public static ApplicationId translate(ApplicationIdProto applicationId) {
+
+        return new DefaultApplicationId(applicationId.getId(), applicationId.getName());
+    }
+
+    /**
+     * Translates {@link ApplicationId} to gRPC ApplicationId message.
+     *
+     * @param applicationId {@link ApplicationId}
+     * @return gRPC ApplicationId message
+     */
+    public static ApplicationIdProto translate(ApplicationId applicationId) {
+
+        if (applicationId != null) {
+            return ApplicationIdProto.newBuilder()
+                    .setId(applicationId.id())
+                    .setName(applicationId.name())
+                    .build();
+        }
+
+        return getDefaultInstance();
+    }
+
+    // utility class not intended for instantiation.
+    private ApplicationIdProtoTranslator() {}
+}
diff --git a/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/ApplicationProtoTranslator.java b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/ApplicationProtoTranslator.java
new file mode 100644
index 0000000..a4b2ca6
--- /dev/null
+++ b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/ApplicationProtoTranslator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.protobuf.models.core;
+
+import com.google.common.collect.Sets;
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationRole;
+import org.onosproject.core.DefaultApplication;
+import org.onosproject.grpc.core.models.ApplicationProtoOuterClass.ApplicationProto;
+import org.onosproject.incubator.protobuf.models.security.PermissionProtoTranslator;
+import org.onosproject.security.Permission;
+
+import java.util.Optional;
+import java.util.Set;
+
+import static org.onosproject.grpc.core.models.ApplicationProtoOuterClass.ApplicationProto.getDefaultInstance;
+
+/**
+ * gRPC ApplicationProto message to equivalent ONOS Application conversion related utilities.
+ */
+public final class ApplicationProtoTranslator {
+
+    /**
+     * Translates gRPC Application to {@link Application}.
+     *
+     * @param app gRPC message
+     * @return {@link Application}
+     */
+    public static Application translate(ApplicationProto app) {
+
+        Set<Permission> permissions = Sets.newHashSet();
+
+        app.getPermissionsList().forEach(p ->
+                permissions.add(PermissionProtoTranslator.translate(p)));
+
+        return new DefaultApplication(ApplicationIdProtoTranslator.translate(app.getAppId()),
+                VersionProtoTranslator.translate(app.getVersion()), app.getTitle(),
+                app.getDescription(), app.getOrigin(), app.getCategory(), app.getUrl(),
+                app.getReadme(), app.toByteArray(),
+                (ApplicationRole) ApplicationEnumsProtoTranslator.translate(app.getRole()).get(),
+                permissions, Optional.empty(), app.getFeaturesList(), app.getRequiredAppsList());
+    }
+
+    /**
+     * Translates {@link Application} to gRPC Application message.
+     *
+     * @param application {@link Application}
+     * @return gRPC message
+     */
+    public static ApplicationProto translate(Application application) {
+
+        if (application != null) {
+            return ApplicationProto.newBuilder()
+                    .setAppId(ApplicationIdProtoTranslator.translate(application.id()))
+                    .setCategory(application.category())
+                    .setDescription(application.description())
+                    .setOrigin(application.origin())
+                    .setReadme(application.readme())
+                    .setTitle(application.title())
+                    .setUrl(application.url())
+                    .setVersion(VersionProtoTranslator.translate(application.version()))
+                    .setRole(ApplicationEnumsProtoTranslator.translate(application.role()))
+                    .build();
+        }
+
+        return getDefaultInstance();
+    }
+
+    // Utility class not intended for instantiation.
+    private ApplicationProtoTranslator() {}
+}
diff --git a/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/VersionProtoTranslator.java b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/VersionProtoTranslator.java
new file mode 100644
index 0000000..6e0c243
--- /dev/null
+++ b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/VersionProtoTranslator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.protobuf.models.core;
+
+import org.onosproject.core.Version;
+import org.onosproject.grpc.core.models.VersionProtoOuterClass.VersionProto;
+
+import static org.onosproject.grpc.core.models.VersionProtoOuterClass.VersionProto.getDefaultInstance;
+
+/**
+ * gRPC Version message to equivalent ONOS Version conversion related utilities.
+ */
+public final class VersionProtoTranslator {
+
+    /**
+     * Translates {@link Version} to gRPC version message.
+     *
+     * @param version {@link Version}
+     * @return gRPC message
+     */
+    public static VersionProto translate(Version version) {
+
+        if (version != null) {
+            return VersionProto.newBuilder()
+                    .setMajor(version.major())
+                    .setMinor(version.minor())
+                    .setPatch(version.patch())
+                    .setBuild(version.build())
+                    .build();
+        }
+
+        return getDefaultInstance();
+    }
+
+    /**
+     * Translates gRPC version message to {@link Version}.
+     *
+     * @param version gRPC message
+     * @return {@link Version}
+     */
+    public static Version translate(VersionProto version) {
+
+        return Version.version(version.getMajor(), version.getMinor(),
+                version.getPatch(), version.getBuild());
+    }
+
+    // Utility class not intended for instantiation.
+    private VersionProtoTranslator() {}
+}
diff --git a/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/package-info.java b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/package-info.java
new file mode 100644
index 0000000..c1de17f
--- /dev/null
+++ b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/core/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+/**
+ * Utilities to handle ProtoBuf version of ONOS core models.
+ */
+package org.onosproject.incubator.protobuf.models.core;
\ No newline at end of file
diff --git a/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/security/PermissionProtoTranslator.java b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/security/PermissionProtoTranslator.java
new file mode 100644
index 0000000..d3cdc7d
--- /dev/null
+++ b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/security/PermissionProtoTranslator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.protobuf.models.security;
+
+import org.onosproject.grpc.security.models.PermissionProtoOuterClass.PermissionProto;
+import org.onosproject.security.Permission;
+
+import static org.onosproject.grpc.security.models.PermissionProtoOuterClass.PermissionProto.getDefaultInstance;
+
+/**
+ * gRPC Permission message to equivalent ONOS Permission conversion related utilities.
+ */
+public final class PermissionProtoTranslator {
+
+    /**
+     * Translate {@link Permission} to gRPC permission message.
+     *
+     * @param permission {@link Permission}
+     * @return gRPC message
+     */
+    public static PermissionProto translate(Permission permission) {
+
+        if (permission != null) {
+            return PermissionProto.newBuilder()
+                    .setActions(permission.getActions())
+                    .setClassname(permission.getClassName())
+                    .setName(permission.getName())
+                    .build();
+        }
+
+        return getDefaultInstance();
+    }
+
+    /**
+     * Translate gRPC permission message to {@link Permission}.
+     *
+     * @param permission gRPC message
+     * @return {@link Permission}
+     */
+    public static Permission translate(PermissionProto permission) {
+
+        return new Permission(permission.getClassname(),
+                permission.getName(), permission.getActions());
+    }
+
+    // Utility class not intended for instantiation.
+    private PermissionProtoTranslator() {}
+}
diff --git a/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/security/package-info.java b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/security/package-info.java
new file mode 100644
index 0000000..5ee50f4
--- /dev/null
+++ b/incubator/protobuf/models/src/main/java/org/onosproject/incubator/protobuf/models/security/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+/**
+ * Utilities to handle ProtoBuf version of ONOS security models.
+ */
+package org.onosproject.incubator.protobuf.models.security;
\ No newline at end of file
diff --git a/incubator/protobuf/models/src/main/proto/core/ApplicationProto.proto b/incubator/protobuf/models/src/main/proto/core/ApplicationProto.proto
index e91ea23..2618124 100644
--- a/incubator/protobuf/models/src/main/proto/core/ApplicationProto.proto
+++ b/incubator/protobuf/models/src/main/proto/core/ApplicationProto.proto
@@ -6,11 +6,12 @@
 import "core/ApplicationIdProto.proto";
 import "app/ApplicationEnumsProto.proto";
 import "security/PermissionProto.proto";
+import "core/VersionProto.proto";
 
 // Corresponds to org.onosproject.core.Application.
 message ApplicationProto {
     core.ApplicationIdProto app_id = 1;
-    string version = 2;
+    core.VersionProto version = 2;
     string title = 3;
     string description = 4;
     string category = 5;
diff --git a/incubator/protobuf/models/src/main/proto/core/VersionProto.proto b/incubator/protobuf/models/src/main/proto/core/VersionProto.proto
new file mode 100644
index 0000000..f25699b
--- /dev/null
+++ b/incubator/protobuf/models/src/main/proto/core/VersionProto.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+option java_package = "org.onosproject.grpc.core.models";
+
+package core;
+
+// Corresponds to org.onosproject.core.
+message VersionProto {
+    int32 major = 1;
+    int32 minor = 2;
+    string patch = 3;
+    string build = 4;
+}
diff --git a/incubator/protobuf/pom.xml b/incubator/protobuf/pom.xml
index 303c1ee..ece47ab 100644
--- a/incubator/protobuf/pom.xml
+++ b/incubator/protobuf/pom.xml
@@ -57,6 +57,8 @@
     </dependencies>
 
     <modules>
+        <module>api</module>
+        <module>registry</module>
         <module>models</module>
         <module>services</module>
     </modules>
diff --git a/incubator/protobuf/registry/BUCK b/incubator/protobuf/registry/BUCK
new file mode 100644
index 0000000..e6a339d
--- /dev/null
+++ b/incubator/protobuf/registry/BUCK
@@ -0,0 +1,21 @@
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//lib:NETTY',
+    '//lib:GRPC_1.3',
+    '//incubator/grpc-dependencies:grpc-core-repkg-1.3.0',
+    '//incubator/protobuf/api:onos-grpc-api'
+]
+
+osgi_jar_with_tests (
+    deps = COMPILE_DEPS,
+)
+
+onos_app (
+    title = 'Grpc Service Registry',
+    app_name = "org.onosproject.grpc.registry",
+    included_bundles = ['//incubator/grpc-dependencies:grpc-core-repkg-1.3.0',
+        '//lib:google-instrumentation-0.3.0'],
+    category = 'TODO',
+    url = 'http://onosproject.org',
+    description = 'Service providing connections for remote apps communicating with the ONOS core via gRPC.',
+)
diff --git a/incubator/protobuf/registry/pom.xml b/incubator/protobuf/registry/pom.xml
new file mode 100644
index 0000000..102a30b
--- /dev/null
+++ b/incubator/protobuf/registry/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017-present Open Networking Foundation
+  ~
+  ~ 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/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>onos-incubator-protobuf</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.12.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-incubator-protobuf-registry</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>gRPC service registry</description>
+    <url>http://onosproject.org</url>
+
+    <properties>
+        <onos.app.name>org.onosproject.incubator.grpc.registry</onos.app.name>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-grpc-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+
+        <plugins>
+
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.onosproject</groupId>
+                <artifactId>onos-maven-plugin</artifactId>
+            </plugin>
+
+        </plugins>
+    </build>
+
+</project>
diff --git a/incubator/protobuf/registry/src/main/java/org/onosproject/protobuf/registry/GrpcServiceRegistryImpl.java b/incubator/protobuf/registry/src/main/java/org/onosproject/protobuf/registry/GrpcServiceRegistryImpl.java
new file mode 100644
index 0000000..2304f0e
--- /dev/null
+++ b/incubator/protobuf/registry/src/main/java/org/onosproject/protobuf/registry/GrpcServiceRegistryImpl.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.protobuf.registry;
+
+import com.google.common.collect.Maps;
+import io.grpc.BindableService;
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+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.Service;
+import org.onosproject.protobuf.api.GrpcServiceRegistry;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import static org.onlab.util.Tools.get;
+
+/**
+ * A basic implementation of {@link GrpcServiceRegistry} designed for use with
+ * built in gRPC services.
+ *
+ * NOTE: this is an early implementation in which the addition of any new
+ * service forces a restart of the server, this is sufficient for testing but
+ * inappropriate for deployment.
+ */
+@Service
+@Component(immediate = false)
+public class GrpcServiceRegistryImpl implements GrpcServiceRegistry {
+
+    private static final int DEFAULT_SERVER_PORT = 64000;
+    private static final int DEFAULT_SHUTDOWN_TIME = 1;
+
+    private static final String PORT_PROPERTY_NAME = "listeningPort";
+
+    private final Map<Class<? extends BindableService>, BindableService> registeredServices =
+            Maps.newHashMap();
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private Server server;
+
+    /* It is currently the responsibility of the administrator to notify
+    clients of nonstandard port usage as there is no mechanism available to
+    discover the port hosting gRPC services.
+     */
+    @Property(name = PORT_PROPERTY_NAME, intValue = DEFAULT_SERVER_PORT,
+             label = "The port number which ONOS will use to host gRPC services.")
+    private int listeningPort = DEFAULT_SERVER_PORT;
+
+    @Activate
+    public void activate() {
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        attemptGracefulShutdownThenForce(DEFAULT_SHUTDOWN_TIME);
+        log.info("Stopped");
+    }
+
+    @Modified
+    public void modified(ComponentContext context) {
+        if (context != null) {
+            setProperties(context);
+        }
+        log.info("Connection was restarted to allow service to be added, " +
+                         "this is a temporary workaround");
+        restartServer(listeningPort);
+    }
+
+    @Override
+    public boolean register(BindableService service) {
+        synchronized (registeredServices) {
+            if (!registeredServices.containsKey(service.getClass())) {
+                registeredServices.put(service.getClass(), service);
+            } else {
+                log.warn("The specified class \"{}\" was not added becuase an " +
+                                 "instance of the class is already registered.",
+                         service.getClass().toString());
+                return false;
+            }
+        }
+        return restartServer(listeningPort);
+    }
+
+    @Override
+    public boolean unregister(BindableService service) {
+        synchronized (registeredServices) {
+            if (registeredServices.containsKey(service.getClass())) {
+                registeredServices.remove(service.getClass());
+            } else {
+                log.warn("The specified class \"{}\" was not removed because it " +
+                                 "was not present.", service.getClass().toString());
+                return false;
+            }
+        }
+        return restartServer(listeningPort);
+    }
+
+    @Override
+    public boolean containsService(Class<BindableService> serviceClass) {
+        return registeredServices.containsKey(serviceClass);
+    }
+
+    private void setProperties(ComponentContext context) {
+        Dictionary<String, Object> properties = context.getProperties();
+        String listeningPort = get(properties, PORT_PROPERTY_NAME);
+        this.listeningPort = listeningPort == null ? DEFAULT_SERVER_PORT :
+                Integer.parseInt(listeningPort.trim());
+    }
+
+    /**
+     * Attempts a graceful shutdown allowing {@code timeLimitSeconds} to elapse
+     * before forcing a shutdown.
+     *
+     * @param timeLimitSeconds time before a shutdown is forced in seconds
+     * @return true if the server is terminated, false otherwise
+     */
+    private boolean attemptGracefulShutdownThenForce(int timeLimitSeconds) {
+        if (!server.isShutdown()) {
+            server.shutdown();
+        }
+        try {
+            /*This is not conditional in case the server is shutdown but
+            handling requests submitted before shutdown was called.*/
+            server.awaitTermination(timeLimitSeconds, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            log.error("Awaiting server termination failed with error {}",
+                      e.getMessage());
+        }
+        if (!server.isTerminated()) {
+            server.shutdownNow();
+            try {
+                server.awaitTermination(10, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                log.error("Server failed to terminate as expected with error" +
+                                  " {}", e.getMessage());
+            }
+        }
+        return server.isTerminated();
+    }
+
+    private boolean restartServer(int port) {
+        if (!attemptGracefulShutdownThenForce(DEFAULT_SHUTDOWN_TIME)) {
+            log.error("Shutdown failed, the previous server may still be" +
+                              " active.");
+        }
+        return createServerAndStart(port);
+    }
+
+    /**
+     * Creates a server with the set of registered services on the specified
+     * port.
+     *
+     * @param port the port on which this server will listen
+     * @return true if the server was started successfully, false otherwise
+     */
+    private boolean createServerAndStart(int port) {
+
+        ServerBuilder serverBuilder =
+                ServerBuilder.forPort(port);
+        synchronized (registeredServices) {
+            registeredServices.values().forEach(
+                    service -> serverBuilder.addService(service));
+        }
+        server = serverBuilder.build();
+        try {
+            server.start();
+        } catch (IllegalStateException e) {
+            log.error("The server could not be started because an existing " +
+                              "server is already running: {}", e.getMessage());
+            return false;
+        } catch (IOException e) {
+            log.error("The server could not be started due to a failure to " +
+                              "bind: {} ", e.getMessage());
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/incubator/protobuf/registry/src/main/java/org/onosproject/protobuf/registry/package-info.java b/incubator/protobuf/registry/src/main/java/org/onosproject/protobuf/registry/package-info.java
new file mode 100644
index 0000000..818c4e8
--- /dev/null
+++ b/incubator/protobuf/registry/src/main/java/org/onosproject/protobuf/registry/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Services for interacting remotely with the gRPC subsystem.
+ */
+package org.onosproject.protobuf.registry;
diff --git a/incubator/protobuf/services/nb/BUCK b/incubator/protobuf/services/nb/BUCK
index 6f582e0..1ce40c2 100644
--- a/incubator/protobuf/services/nb/BUCK
+++ b/incubator/protobuf/services/nb/BUCK
@@ -7,7 +7,8 @@
     '//lib:protobuf-java-3.2.0',
     '//lib:GRPC_1.3',
     '//incubator/grpc-dependencies:grpc-core-repkg-1.3.0',
-    '//lib:grpc-protobuf-lite-1.3.0'
+    '//lib:grpc-protobuf-lite-1.3.0',
+    '//incubator/protobuf/api:onos-grpc-api'
 ]
 
 GRPC_DEPS = [
@@ -15,7 +16,7 @@
     '//incubator/grpc-dependencies:grpc-core-repkg-1.3.0',
     '//incubator/protobuf/models:onos-incubator-protobuf-models-proto',
     '//lib:protobuf-java-3.2.0',
-    '//lib:guava'
+    '//lib:guava',
 ]
 
 BUNDLES = [
diff --git a/incubator/protobuf/services/nb/pom.xml b/incubator/protobuf/services/nb/pom.xml
index 43be7ce..ed6eb47 100644
--- a/incubator/protobuf/services/nb/pom.xml
+++ b/incubator/protobuf/services/nb/pom.xml
@@ -42,6 +42,12 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-grpc-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
     </dependencies>
 
     <build>
@@ -76,7 +82,7 @@
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
-                        <Export-Package>org.onosproject.grpc.net,org.onosproject.incubator.protobuf.services.nb</Export-Package>
+                        <Export-Package>org.onosproject.grpc.net,org.onosproject.protobuf.services.nb</Export-Package>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/incubator/protobuf/services/nb/src/main/java/org/onosproject/grpc/nb/app/GrpcNbApplicationService.java b/incubator/protobuf/services/nb/src/main/java/org/onosproject/grpc/nb/app/GrpcNbApplicationService.java
new file mode 100644
index 0000000..4bc5a31
--- /dev/null
+++ b/incubator/protobuf/services/nb/src/main/java/org/onosproject/grpc/nb/app/GrpcNbApplicationService.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.grpc.nb.app;
+
+import com.google.common.annotations.Beta;
+import io.grpc.stub.StreamObserver;
+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.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.app.ApplicationState;
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.grpc.nb.app.ApplicationServiceGrpc.ApplicationServiceImplBase;
+import org.onosproject.grpc.nb.app.ApplicationServiceNb.getApplicationReply;
+import org.onosproject.grpc.nb.app.ApplicationServiceNb.getApplicationsReply;
+import org.onosproject.grpc.nb.app.ApplicationServiceNb.getIdReply;
+import org.onosproject.grpc.nb.app.ApplicationServiceNb.getPermissionsReply;
+import org.onosproject.grpc.nb.app.ApplicationServiceNb.getStateReply;
+import org.onosproject.incubator.protobuf.models.core.ApplicationEnumsProtoTranslator;
+import org.onosproject.incubator.protobuf.models.core.ApplicationIdProtoTranslator;
+import org.onosproject.incubator.protobuf.models.core.ApplicationProtoTranslator;
+import org.onosproject.incubator.protobuf.models.security.PermissionProtoTranslator;
+
+/**
+ * A server that provides access to the methods exposed by {@link ApplicationService}.
+ */
+
+@Beta
+@Component(immediate = true)
+public class GrpcNbApplicationService {
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ApplicationService applicationService;
+
+    @Activate
+    public void activate() {
+        //TODO this should contact the registry service and register an instance
+        // of this service.
+    }
+
+    @Deactivate
+    public void deactivate() {
+    }
+
+    private class ApplicationServiceNbServerInternal extends ApplicationServiceImplBase {
+
+        public ApplicationServiceNbServerInternal() {
+            super();
+        }
+
+        @Override
+        public void getApplications(ApplicationServiceNb.getApplicationsRequest request,
+                                    StreamObserver<getApplicationsReply> responseObserver) {
+            getApplicationsReply.Builder replyBuilder = getApplicationsReply.newBuilder();
+
+            applicationService.getApplications().forEach(a ->
+                    replyBuilder.addApplication(ApplicationProtoTranslator.translate(a)));
+            responseObserver.onNext(replyBuilder.build());
+            responseObserver.onCompleted();
+        }
+
+        @Override
+        public void getId(ApplicationServiceNb.getIdRequest request,
+                          StreamObserver<getIdReply> responseObserver) {
+            ApplicationId appId = applicationService.getId(request.getName());
+
+            responseObserver.onNext(getIdReply.newBuilder()
+                    .setApplicationId(ApplicationIdProtoTranslator.translate(appId)).build());
+            responseObserver.onCompleted();
+        }
+
+        @Override
+        public void getApplication(ApplicationServiceNb.getApplicationRequest request,
+                                   StreamObserver<getApplicationReply> responseObserver) {
+
+            Application application = applicationService.getApplication(
+                    ApplicationIdProtoTranslator.translate(request.getApplicationId()));
+
+            responseObserver.onNext(getApplicationReply.newBuilder()
+                    .setApplication(ApplicationProtoTranslator
+                            .translate(application)).build());
+            responseObserver.onCompleted();
+        }
+
+        @Override
+        public void getState(ApplicationServiceNb.getStateRequest request,
+                             StreamObserver<getStateReply> responseObserver) {
+            ApplicationState state = applicationService.getState(
+                    ApplicationIdProtoTranslator.translate(request.getApplicationId()));
+
+            responseObserver.onNext(getStateReply
+                    .newBuilder().setState(ApplicationEnumsProtoTranslator
+                            .translate(state)).build());
+            responseObserver.onCompleted();
+        }
+
+        @Override
+        public void getPermissions(ApplicationServiceNb.getPermissionsRequest request,
+                                   StreamObserver<getPermissionsReply> responseObserver) {
+            getPermissionsReply.Builder replyBuilder = getPermissionsReply.newBuilder();
+
+            applicationService.getPermissions(ApplicationIdProtoTranslator
+                    .translate(request.getApplicationId()))
+                    .forEach(p -> replyBuilder.addPermission(
+                            PermissionProtoTranslator.translate(p)));
+            responseObserver.onNext(replyBuilder.build());
+            responseObserver.onCompleted();
+        }
+    }
+}
diff --git a/incubator/protobuf/services/nb/src/main/java/org/onosproject/grpc/nb/app/package-info.java b/incubator/protobuf/services/nb/src/main/java/org/onosproject/grpc/nb/app/package-info.java
new file mode 100644
index 0000000..b5cd387
--- /dev/null
+++ b/incubator/protobuf/services/nb/src/main/java/org/onosproject/grpc/nb/app/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+/**
+ * gRPC server implementations for northbound services.
+ */
+package org.onosproject.grpc.nb.app;
\ No newline at end of file
diff --git a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbDeviceService.java b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbDeviceService.java
index df85f18..0ba9e48 100644
--- a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbDeviceService.java
+++ b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbDeviceService.java
@@ -28,6 +28,7 @@
 import org.onosproject.grpc.nb.net.device.DeviceServiceGrpc.DeviceServiceImplBase;
 import org.onosproject.grpc.net.models.PortProtoOuterClass.PortProto;
 import org.onosproject.grpc.net.device.models.DeviceEnumsProto;
+import org.onosproject.protobuf.api.GrpcServiceRegistry;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.MastershipRole;
@@ -47,26 +48,39 @@
 @Component(immediate = true)
 public class GrpcNbDeviceService {
 
+    private static DeviceServiceNbServerInternal instance = null;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected GrpcServiceRegistry registry;
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceService deviceService;
 
     @Activate
     public void activate() {
         //TODO this should contact the registry service and register an instance
-        // of this service.
+        // of this service
+        registry.register(getInnerInstance());
     }
 
     @Deactivate
     public void deactivate() {
+        registry.unregister(getInnerInstance());
     }
 
-    private class DeviceServiceNbServerInternal extends DeviceServiceImplBase {
+    public DeviceServiceNbServerInternal getInnerInstance() {
+        if (instance == null) {
+            instance = new DeviceServiceNbServerInternal();
+        }
+        return instance;
+    }
 
-        public DeviceServiceNbServerInternal() {
+    private final class DeviceServiceNbServerInternal extends DeviceServiceImplBase {
+
+        private DeviceServiceNbServerInternal() {
             super();
         }
 
-
         @Override
         public void getDeviceCount(
                 getDeviceCountRequest request,
diff --git a/incubator/protobuf/services/nb/src/main/proto/app/ApplicationServiceNb.proto b/incubator/protobuf/services/nb/src/main/proto/app/ApplicationServiceNb.proto
new file mode 100644
index 0000000..2b4515a
--- /dev/null
+++ b/incubator/protobuf/services/nb/src/main/proto/app/ApplicationServiceNb.proto
@@ -0,0 +1,56 @@
+syntax="proto3";
+option java_package = "org.onosproject.grpc.nb.app";
+
+package nb.app;
+
+import "core/ApplicationProto.proto";
+import "core/ApplicationIdProto.proto";
+import "app/ApplicationEnumsProto.proto";
+import "security/PermissionProto.proto";
+
+message getApplicationsRequest {
+}
+
+message getApplicationsReply {
+    repeated .core.ApplicationProto application = 1;
+}
+
+message getIdRequest {
+    string name = 1;
+}
+
+message getIdReply {
+    .core.ApplicationIdProto application_id = 1;
+}
+
+message getApplicationRequest {
+    .core.ApplicationIdProto application_id = 1;
+}
+
+message getApplicationReply {
+    .core.ApplicationProto application = 1;
+}
+
+message getStateRequest {
+    .core.ApplicationIdProto application_id = 1;
+}
+
+message getStateReply {
+    .app.ApplicationStateProto state = 1;
+}
+
+message getPermissionsRequest {
+    .core.ApplicationIdProto application_id = 1;
+}
+
+message getPermissionsReply {
+    repeated .security.PermissionProto permission = 1;
+}
+
+service ApplicationService {
+    rpc getApplications(getApplicationsRequest) returns (getApplicationsReply) {}
+    rpc getId(getIdRequest) returns (getIdReply) {}
+    rpc getApplication(getApplicationRequest) returns (getApplicationReply) {}
+    rpc getState(getStateRequest) returns (getStateReply) {}
+    rpc getPermissions(getPermissionsRequest) returns (getPermissionsReply) {}
+}
\ No newline at end of file
diff --git a/incubator/protobuf/services/pom.xml b/incubator/protobuf/services/pom.xml
index 9527113..df55080 100644
--- a/incubator/protobuf/services/pom.xml
+++ b/incubator/protobuf/services/pom.xml
@@ -31,17 +31,6 @@
     </modules>
 
     <dependencies>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-incubator-protobuf-models</artifactId>
-            <version>1.12.0-SNAPSHOT</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-incubator-api</artifactId>
-            <version>1.12.0-SNAPSHOT</version>
-        </dependency>
 
         <dependency>
             <groupId>org.onosproject</groupId>
@@ -74,7 +63,4 @@
 
     </dependencies>
 
-    <description>ONOS ProtoBuf service models and implementations</description>
-    <url>http://onosproject.org</url>
-
 </project>
diff --git a/lib/BUCK b/lib/BUCK
index 6db1b6b..ad18962 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -1,4 +1,4 @@
-# ***** This file was auto-generated at Tue, 29 Aug 2017 04:00:21 GMT. Do not edit this file manually. *****
+# ***** This file was auto-generated at Wed, 30 Aug 2017 00:20:31 GMT. Do not edit this file manually. *****
 # ***** Use onos-lib-gen *****
 
 pass_thru_pom(
diff --git a/modules.defs b/modules.defs
index 40524a6..7e3ba3f 100644
--- a/modules.defs
+++ b/modules.defs
@@ -217,6 +217,7 @@
     '//apps/evpnopenflow:onos-apps-evpnopenflow-oar',
     '//apps/route-service:onos-apps-route-service-oar',
     '//apps/evpn-route-service:onos-apps-evpn-route-service-oar',
+    '//incubator/protobuf/registry:onos-incubator-protobuf-registry-oar',
 ]
 
 PROTOCOL_APPS = [
diff --git a/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java b/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java
index fb1e7ee..91b9db0 100644
--- a/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java
+++ b/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java
@@ -16,7 +16,9 @@
 
 package org.onosproject.grpc.ctl;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import io.grpc.CallOptions;
 import io.grpc.Channel;
 import io.grpc.ClientCall;
@@ -51,6 +53,10 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Default implementation of the GrpcController.
@@ -71,6 +77,7 @@
     private Map<GrpcStreamObserverId, GrpcObserverHandler> observers;
     private Map<GrpcChannelId, ManagedChannel> channels;
     private Map<GrpcChannelId, ManagedChannelBuilder<?>> channelBuilders;
+    private final Map<GrpcChannelId, Lock> channelLocks = Maps.newConcurrentMap();
 
     @Activate
     public void activate() {
@@ -109,20 +116,26 @@
     @Override
     public ManagedChannel connectChannel(GrpcChannelId channelId, ManagedChannelBuilder<?> channelBuilder)
             throws IOException {
+        checkNotNull(channelId);
+        checkNotNull(channelBuilder);
 
-        if (enableMessageLog) {
-            channelBuilder.intercept(new InternalLogChannelInterceptor(channelId));
+        Lock lock = channelLocks.computeIfAbsent(channelId, k -> new ReentrantLock());
+        lock.lock();
+
+        try {
+            if (enableMessageLog) {
+                channelBuilder.intercept(new InternalLogChannelInterceptor(channelId));
+            }
+            ManagedChannel channel = channelBuilder.build();
+            // Forced connection not yet implemented. Use workaround...
+            // channel.getState(true);
+            doDummyMessage(channel);
+            channelBuilders.put(channelId, channelBuilder);
+            channels.put(channelId, channel);
+            return channel;
+        } finally {
+            lock.unlock();
         }
-
-        ManagedChannel channel = channelBuilder.build();
-
-        // Forced connection not yet implemented. Use workaround...
-        // channel.getState(true);
-        doDummyMessage(channel);
-
-        channelBuilders.put(channelId, channelBuilder);
-        channels.put(channelId, channel);
-        return channel;
     }
 
     private void doDummyMessage(ManagedChannel channel) throws IOException {
@@ -141,45 +154,63 @@
 
     @Override
     public boolean isChannelOpen(GrpcChannelId channelId) {
-        if (!channels.containsKey(channelId)) {
-            log.warn("Can't check if channel open for unknown channel id {}", channelId);
-            return false;
-        }
+        checkNotNull(channelId);
+
+        Lock lock = channelLocks.computeIfAbsent(channelId, k -> new ReentrantLock());
+        lock.lock();
 
         try {
-            doDummyMessage(channels.get(channelId));
-            return true;
-        } catch (IOException e) {
-            return false;
+            if (!channels.containsKey(channelId)) {
+                log.warn("Can't check if channel open for unknown channel id {}", channelId);
+                return false;
+            }
+            try {
+                doDummyMessage(channels.get(channelId));
+                return true;
+            } catch (IOException e) {
+                return false;
+            }
+        } finally {
+            lock.unlock();
         }
     }
 
     @Override
     public void disconnectChannel(GrpcChannelId channelId) {
-        if (!channels.containsKey(channelId)) {
-            // Nothing to do.
-            return;
-        }
-        ManagedChannel channel = channels.get(channelId);
+        checkNotNull(channelId);
+
+        Lock lock = channelLocks.computeIfAbsent(channelId, k -> new ReentrantLock());
+        lock.lock();
 
         try {
-            channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
-        } catch (InterruptedException e) {
-            log.warn("Channel {} didn't shut down in time.");
-            channel.shutdownNow();
-        }
+            if (!channels.containsKey(channelId)) {
+                // Nothing to do.
+                return;
+            }
+            ManagedChannel channel = channels.get(channelId);
 
-        channels.remove(channelId);
-        channelBuilders.remove(channelId);
+            try {
+                channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                log.warn("Channel {} didn't shut down in time.");
+                channel.shutdownNow();
+            }
+
+            channels.remove(channelId);
+            channelBuilders.remove(channelId);
+        } finally {
+            lock.unlock();
+        }
     }
 
     @Override
     public Map<GrpcChannelId, ManagedChannel> getChannels() {
-        return channels;
+        return ImmutableMap.copyOf(channels);
     }
 
     @Override
     public Collection<ManagedChannel> getChannels(final DeviceId deviceId) {
+        checkNotNull(deviceId);
         final Set<ManagedChannel> deviceChannels = new HashSet<>();
         channels.forEach((k, v) -> {
             if (k.deviceId().equals(deviceId)) {
@@ -192,7 +223,16 @@
 
     @Override
     public Optional<ManagedChannel> getChannel(GrpcChannelId channelId) {
-        return Optional.ofNullable(channels.get(channelId));
+        checkNotNull(channelId);
+
+        Lock lock = channelLocks.computeIfAbsent(channelId, k -> new ReentrantLock());
+        lock.lock();
+
+        try {
+            return Optional.ofNullable(channels.get(channelId));
+        } finally {
+            lock.unlock();
+        }
     }
 
     /**
diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeController.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeController.java
index 6101f8b..b095bca 100644
--- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeController.java
+++ b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeController.java
@@ -39,7 +39,7 @@
      * @return true if the client was created and the channel to the device is open
      * @throws IllegalStateException if a client already exists for the given device identifier
      */
-    boolean createClient(DeviceId deviceId, int p4DeviceId, ManagedChannelBuilder channelBuilder);
+    boolean createClient(DeviceId deviceId, long p4DeviceId, ManagedChannelBuilder channelBuilder);
 
     /**
      * Returns a client to operate on the given device.
@@ -69,7 +69,7 @@
     /**
      * Returns true if the P4Runtime server running on the given device is reachable, i.e. the channel is open and the
      * server is able to respond to RPCs, false otherwise. Reachability can be tested only if a client was previously
-     * created using {@link #createClient(DeviceId, int, ManagedChannelBuilder)}, otherwise this method returns false.
+     * created using {@link #createClient(DeviceId, long, ManagedChannelBuilder)}, otherwise this method returns false.
      *
      * @param deviceId device identifier.
      * @return true if a client was created and is able to contact the P4Runtime server, false otherwise.
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
index 3a4a1b5..26ec75a 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
@@ -94,7 +94,7 @@
     private final Logger log = getLogger(getClass());
 
     private final DeviceId deviceId;
-    private final int p4DeviceId;
+    private final long p4DeviceId;
     private final P4RuntimeControllerImpl controller;
     private final P4RuntimeGrpc.P4RuntimeBlockingStub blockingStub;
     private final Context.CancellableContext cancellableContext;
@@ -104,7 +104,8 @@
     private final StreamObserver<StreamMessageRequest> streamRequestObserver;
 
 
-    P4RuntimeClientImpl(DeviceId deviceId, int p4DeviceId, ManagedChannel channel, P4RuntimeControllerImpl controller) {
+    P4RuntimeClientImpl(DeviceId deviceId, long p4DeviceId, ManagedChannel channel,
+                        P4RuntimeControllerImpl controller) {
         this.deviceId = deviceId;
         this.p4DeviceId = p4DeviceId;
         this.controller = controller;
@@ -391,7 +392,7 @@
      *
      * @return P4 device ID
      */
-    public int p4DeviceId() {
+    public long p4DeviceId() {
         return p4DeviceId;
     }
 
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeControllerImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeControllerImpl.java
index 33930bb..e26341b 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeControllerImpl.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeControllerImpl.java
@@ -81,7 +81,7 @@
 
 
     @Override
-    public boolean createClient(DeviceId deviceId, int p4DeviceId, ManagedChannelBuilder channelBuilder) {
+    public boolean createClient(DeviceId deviceId, long p4DeviceId, ManagedChannelBuilder channelBuilder) {
         checkNotNull(deviceId);
         checkNotNull(channelBuilder);
 
@@ -101,7 +101,7 @@
         }
     }
 
-    private boolean doCreateClient(DeviceId deviceId, int p4DeviceId, ManagedChannelBuilder channelBuilder) {
+    private boolean doCreateClient(DeviceId deviceId, long p4DeviceId, ManagedChannelBuilder channelBuilder) {
 
         GrpcChannelId channelId = GrpcChannelId.of(deviceId, "p4runtime");
 
diff --git a/tools/build/check-uploaded-maven-artifacts b/tools/build/check-uploaded-maven-artifacts
index 7b8f003..05819f1 100755
--- a/tools/build/check-uploaded-maven-artifacts
+++ b/tools/build/check-uploaded-maven-artifacts
@@ -49,7 +49,8 @@
         sha1 = repoResponse.headers['x-checksum-sha1']
 
     if sha1 != expectedSha1:
-        print 'SHA1 hash is wrong expected ' + expectedSha1 + ' but found ' + sha1
+        print 'SHA1 hash is wrong for ' + remoteUrl + ' expected ' + \
+              expectedSha1 + ' but found ' + sha1
         sys.exit(1)
 
 def checkArtifactsForComponent(version, name, component, buildRoot, repoRoot):
diff --git a/tools/dev/bin/patch-yang-libs b/tools/dev/bin/patch-yang-libs
index d5a6161..89ebeb6 100755
--- a/tools/dev/bin/patch-yang-libs
+++ b/tools/dev/bin/patch-yang-libs
@@ -3,7 +3,7 @@
 # Patches lib/BUCK file to use locally built YANG tools.
 # -----------------------------------------------------------------------------
 
-BVER=2.2.0-b3
+BVER=2.2.0-b6
 SVER=2.2-SNAPSHOT
 
 YANG_TOOLS_ROOT=${YANG_TOOLS_ROOT:-~/onos-yang-tools}
diff --git a/tools/test/p4src/p4-16/default.p4 b/tools/test/p4src/p4-16/default.p4
index c76cbf7..93683a5 100644
--- a/tools/test/p4src/p4-16/default.p4
+++ b/tools/test/p4src/p4-16/default.p4
@@ -19,9 +19,13 @@
 #include "include/defines.p4"
 #include "include/headers.p4"
 
+struct ecmp_metadata_t {
+    ecmp_group_id_t ecmp_group_id;
+}
+
 struct metadata_t {
     intrinsic_metadata_t intrinsic_metadata;
-    ecmp_group_id_t ecmp_group_id;
+    ecmp_metadata_t ecmp_metadata;
 }
 
 #include "include/parsers.p4"
@@ -35,7 +39,7 @@
     direct_counter(CounterType.packets) ecmp_counter;
 
     action do_ecmp(ecmp_group_id_t ecmp_group_id) {
-        meta.ecmp_group_id = ecmp_group_id;
+        meta.ecmp_metadata.ecmp_group_id = ecmp_group_id;
     }
 
     table table0 {
@@ -64,7 +68,7 @@
     table ecmp {
         support_timeout = false;
         key = {
-            meta.ecmp_group_id             : exact;
+            meta.ecmp_metadata.ecmp_group_id             : exact;
             // Not for matching.
             // Inputs to the hash function of the action selector.
             hdr.ipv4.srcAddr               : selector;
diff --git a/tools/test/p4src/p4-16/p4c-out/default.json b/tools/test/p4src/p4-16/p4c-out/default.json
index 33af4c7..62e397a 100644
--- a/tools/test/p4src/p4-16/p4c-out/default.json
+++ b/tools/test/p4src/p4-16/p4c-out/default.json
@@ -10,8 +10,7 @@
       "id" : 0,
       "fields" : [
         ["tmp", 32, false],
-        ["tmp_0", 32, false],
-        ["metadata_t.ecmp_group_id", 8, false]
+        ["tmp_0", 32, false]
       ]
     },
     {
@@ -117,6 +116,13 @@
         ["mcast_grp", 16, false],
         ["egress_rid", 16, false]
       ]
+    },
+    {
+      "name" : "ecmp_metadata_t",
+      "id" : 9,
+      "fields" : [
+        ["ecmp_group_id", 8, false]
+      ]
     }
   ],
   "headers" : [
@@ -210,6 +216,13 @@
       "header_type" : "intrinsic_metadata_t",
       "metadata" : true,
       "pi_omit" : true
+    },
+    {
+      "name" : "ecmp_metadata",
+      "id" : 13,
+      "header_type" : "ecmp_metadata_t",
+      "metadata" : true,
+      "pi_omit" : true
     }
   ],
   "header_stacks" : [],
@@ -218,12 +231,12 @@
   "header_union_stacks" : [],
   "field_lists" : [],
   "errors" : [
-    ["NoError", 1],
-    ["PacketTooShort", 2],
-    ["NoMatch", 3],
-    ["StackOutOfBounds", 4],
-    ["HeaderTooShort", 5],
-    ["ParserTimeout", 6]
+    ["NoError", 0],
+    ["PacketTooShort", 1],
+    ["NoMatch", 2],
+    ["StackOutOfBounds", 3],
+    ["HeaderTooShort", 4],
+    ["ParserTimeout", 5]
   ],
   "enums" : [],
   "parsers" : [
@@ -3173,7 +3186,7 @@
           "parameters" : [
             {
               "type" : "field",
-              "value" : ["scalars", "metadata_t.ecmp_group_id"]
+              "value" : ["ecmp_metadata", "ecmp_group_id"]
             },
             {
               "type" : "runtime_data",
@@ -3182,9 +3195,9 @@
           ],
           "source_info" : {
             "filename" : "default.p4",
-            "line" : 38,
+            "line" : 42,
             "column" : 8,
-            "source_fragment" : "meta.ecmp_group_id = ecmp_group_id"
+            "source_fragment" : "meta.ecmp_metadata.ecmp_group_id = ecmp_group_id"
           }
         }
       ]
@@ -3382,7 +3395,7 @@
       "id" : 0,
       "source_info" : {
         "filename" : "default.p4",
-        "line" : 33,
+        "line" : 37,
         "column" : 8,
         "source_fragment" : "ingress"
       },
@@ -3416,7 +3429,7 @@
           "id" : 1,
           "source_info" : {
             "filename" : "default.p4",
-            "line" : 41,
+            "line" : 45,
             "column" : 10,
             "source_fragment" : "table0"
           },
@@ -3468,14 +3481,14 @@
           "id" : 2,
           "source_info" : {
             "filename" : "default.p4",
-            "line" : 64,
+            "line" : 68,
             "column" : 10,
             "source_fragment" : "ecmp"
           },
           "key" : [
             {
               "match_type" : "exact",
-              "target" : ["scalars", "metadata_t.ecmp_group_id"],
+              "target" : ["ecmp_metadata", "ecmp_group_id"],
               "mask" : null
             }
           ],
@@ -3589,7 +3602,7 @@
           "id" : 1,
           "source_info" : {
             "filename" : "default.p4",
-            "line" : 90,
+            "line" : 94,
             "column" : 13,
             "source_fragment" : "hdr.packet_out.isValid()"
           },
@@ -3643,7 +3656,7 @@
       "id" : 1,
       "source_info" : {
         "filename" : "default.p4",
-        "line" : 102,
+        "line" : 106,
         "column" : 8,
         "source_fragment" : "egress"
       },
diff --git a/tools/test/p4src/p4-16/p4c-out/default.p4info b/tools/test/p4src/p4-16/p4c-out/default.p4info
index a485e17..6bf4a27 100644
--- a/tools/test/p4src/p4-16/p4c-out/default.p4info
+++ b/tools/test/p4src/p4-16/p4c-out/default.p4info
@@ -51,7 +51,7 @@
   }
   match_fields {
     id: 1
-    name: "meta.ecmp_group_id"
+    name: "meta.ecmp_metadata.ecmp_group_id"
     bitwidth: 8
     match_type: EXACT
   }
diff --git a/tools/test/scenarios/app-reactivate.xml b/tools/test/scenarios/app-reactivate.xml
new file mode 100644
index 0000000..865f218
--- /dev/null
+++ b/tools/test/scenarios/app-reactivate.xml
@@ -0,0 +1,46 @@
+<!--
+  ~ Copyright 2017-present Open Networking Foundation
+  ~
+  ~ 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.
+  -->
+<scenario name="app-reactivate" description="Application re-activation test">
+    <group name="App-Reactivation">
+        <step name="App-Activate"
+              exec="onos ${OCI} app activate org.onosproject.pathpainter"/>
+        <group name="App-Component-Check" requires="App-Activate">
+            <parallel var="${OC#}">
+                <step name="App-Component-Check-${#}"
+                      exec="onos-check-component ${OC#} org.onosproject.pathpainter.PathPainter ACTIVE"/>
+            </parallel>
+        </group>
+
+        <step name="App-Deactivate"
+              exec="onos ${OCI} app deactivate org.onosproject.pathpainter"
+              requires="App-Component-Check"/>
+
+        <step name="App-Reactivate"
+              exec="onos ${OCI} app activate org.onosproject.pathpainter"
+              requires="^"/>
+
+        <group name="App-Component-Check-Again" requires="App-Reactivate">
+            <parallel var="${OC#}">
+                <step name="App-Component-Check-Again-${#}"
+                      exec="onos-check-component ${OC#} org.onosproject.pathpainter.PathPainter ACTIVE"/>
+            </parallel>
+        </group>
+
+        <step name="App-Deactivate-Again"
+              exec="onos ${OCI} app deactivate org.onosproject.pathpainter"
+              requires="App-Component-Check-Again"/>
+    </group>
+</scenario>
diff --git a/tools/test/scenarios/smoke.xml b/tools/test/scenarios/smoke.xml
index c08e8a4..4661a61 100644
--- a/tools/test/scenarios/smoke.xml
+++ b/tools/test/scenarios/smoke.xml
@@ -17,6 +17,7 @@
     <import file="${ONOS_SCENARIOS}/prerequisites.xml"/>
     <import file="${ONOS_SCENARIOS}/net-prerequisites.xml"/>
     <import file="${ONOS_SCENARIOS}/setup.xml"/>
+    <import file="${ONOS_SCENARIOS}/app-reactivate.xml"/>
     <import file="${ONOS_SCENARIOS}/maps-cli.xml"/>
     <import file="${ONOS_SCENARIOS}/dist-test.xml"/>
     <import file="${ONOS_SCENARIOS}/smoke-internal.xml"/>
@@ -25,6 +26,7 @@
 
     <dependency name="Setup" requires="Prerequisites,Net-Prerequisites"/>
     <dependency name="Net-Smoke" requires="Setup"/>
+    <dependency name="App-Reactivation" requires="Setup"/>
     <dependency name="Maps-Cli" requires="Setup"/>
     <dependency name="Drivers-Test" requires="Setup"/>
     <dependency name="Meta-App" requires="Setup"/>
@@ -34,5 +36,5 @@
     <dependency name="YANG-Live-Compile" requires="Setup,~YANG-Smoke"/>
 
     <import file="${ONOS_SCENARIOS}/wrapup.xml"/>
-    <dependency name="Wrapup" requires="~Setup,~Net-Smoke,~Drivers-Test,~Meta-App,~Netcfg,~YANG-Smoke,~YANG-Live-Compile"/>
+    <dependency name="Wrapup" requires="~Setup,~Net-Smoke,~App-Reactivation,~Drivers-Test,~Meta-App,~Netcfg,~YANG-Smoke,~YANG-Live-Compile"/>
 </scenario>
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Action_es.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Action_es.properties
index cd1d275..84d971c 100644
--- a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Action_es.properties
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Action_es.properties
@@ -24,14 +24,14 @@
 uninstall=Desinstalar
 activate=Activar
 deactivate=Desactivar
-show=Show (es)
-hide=Hide (es)
-enable=Enable (es)
-disable=Disable (es)
-select=Select (es)
+show=Mostrar
+hide=Ocultar
+enable=Activar
+disable=Desactivar
+select=Seleccionar
 
 # Past Tense
-purged=purged (es)
-withdrawn=withdrawn (es)
-resubmitted=resubmitted (es)
-added=added (es)
+purged=eliminado
+withdrawn=retirado
+resubmitted=reenviado
+added=añadido
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Network_es.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Network_es.properties
index 3a3f6df..98c9787 100644
--- a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Network_es.properties
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Network_es.properties
@@ -22,24 +22,24 @@
 device=Dispositivo
 host=Host
 link=Enlace
-intent=Intent (es)
-tunnel=Tunnel (es)
-flow=Flow (es)
-port=Port (es)
+intent=Intent
+tunnel=Túnel
+flow=Flujo
+port=Puerto
 
 # --- Elements (Plural)
 nodes=Nodos
 topologies=Topologías
-topology_sccs=Topology SCCs (es)
+topology_sccs=SCCs de la topología
 networks=Redes
 regions=Regiones
 devices=Dispositivos
 hosts=Hosts
 links=Enlaces
-intents=Intents (es)
-tunnels=Tunnels (es)
-flows=Flows (es)
-ports=Ports (es)
+intents=Intents
+tunnels=Túneles
+flows=Flujos
+ports=Puertos
 
 # --- Element IDs
 node_id=ID del Nodo
@@ -47,10 +47,10 @@
 device_id=ID del Dispositivo
 host_id=ID del Host
 link_id=ID del Enlace
-intent_id=Intent ID (es)
-tunnel_id=Tunnel ID (es)
-flow_id=Flow ID (es)
-port_id=Port ID (es)
+intent_id=ID del Intent
+tunnel_id=ID del Túnel
+flow_id=ID del Flujo
+port_id=ID del Puerto
 
 # --- Protocol terms
 protocol=Protocolo
@@ -60,4 +60,4 @@
 mac=MAC
 mac_address=Dirección MAC
 uri=URI
-vlan=VLAN (es)
+vlan=VLAN 
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Props_es.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Props_es.properties
index 5d026b9..44732d0 100644
--- a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Props_es.properties
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Props_es.properties
@@ -17,7 +17,7 @@
 
 # Typically used as table column headers or property labels
 
-chassis_id=Chassis ID
+chassis_id=Bastidor
 hw_version=Versión Hardware
 sw_version=Versión Software
 serial_number=Número de Serie
@@ -33,7 +33,7 @@
 origin=Origen
 role=Rol
 
-latitude=Latitude (es)
-longitude=Longitude (es)
-grid_y=Grid Y (es)
-grid_x=Grid X (es)
+latitude=Latitud
+longitude=Longitud
+grid_y=Eje Y
+grid_x=Eje X
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/State_es.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/State_es.properties
index 55271fb..c0fe6c9 100644
--- a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/State_es.properties
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/State_es.properties
@@ -23,12 +23,12 @@
 last_updated=Última Actualización
 last_modified=Última Modificación
 
-visible=Visible (es)
-hidden=Hidden (es)
+visible=Visible
+hidden=Oculto
 
 # VLAN id == NONE (-1)
-vlan_none=None (es)
+vlan_none=Ninguna
 
 # lower case values, please
-expected=expected (es)
-not_expected=not expected (es)
+expected=esperado
+not_expected=no esperado
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Ui_es.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Ui_es.properties
index 141bb38..e20739e 100644
--- a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Ui_es.properties
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Ui_es.properties
@@ -18,14 +18,14 @@
 # Common button text
 ok=OK
 cancel=Cancelar
-close=Close (es)
+close=Cerrar
 
 # Gesture text
 click=click
-click_row=click row
-scroll_down=Desplazar hacia abajo
+click_row=click row (es)
+scroll_down=desplazar hacia abajo
 shift_click=shift-click (es)
-drag=drag (es)
+drag=arrastrar
 cmd_scroll=cmd-scroll (es)
 cmd_drag=cmd-drag (es)
 
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/DeviceEnums_es.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/DeviceEnums_es.properties
new file mode 100644
index 0000000..a5e81c4
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/DeviceEnums_es.properties
@@ -0,0 +1,34 @@
+#
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+#
+
+# display names for Device.Type constants
+switch=switch
+router=router
+roadm=roadm
+otn=otn
+roadm_otn=roadm otn
+firewall=firewall
+balancer=balanceador
+ips=ips
+ids=ids
+controller=controlador
+virtual=virtual
+fiber_switch=switch de fibra
+microwave=microonda
+olt=olt
+onu=onu
+optical_amplifier=amplificador óptico
+other=otro
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/LinkEnums_es.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/LinkEnums_es.properties
new file mode 100644
index 0000000..e6b91b6
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/LinkEnums_es.properties
@@ -0,0 +1,27 @@
+#
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+#
+
+# display names for Link.Type constants
+direct=directo
+indirect=indirecto
+edge=edge (es)
+tunnel=túnel
+optical=óptico
+virtual=virtual
+
+# display names for Link.State constants
+active=activo
+inactive=inactivo
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/App_es.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/App_es.properties
new file mode 100644
index 0000000..cadedf4
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/App_es.properties
@@ -0,0 +1,45 @@
+#
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+#
+#
+
+# Text that appears in the navigation panel
+nav_item_app=Aplicaciones
+
+# View title
+title_apps=Aplicaciones
+
+# Control button tooltips
+tt_ctl_upload=Cargar una aplicación (fichero .oar)
+tt_ctl_activate=Activar aplicación seleccionada
+tt_ctl_deactivate=Desactivar aplicación seleccionada
+tt_ctl_uninstall=Desinstalar aplicación seleccionada
+
+# Quick-Help panel
+qh_hint_esc=Deseleccionar aplicación
+qh_hint_click_row=Seleccionar / deseleccionar aplicación
+qh_hint_scroll_down=Ver más aplicaciones
+
+# App details panel
+dp_features=Características
+dp_required_apps=Aplicaciones requeridas
+dp_permissions=Permisos
+
+# App dialog panel
+dlg_confirm_action=Confirmar Acción
+
+dlg_warn_deactivate=¡Desactivar o desinstalar este componente \
+  puede tener graves consecuencias!
+dlg_warn_own_risk=** HÁGALO BAJO SU PROPIA RESPONSABILIDAD **