Add CLI for listing all openstack instance ports
Change-Id: I05c826102a257c9d924397d22c368d35ff0587cf
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/InstancePortListCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/InstancePortListCommand.java
new file mode 100644
index 0000000..369b27a
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/InstancePortListCommand.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018-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.openstacknetworking.cli;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+
+import java.util.Comparator;
+import java.util.List;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack instance ports.
+ */
+@Command(scope = "onos", name = "openstack-instance-ports",
+ description = "Lists all OpenStack instance ports")
+public class InstancePortListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT = "%-40s%-10s%-25s%-15s%-20s";
+
+ @Override
+ protected void execute() {
+ InstancePortService service = get(InstancePortService.class);
+ List<InstancePort> instancePorts = Lists.newArrayList(service.instancePorts());
+ instancePorts.sort(Comparator.comparing(InstancePort::portId));
+
+ if (outputJson()) {
+ print("%s", json(this, instancePorts));
+ } else {
+ print(FORMAT, "ID", "State", "Device ID", "Port Number", "Fixed IP");
+ for (InstancePort port : instancePorts) {
+ print(FORMAT, port.portId(), port.state(), port.deviceId().toString(),
+ port.portNumber().toLong(), port.ipAddress().toString());
+ }
+ }
+ }
+
+ private JsonNode json(AbstractShellCommand context, List<InstancePort> ports) {
+ ObjectMapper mapper = new ObjectMapper();
+ ArrayNode result = mapper.enable(INDENT_OUTPUT).createArrayNode();
+ ports.forEach(p -> result.add(context.jsonForEntity(p, InstancePort.class)));
+
+ return result;
+ }
+}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/codec/InstancePortCodec.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/codec/InstancePortCodec.java
new file mode 100644
index 0000000..c4b3cdc
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/codec/InstancePortCodec.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018-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.openstacknetworking.codec;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Openstack instance port codec used for serializing and de-serializing JSON string.
+ */
+public class InstancePortCodec extends JsonCodec<InstancePort> {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final String NETWORK_ID = "networkId";
+ private static final String PORT_ID = "portId";
+ private static final String MAC_ADDRESS = "macAddress";
+ private static final String IP_ADDRESS = "ipAddress";
+ private static final String DEVICE_ID = "deviceId";
+ private static final String PORT_NUMBER = "portNumber";
+ private static final String STATE = "state";
+
+ private static final String MISSING_MESSAGE = " is required in InstancePort";
+
+ @Override
+ public ObjectNode encode(InstancePort port, CodecContext context) {
+ checkNotNull(port, "Instance port cannot be null");
+
+ return context.mapper().createObjectNode()
+ .put(NETWORK_ID, port.networkId())
+ .put(PORT_ID, port.portId())
+ .put(MAC_ADDRESS, port.macAddress().toString())
+ .put(IP_ADDRESS, port.ipAddress().toString())
+ .put(DEVICE_ID, port.deviceId().toString())
+ .put(PORT_NUMBER, port.portNumber().toString())
+ .put(STATE, port.state().name());
+ }
+
+ @Override
+ public InstancePort decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ String networkId = nullIsIllegal(json.get(NETWORK_ID).asText(),
+ NETWORK_ID + MISSING_MESSAGE);
+ String portId = nullIsIllegal(json.get(PORT_ID).asText(),
+ PORT_ID + MISSING_MESSAGE);
+ String macAddress = nullIsIllegal(json.get(MAC_ADDRESS).asText(),
+ MAC_ADDRESS + MISSING_MESSAGE);
+ String ipAddress = nullIsIllegal(json.get(IP_ADDRESS).asText(),
+ IP_ADDRESS + MISSING_MESSAGE);
+ String deviceId = nullIsIllegal(json.get(DEVICE_ID).asText(),
+ DEVICE_ID + MISSING_MESSAGE);
+ String portNumber = nullIsIllegal(json.get(PORT_NUMBER).asText(),
+ PORT_NUMBER + MISSING_MESSAGE);
+ String state = nullIsIllegal(json.get(STATE).asText(),
+ STATE + MISSING_MESSAGE);
+
+ return DefaultInstancePort.builder()
+ .networkId(networkId)
+ .portId(portId)
+ .macAddress(MacAddress.valueOf(macAddress))
+ .ipAddress(IpAddress.valueOf(ipAddress))
+ .deviceId(DeviceId.deviceId(deviceId))
+ .portNumber(PortNumber.fromString(portNumber))
+ .state(InstancePort.State.valueOf(state)).build();
+ }
+}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/codec/package-info.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/codec/package-info.java
new file mode 100644
index 0000000..39000c0
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/codec/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-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.
+ */
+
+/**
+ * Implementations of the codec broker and openstack networking entity JSON codecs.
+ */
+package org.onosproject.openstacknetworking.codec;
\ No newline at end of file
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingCodecRegister.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingCodecRegister.java
new file mode 100644
index 0000000..55077f3
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingCodecRegister.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016-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.openstacknetworking.web;
+
+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.codec.CodecService;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.codec.InstancePortCodec;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the JSON codec brokering service for OpenstackNetworking.
+ */
+@Component(immediate = true)
+public class OpenstackNetworkingCodecRegister {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CodecService codecService;
+
+ @Activate
+ protected void activate() {
+
+ codecService.registerCodec(InstancePort.class, new InstancePortCodec());
+
+ log.info("Started");
+
+ }
+
+ @Deactivate
+ protected void deactivate() {
+
+ codecService.unregisterCodec(InstancePort.class);
+
+ log.info("Stopped");
+ }
+}
diff --git a/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 8fb8642..da387fb 100644
--- a/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -75,10 +75,13 @@
</completers>
</command>
<command>
- <action class="org.onosproject.openstacknetworking.cli.OpenstackAddAclCommand"></action>
+ <action class="org.onosproject.openstacknetworking.cli.OpenstackAddAclCommand" />
</command>
<command>
- <action class="org.onosproject.openstacknetworking.cli.OpenstackRemoveAclCommand"></action>
+ <action class="org.onosproject.openstacknetworking.cli.OpenstackRemoveAclCommand" />
+ </command>
+ <command>
+ <action class="org.onosproject.openstacknetworking.cli.InstancePortListCommand" />
</command>
</command-bundle>
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/InstancePortCodecTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/InstancePortCodecTest.java
new file mode 100644
index 0000000..7c5e61c
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/InstancePortCodecTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2018-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.openstacknetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.hamcrest.MatcherAssert;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.impl.DefaultInstancePort;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onosproject.openstacknetworking.codec.InstancePortJsonMatcher.matchesInstancePort;
+
+/**
+ * Unit tests for InstancePort codec.
+ */
+public class InstancePortCodecTest {
+
+ MockCodecContext context;
+ JsonCodec<InstancePort> instancePortCodec;
+
+
+ @Before
+ public void setUp() {
+ context = new MockCodecContext();
+ instancePortCodec = new InstancePortCodec();
+
+ assertThat(instancePortCodec, notNullValue());
+ }
+
+ /**
+ * Tests the instance port encoding.
+ */
+ @Test
+ public void testInstancePortEncode() {
+ InstancePort port = DefaultInstancePort.builder()
+ .networkId("net-id-1")
+ .portId("port-id-1")
+ .deviceId(DeviceId.deviceId("of:000000000000000a"))
+ .portNumber(PortNumber.portNumber(1, "tap-1"))
+ .ipAddress(IpAddress.valueOf("10.10.10.1"))
+ .macAddress(MacAddress.valueOf("11:22:33:44:55:66"))
+ .state(InstancePort.State.valueOf("ACTIVE"))
+ .build();
+
+ ObjectNode portJson = instancePortCodec.encode(port, context);
+ assertThat(portJson, matchesInstancePort(port));
+ }
+
+ /**
+ * Tests the instance port decoding.
+ */
+ @Test
+ public void testInstancePortDecode() throws IOException {
+ InstancePort port = getInstancePort("InstancePort.json");
+
+ assertThat(port.networkId(), is("net-id-1"));
+ assertThat(port.portId(), is("port-id-1"));
+ assertThat(port.deviceId(), is(DeviceId.deviceId("of:000000000000000a")));
+ assertThat(port.portNumber(), is(PortNumber.portNumber(1, "tap-1")));
+ assertThat(port.ipAddress(), is(IpAddress.valueOf("10.10.10.1")));
+ assertThat(port.macAddress(), is(MacAddress.valueOf("11:22:33:44:55:66")));
+ assertThat(port.state().name(), is("ACTIVE"));
+ }
+
+ /**
+ * Reads in an instance port from the given resource and decodes it.
+ *
+ * @param resourceName resource to use to read the JSON for the rule
+ * @return decoded instance port
+ * @throws IOException if processing the resource fails
+ */
+ private InstancePort getInstancePort(String resourceName) throws IOException {
+ InputStream jsonStream = InstancePortCodecTest.class.getResourceAsStream(resourceName);
+ JsonNode json = context.mapper().readTree(jsonStream);
+ MatcherAssert.assertThat(json, notNullValue());
+ InstancePort port = instancePortCodec.decode((ObjectNode) json, context);
+ assertThat(port, notNullValue());
+ return port;
+ }
+
+ /**
+ * Mock codec context for use in codec unit tests.
+ */
+ private class MockCodecContext implements CodecContext {
+ private final ObjectMapper mapper = new ObjectMapper();
+ private final CodecManager manager = new CodecManager();
+ private final Map<Class<?>, Object> services = new HashMap<>();
+
+ /**
+ * Constructs a new mock codec context.
+ */
+ public MockCodecContext() {
+ manager.activate();
+ }
+
+ @Override
+ public ObjectMapper mapper() {
+ return mapper;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> JsonCodec<T> codec(Class<T> entityClass) {
+ if (entityClass == InstancePort.class) {
+ return (JsonCodec<T>) instancePortCodec;
+ }
+ return manager.getCodec(entityClass);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T getService(Class<T> serviceClass) {
+ return (T) services.get(serviceClass);
+ }
+
+ // for registering mock services
+ public <T> void registerService(Class<T> serviceClass, T impl) {
+ services.put(serviceClass, impl);
+ }
+ }
+}
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/InstancePortJsonMatcher.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/InstancePortJsonMatcher.java
new file mode 100644
index 0000000..b816339
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/codec/InstancePortJsonMatcher.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2018-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.openstacknetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.openstacknetworking.api.InstancePort;
+
+/**
+ * Hamcrest matcher for instance port.
+ */
+public final class InstancePortJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+ private final InstancePort port;
+ private static final String NETWORK_ID = "networkId";
+ private static final String PORT_ID = "portId";
+ private static final String MAC_ADDRESS = "macAddress";
+ private static final String IP_ADDRESS = "ipAddress";
+ private static final String DEVICE_ID = "deviceId";
+ private static final String PORT_NUMBER = "portNumber";
+ private static final String STATE = "state";
+
+ private InstancePortJsonMatcher(InstancePort port) {
+ this.port = port;
+ }
+
+ @Override
+ protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+
+ // check network ID
+ String jsonNetworkId = jsonNode.get(NETWORK_ID).asText();
+ String networkId = port.networkId();
+ if (!jsonNetworkId.equals(networkId)) {
+ description.appendText("networkId was " + jsonNetworkId);
+ return false;
+ }
+
+ // check port ID
+ String jsonPortId = jsonNode.get(PORT_ID).asText();
+ String portId = port.portId();
+ if (!jsonPortId.equals(portId)) {
+ description.appendText("portId was " + jsonPortId);
+ return false;
+ }
+
+ // check MAC address
+ String jsonMacAddress = jsonNode.get(MAC_ADDRESS).asText();
+ String macAddress = port.macAddress().toString();
+ if (!jsonMacAddress.equals(macAddress)) {
+ description.appendText("macAddress was " + jsonMacAddress);
+ return false;
+ }
+
+ // check IP address
+ String jsonIpAddress = jsonNode.get(IP_ADDRESS).asText();
+ String ipAddress = port.ipAddress().toString();
+ if (!jsonIpAddress.equals(ipAddress)) {
+ description.appendText("ipAddress was " + jsonIpAddress);
+ return false;
+ }
+
+ // check device ID
+ String jsonDeviceId = jsonNode.get(DEVICE_ID).asText();
+ String deviceId = port.deviceId().toString();
+ if (!jsonDeviceId.equals(deviceId)) {
+ description.appendText("deviceId was " + jsonDeviceId);
+ return false;
+ }
+
+ // check port number
+ String jsonPortNumber = jsonNode.get(PORT_NUMBER).asText();
+ String portNumber = port.portNumber().toString();
+ if (!jsonPortNumber.equals(portNumber)) {
+ description.appendText("portNumber was " + jsonPortNumber);
+ return false;
+ }
+
+ // check state
+ String jsonState = jsonNode.get(STATE).asText();
+ String state = port.state().name();
+ if (!jsonState.equals(state)) {
+ description.appendText("state was " + jsonState);
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(port.toString());
+ }
+
+ /**
+ * Factory to allocate an instance port matcher.
+ *
+ * @param port openstack instance port we are looking for
+ * @return matcher
+ */
+ public static InstancePortJsonMatcher matchesInstancePort(InstancePort port) {
+ return new InstancePortJsonMatcher(port);
+ }
+}
diff --git a/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/codec/InstancePort.json b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/codec/InstancePort.json
new file mode 100644
index 0000000..040bca2
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/resources/org/onosproject/openstacknetworking/codec/InstancePort.json
@@ -0,0 +1,9 @@
+{
+ "networkId": "net-id-1",
+ "portId": "port-id-1",
+ "deviceId": "of:000000000000000a",
+ "portNumber": "[tap-1](1)",
+ "ipAddress": "10.10.10.1",
+ "macAddress": "11:22:33:44:55:66",
+ "state": "ACTIVE"
+}
\ No newline at end of file