[ONOS-4513] decouple optical-model from CLI

- core/cli is now optical-model clean
- Add command for pretty printing optical-ports

Change-Id: I2926ef6d52971d1a7f6642dfe4098d7ed2422363
diff --git a/apps/optical-model/BUCK b/apps/optical-model/BUCK
index e311eb3..e78c286 100644
--- a/apps/optical-model/BUCK
+++ b/apps/optical-model/BUCK
@@ -1,6 +1,8 @@
 COMPILE_DEPS = [
     '//lib:CORE_DEPS',
     '//incubator/api:onos-incubator-api',
+    '//cli:onos-cli',
+    '//lib:org.apache.karaf.shell.console',
 ]
 
 TEST_DEPS = [
diff --git a/apps/optical-model/pom.xml b/apps/optical-model/pom.xml
index c5bb627..d8d50d8 100644
--- a/apps/optical-model/pom.xml
+++ b/apps/optical-model/pom.xml
@@ -59,6 +59,17 @@
 
         <dependency>
             <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
             <artifactId>onlab-junit</artifactId>
             <scope>test</scope>
         </dependency>
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/OchPort.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/OchPort.java
index 8d19889..051bbed 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/OchPort.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/OchPort.java
@@ -17,8 +17,6 @@
 
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.OduSignalType;
-import org.onosproject.net.Port;
-
 import com.google.common.annotations.Beta;
 
 /**
@@ -27,7 +25,7 @@
  * See ITU G.709 "Interfaces for the Optical Transport Network (OTN)"
  */
 @Beta
-public interface OchPort extends Port {
+public interface OchPort extends ProjectedPort {
 
     /**
      * Returns ODU signal type.
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/OduCltPort.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/OduCltPort.java
index b4342f4..5dc6dfc 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/OduCltPort.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/OduCltPort.java
@@ -16,8 +16,6 @@
 package org.onosproject.net.optical;
 
 import org.onosproject.net.CltSignalType;
-import org.onosproject.net.Port;
-
 import com.google.common.annotations.Beta;
 
 /**
@@ -26,7 +24,7 @@
  * See ITU G.709 "Interfaces for the Optical Transport Network (OTN)"
  */
 @Beta
-public interface OduCltPort extends Port {
+public interface OduCltPort extends ProjectedPort {
 
     /**
      * Returns ODU client signal type.
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/OmsPort.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/OmsPort.java
index 898fce9..f553c71 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/OmsPort.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/OmsPort.java
@@ -16,8 +16,6 @@
 package org.onosproject.net.optical;
 
 import org.onlab.util.Frequency;
-import org.onosproject.net.Port;
-
 import com.google.common.annotations.Beta;
 
 /**
@@ -28,7 +26,7 @@
  * Assumes we only support fixed grid for now.
  */
 @Beta
-public interface OmsPort extends Port {
+public interface OmsPort extends ProjectedPort {
 
     /**
      * Returns the total number of channels on the port.
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/OtuPort.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/OtuPort.java
index 15f284d..bf37acf 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/OtuPort.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/OtuPort.java
@@ -16,8 +16,6 @@
 package org.onosproject.net.optical;
 
 import org.onosproject.net.OtuSignalType;
-import org.onosproject.net.Port;
-
 import com.google.common.annotations.Beta;
 
 /**
@@ -27,7 +25,7 @@
  * Open Networking Foundation "Optical Transport Protocol Extensions Version 1.0".
  */
 @Beta
-public interface OtuPort extends Port {
+public interface OtuPort extends ProjectedPort {
 
     /**
      * Returns OTU signal type.
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/ProjectedPort.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/ProjectedPort.java
new file mode 100644
index 0000000..a366229
--- /dev/null
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/ProjectedPort.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-present 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.net.optical;
+
+import org.onosproject.net.Annotations;
+import org.onosproject.net.Port;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Abstraction of a network port.
+ */
+@Beta
+public interface ProjectedPort extends Port {
+
+    /**
+     * Returns the key/value annotations, not used by projection.
+     *
+     * @return key/value annotations
+     */
+    Annotations unhandledAnnotations();
+
+}
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java
new file mode 100644
index 0000000..382c6e6
--- /dev/null
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2014-present 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.net.optical.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.app.AllApplicationNamesCompleter;
+import org.onosproject.cli.net.ConnectPointCompleter;
+import org.onosproject.cli.net.ConnectivityIntentCommand;
+import org.onosproject.net.CltSignalType;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.OduSignalType;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.OpticalCircuitIntent;
+import org.onosproject.net.intent.OpticalConnectivityIntent;
+import org.onosproject.net.intent.OpticalOduIntent;
+import org.onosproject.net.optical.OchPort;
+import org.onosproject.net.optical.OduCltPort;
+
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
+
+/**
+ * Installs optical connectivity or circuit intents, depending on given port types.
+ */
+@Command(scope = "onos", name = "add-optical-intent",
+         description = "Installs optical connectivity intent")
+public class AddOpticalIntentCommand extends ConnectivityIntentCommand {
+
+    // OSGi workaround
+    @SuppressWarnings("unused")
+    private ConnectPointCompleter cpCompleter;
+
+    // OSGi workaround
+    @SuppressWarnings("unused")
+    private AllApplicationNamesCompleter appCompleter;
+
+    @Argument(index = 0, name = "ingressDevice",
+              description = "Ingress Device/Port Description",
+              required = true, multiValued = false)
+    String ingressDeviceString = "";
+
+    @Argument(index = 1, name = "egressDevice",
+              description = "Egress Device/Port Description",
+              required = true, multiValued = false)
+    String egressDeviceString = "";
+
+    @Option(name = "-b", aliases = "--bidirectional",
+            description = "If this argument is passed the optical link created will be bidirectional, " +
+            "else the link will be unidirectional.",
+            required = false, multiValued = false)
+    private boolean bidirectional = false;
+
+
+    private ConnectPoint createConnectPoint(String devicePortString) {
+        String[] splitted = devicePortString.split("/");
+
+        checkArgument(splitted.length == 2,
+                "Connect point must be in \"deviceUri/portNumber\" format");
+
+        DeviceId deviceId = DeviceId.deviceId(splitted[0]);
+        DeviceService deviceService = get(DeviceService.class);
+
+        List<Port> ports = deviceService.getPorts(deviceId);
+
+        for (Port port : ports) {
+            if (splitted[1].equals(port.number().name())) {
+                return new ConnectPoint(deviceId, port.number());
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    protected void execute() {
+        IntentService service = get(IntentService.class);
+
+        ConnectPoint ingress = createConnectPoint(ingressDeviceString);
+        ConnectPoint egress = createConnectPoint(egressDeviceString);
+
+        if (ingress == null || egress == null) {
+            print("Invalid endpoint(s); could not create optical intent");
+            return;
+        }
+
+        DeviceService deviceService = opticalView(get(DeviceService.class));
+
+        Port srcPort = deviceService.getPort(ingress.deviceId(), ingress.port());
+        Port dstPort = deviceService.getPort(egress.deviceId(), egress.port());
+
+        Intent intent;
+
+        if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
+            Device srcDevice = deviceService.getDevice(ingress.deviceId());
+            Device dstDevice = deviceService.getDevice(egress.deviceId());
+
+            // continue only if both OduClt port's Devices are of the same type
+            if (!(srcDevice.type().equals(dstDevice.type()))) {
+                print("Devices without same deviceType: SRC=%s and DST=%s", srcDevice.type(), dstDevice.type());
+                return;
+            }
+
+            CltSignalType signalType = ((OduCltPort) srcPort).signalType();
+            if (Device.Type.ROADM.equals(srcDevice.type())) {
+                intent = OpticalCircuitIntent.builder()
+                        .appId(appId())
+                        .key(key())
+                        .src(ingress)
+                        .dst(egress)
+                        .signalType(signalType)
+                        .bidirectional(bidirectional)
+                        .build();
+            } else if (Device.Type.OTN.equals(srcDevice.type())) {
+                intent = OpticalOduIntent.builder()
+                        .appId(appId())
+                        .key(key())
+                        .src(ingress)
+                        .dst(egress)
+                        .signalType(signalType)
+                        .bidirectional(bidirectional)
+                        .build();
+            } else {
+                print("Wrong Device Type for connect points %s and %s", ingress, egress);
+                return;
+            }
+        } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
+            OduSignalType signalType = ((OchPort) srcPort).signalType();
+            intent = OpticalConnectivityIntent.builder()
+                    .appId(appId())
+                    .key(key())
+                    .src(ingress)
+                    .dst(egress)
+                    .signalType(signalType)
+                    .bidirectional(bidirectional)
+                    .build();
+        } else if (srcPort instanceof org.onosproject.net.OchPort &&
+                   dstPort instanceof org.onosproject.net.OchPort) {
+            print("WARN: encountered old OchPort model");
+            // old OchPort model can be removed when ready
+            OduSignalType signalType = ((org.onosproject.net.OchPort) srcPort).signalType();
+            intent = OpticalConnectivityIntent.builder()
+                    .appId(appId())
+                    .key(key())
+                    .src(ingress)
+                    .dst(egress)
+                    .signalType(signalType)
+                    .bidirectional(bidirectional)
+                    .build();
+        } else {
+            print("Unable to create optical intent between connect points %s and %s", ingress, egress);
+            return;
+        }
+
+        service.submit(intent);
+        print("Optical intent submitted:\n%s", intent.toString());
+    }
+}
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/OpticalPortsListCommand.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/OpticalPortsListCommand.java
new file mode 100644
index 0000000..c6aca6d
--- /dev/null
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/OpticalPortsListCommand.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2014-present 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.net.optical.cli;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.util.Frequency;
+import org.onosproject.cli.net.DevicePortsListCommand;
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
+import org.onosproject.net.Port.Type;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.optical.OchPort;
+import org.onosproject.net.optical.OduCltPort;
+import org.onosproject.net.optical.OmsPort;
+import org.onosproject.net.optical.OtuPort;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
+
+/**
+ * Lists all ports or all ports of a device.
+ */
+@Command(scope = "onos", name = "optical-ports",
+         description = "Lists all optical ports or all optical ports of a device")
+public class OpticalPortsListCommand extends DevicePortsListCommand {
+
+    private static final String FMT = "  port=%s, state=%s, type=%s, speed=%s %s";
+    private static final String FMT_OCH = "  port=%s, state=%s, type=%s, signalType=%s, isTunable=%s %s";
+    private static final String FMT_ODUCLT_OTU = "  port=%s, state=%s, type=%s, signalType=%s %s";
+    private static final String FMT_OMS = "  port=%s, state=%s, type=%s, freqs=%s / %s / %s GHz, totalChannels=%s %s";
+
+    private static final EnumSet<Port.Type> OPTICAL = EnumSet.of(Type.OCH, Type.ODUCLT, Type.OMS, Type.OTU);
+
+    @Override
+    protected void execute() {
+        DeviceService service = opticalView(get(DeviceService.class));
+        if (uri == null) {
+            if (outputJson()) {
+                print("%s", jsonPorts(service, getSortedDevices(service)));
+            } else {
+                for (Device device : getSortedDevices(service)) {
+                    printDevice(service, device);
+                    printPorts(service, device);
+                }
+            }
+
+        } else {
+            Device device = service.getDevice(deviceId(uri));
+            if (device == null) {
+                error("No such device %s", uri);
+            } else if (outputJson()) {
+                print("%s", jsonPorts(service, new ObjectMapper(), device));
+            } else {
+                printDevice(service, device);
+                printPorts(service, device);
+            }
+        }
+    }
+
+    // Determines if a port should be included in output.
+    @Override
+    protected boolean isIncluded(Port port) {
+        return OPTICAL.contains(port.type()) &&
+               super.isIncluded(port);
+    }
+
+    @Override
+    protected void printPorts(DeviceService service, Device device) {
+        List<Port> ports = new ArrayList<>(service.getPorts(device.id()));
+        ports.sort((p1, p2) ->
+             Long.signum(p1.number().toLong() - p2.number().toLong())
+        );
+        for (Port port : ports) {
+            if (!isIncluded(port)) {
+                continue;
+            }
+            String portName = port.number().toString();
+            String portIsEnabled = port.isEnabled() ? "enabled" : "disabled";
+            String portType = port.type().toString().toLowerCase();
+            switch (port.type()) {
+                case OCH:
+                    if (port instanceof OchPort) {
+                        OchPort och = (OchPort) port;
+                        print(FMT_OCH, portName, portIsEnabled, portType,
+                              och.signalType().toString(),
+                              och.isTunable() ? "yes" : "no",
+                              annotations(och.unhandledAnnotations()));
+                       break;
+                    }
+                    print("WARN: OchPort but not on OpticalDevice or ill-formed");
+                    print(FMT, portName, portIsEnabled, portType, port.portSpeed(), annotations(port.annotations()));
+                    break;
+                case ODUCLT:
+                    if (port instanceof OduCltPort) {
+                        OduCltPort oduCltPort = (OduCltPort) port;
+                        print(FMT_ODUCLT_OTU, portName, portIsEnabled, portType,
+                              oduCltPort.signalType().toString(),
+                              annotations(oduCltPort.unhandledAnnotations()));
+                        break;
+                    }
+                    print("WARN: OduCltPort but not on OpticalDevice or ill-formed");
+                    print(FMT, portName, portIsEnabled, portType, port.portSpeed(), annotations(port.annotations()));
+                    break;
+                case OMS:
+                    if (port instanceof OmsPort) {
+                        OmsPort oms = (OmsPort) port;
+                        print(FMT_OMS, portName, portIsEnabled, portType,
+                              oms.minFrequency().asHz() / Frequency.ofGHz(1).asHz(),
+                              oms.maxFrequency().asHz() / Frequency.ofGHz(1).asHz(),
+                              oms.grid().asHz() / Frequency.ofGHz(1).asHz(),
+                              oms.totalChannels(),
+                              annotations(oms.unhandledAnnotations()));
+                        break;
+                    }
+                    print("WARN: OmsPort but not on OpticalDevice or ill-formed");
+                    print(FMT, portName, portIsEnabled, portType, port.portSpeed(), annotations(port.annotations()));
+                    break;
+                case OTU:
+                    if (port instanceof OtuPort) {
+                        OtuPort otuPort = (OtuPort) port;
+                        print(FMT_ODUCLT_OTU, portName, portIsEnabled, portType,
+                              otuPort.signalType().toString(),
+                              annotations(otuPort.unhandledAnnotations()));
+                        break;
+                    }
+                    print("WARN: OtuPort but not on OpticalDevice or ill-formed");
+                    print(FMT, portName, portIsEnabled, portType, port.portSpeed(), annotations(port.annotations()));
+                    break;
+                default:
+                    // do not print non-optical ports
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/package-info.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/package-info.java
new file mode 100644
index 0000000..36e60f0
--- /dev/null
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2016-present 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.
+ */
+
+/**
+ * Console command-line for interacting with the
+ * optical network model &amp; services.
+ */
+package org.onosproject.net.optical.cli;
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/device/FilteredAnnotation.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/FilteredAnnotation.java
new file mode 100644
index 0000000..915e441
--- /dev/null
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/FilteredAnnotation.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016-present 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.net.optical.device;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.onosproject.net.Annotations;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+/**
+ * Filtered {@link Annotations} view.
+ */
+@Beta
+public class FilteredAnnotation implements Annotations {
+
+    private final Annotations delegate;
+    private final Set<String> filtered;
+
+    /**
+     * Creates filtered {@link Annotations} view based on {@code delegate}.
+     *
+     * @param delegate input {@link Annotations}
+     * @param keys to filter-out
+     */
+    public FilteredAnnotation(Annotations delegate, Set<String> keys) {
+        this.delegate = checkNotNull(delegate);
+        this.filtered = ImmutableSet.copyOf(keys);
+    }
+
+    @Override
+    public String value(String key) {
+        if (filtered.contains(key)) {
+            return null;
+        }
+        return delegate.value(key);
+    }
+
+    @Override
+    public Set<String> keys() {
+        return Sets.difference(delegate.keys(), filtered);
+    }
+
+    @Override
+    public String toString() {
+        Map<String, String> mapView = new HashMap<>();
+        keys().forEach(key -> mapView.put(key, delegate.value(key)));
+        return mapView.toString();
+    }
+}
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OchPortHelper.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OchPortHelper.java
index 50dc98c..20bcb25 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OchPortHelper.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OchPortHelper.java
@@ -38,6 +38,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * OCh port related helpers.
@@ -151,6 +152,16 @@
         }
     }
 
+    /**
+     * Returns {@link Annotations} not used by the port type projection.
+     *
+     * @param input {@link Annotations}
+     * @return filtered view of given {@link Annotations}
+     */
+    public static Annotations stripHandledAnnotations(Annotations input) {
+        return new FilteredAnnotation(input, ImmutableSet.of(SIGNAL_TYPE, TUNABLE, LAMBDA));
+    }
+
     // not meant to be instantiated
     private OchPortHelper() {}
 }
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OduCltPortHelper.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OduCltPortHelper.java
index efda401..380b8fe 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OduCltPortHelper.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OduCltPortHelper.java
@@ -33,6 +33,7 @@
 import org.slf4j.Logger;
 
 import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * ODU client port related helpers.
@@ -122,6 +123,16 @@
         }
     }
 
+    /**
+     * Returns {@link Annotations} not used by the port type projection.
+     *
+     * @param input {@link Annotations}
+     * @return filtered view of given {@link Annotations}
+     */
+    public static Annotations stripHandledAnnotations(Annotations input) {
+        return new FilteredAnnotation(input, ImmutableSet.of(SIGNAL_TYPE));
+    }
+
     // not meant to be instantiated
     private OduCltPortHelper() {}
 }
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OmsPortHelper.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OmsPortHelper.java
index 2c99c53..f0d878d 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OmsPortHelper.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OmsPortHelper.java
@@ -33,6 +33,7 @@
 import org.slf4j.Logger;
 
 import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * OMS port related helpers.
@@ -142,6 +143,16 @@
         }
     }
 
+    /**
+     * Returns {@link Annotations} not used by the port type projection.
+     *
+     * @param input {@link Annotations}
+     * @return filtered view of given {@link Annotations}
+     */
+    public static Annotations stripHandledAnnotations(Annotations input) {
+        return new FilteredAnnotation(input, ImmutableSet.of(MIN_FREQ_HZ, MAX_FREQ_HZ, GRID_HZ));
+    }
+
     // not meant to be instantiated
     private OmsPortHelper() {}
 }
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OtuPortHelper.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OtuPortHelper.java
index aeac4f9..968dd6b 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OtuPortHelper.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/device/OtuPortHelper.java
@@ -33,6 +33,7 @@
 import org.slf4j.Logger;
 
 import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * OTU port related helpers.
@@ -122,6 +123,15 @@
         }
     }
 
+    /**
+     * Returns {@link Annotations} not used by the port type projection.
+     *
+     * @param input {@link Annotations}
+     * @return filtered view of given {@link Annotations}
+     */
+    public static Annotations stripHandledAnnotations(Annotations input) {
+        return new FilteredAnnotation(input, ImmutableSet.of(SIGNAL_TYPE));
+    }
 
     // not meant to be instantiated
     private OtuPortHelper() {}
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOchPort.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOchPort.java
index 593b021..eec754a 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOchPort.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOchPort.java
@@ -25,6 +25,7 @@
 import com.google.common.annotations.Beta;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.optical.device.OchPortHelper.stripHandledAnnotations;
 
 import java.util.Objects;
 
@@ -71,12 +72,9 @@
         return signalType.bitRate();
     }
 
-
     @Override
-    public Annotations annotations() {
-        // FIXME Filter OCh annotations, after confirming that
-        // it'll not result in information-loss
-        return super.annotations();
+    public Annotations unhandledAnnotations() {
+        return stripHandledAnnotations(super.annotations());
     }
 
     /**
@@ -140,6 +138,7 @@
                 .add("signalType", signalType())
                 .add("isTunable", isTunable())
                 .add("lambda", lambda())
+                .add("annotations", unhandledAnnotations())
                 .toString();
     }
 }
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOduCltPort.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOduCltPort.java
index 78a4266..adb6b27 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOduCltPort.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOduCltPort.java
@@ -16,9 +16,11 @@
 package org.onosproject.net.optical.impl;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.optical.device.OduCltPortHelper.stripHandledAnnotations;
 
 import java.util.Objects;
 
+import org.onosproject.net.Annotations;
 import org.onosproject.net.CltSignalType;
 import org.onosproject.net.Port;
 import org.onosproject.net.optical.OduCltPort;
@@ -58,6 +60,11 @@
     }
 
     @Override
+    public Annotations unhandledAnnotations() {
+        return stripHandledAnnotations(super.annotations());
+    }
+
+    @Override
     public CltSignalType signalType() {
         return signalType;
     }
@@ -87,6 +94,7 @@
     public String toString() {
         return super.toStringHelper()
                 .add("signalType", signalType())
+                .add("annotations", unhandledAnnotations())
                 .toString();
     }
 
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOmsPort.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOmsPort.java
index eb2c426..980b63e 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOmsPort.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOmsPort.java
@@ -16,10 +16,12 @@
 package org.onosproject.net.optical.impl;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.optical.device.OmsPortHelper.stripHandledAnnotations;
 
 import java.util.Objects;
 
 import org.onlab.util.Frequency;
+import org.onosproject.net.Annotations;
 import org.onosproject.net.Port;
 import org.onosproject.net.optical.OmsPort;
 import org.onosproject.net.utils.ForwardingPort;
@@ -67,6 +69,12 @@
     }
 
     @Override
+    public Annotations unhandledAnnotations() {
+        return stripHandledAnnotations(super.annotations());
+    }
+
+
+    @Override
     public Frequency minFrequency() {
         return minFrequency;
     }
@@ -112,6 +120,7 @@
                 .add("minFrequency", minFrequency())
                 .add("maxFrequency", maxFrequency())
                 .add("grid", grid())
+                .add("annotations", unhandledAnnotations())
                 .toString();
     }
 
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOtuPort.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOtuPort.java
index 2c038c8..7db003d 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOtuPort.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/impl/DefaultOtuPort.java
@@ -16,9 +16,11 @@
 package org.onosproject.net.optical.impl;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.optical.device.OtuPortHelper.stripHandledAnnotations;
 
 import java.util.Objects;
 
+import org.onosproject.net.Annotations;
 import org.onosproject.net.OtuSignalType;
 import org.onosproject.net.Port;
 import org.onosproject.net.optical.OtuPort;
@@ -57,6 +59,11 @@
 //    }
 
     @Override
+    public Annotations unhandledAnnotations() {
+        return stripHandledAnnotations(super.annotations());
+    }
+
+    @Override
     public OtuSignalType signalType() {
         return signalType;
     }
@@ -86,6 +93,7 @@
     public String toString() {
         return super.toStringHelper()
                 .add("signalType", signalType())
+                .add("annotations", unhandledAnnotations())
                 .toString();
     }
 
diff --git a/apps/optical-model/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/optical-model/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..280cd33
--- /dev/null
+++ b/apps/optical-model/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright 2016-present 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.net.optical.cli.AddOpticalIntentCommand"/>
+            <completers>
+                <ref component-id="connectPointCompleter"/>
+                <ref component-id="connectPointCompleter"/>
+            </completers>
+            <optional-completers>
+                <entry key="-a" value-ref="allAppNameCompleter"/>
+            </optional-completers>
+        </command>
+        <command>
+            <action class="org.onosproject.net.optical.cli.OpticalPortsListCommand"/>
+            <completers>
+                <ref component-id="deviceIdCompleter"/>
+            </completers>
+        </command>
+    </command-bundle>
+
+    <bean id="connectPointCompleter" class="org.onosproject.cli.net.ConnectPointCompleter"/>
+    <bean id="allAppNameCompleter" class="org.onosproject.cli.app.AllApplicationNamesCompleter"/>
+    <bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
+
+</blueprint>
\ No newline at end of file