Support GATEWAY type node bootstrapping
- Create router bridge and pactch port to integration bridge for gateway node
- Refactored to listen map event for node add/update
- Added CLIs
Change-Id: Id653f2a2c01d94036f77e6ce1b1230111f3dbbb1
diff --git a/apps/openstacknode/BUCK b/apps/openstacknode/BUCK
index 0921c3d..e614257 100644
--- a/apps/openstacknode/BUCK
+++ b/apps/openstacknode/BUCK
@@ -1,5 +1,7 @@
COMPILE_DEPS = [
'//lib:CORE_DEPS',
+ '//lib:org.apache.karaf.shell.console',
+ '//cli:onos-cli',
'//protocols/ovsdb/api:onos-protocols-ovsdb-api',
'//protocols/ovsdb/rfc:onos-protocols-ovsdb-rfc',
'//core/store/serializers:onos-core-serializers',
@@ -14,5 +16,5 @@
category = 'Utility',
url = 'http://onosproject.org',
description = 'SONA Openstack Node Bootstrap Application.',
- required_app = [ 'org.onosproject.ovsdb' ],
+ required_app = [ 'org.onosproject.ovsdb-base' ],
)
diff --git a/apps/openstacknode/network-cfg.json b/apps/openstacknode/network-cfg.json
index 959b4b3..47d7b80 100644
--- a/apps/openstacknode/network-cfg.json
+++ b/apps/openstacknode/network-cfg.json
@@ -3,29 +3,36 @@
"org.onosproject.openstacknode" : {
"openstacknode" : {
"nodes" : [
- {
- "hostname" : "compute-01",
- "ovsdbIp" : "192.168.56.112",
- "ovsdbPort" : "6640",
- "bridgeId" : "of:0000000000000001",
- "openstackNodeType" : "COMPUTENODE"
- },
- {
- "hostname" : "compute-02",
- "ovsdbIp" : "192.168.56.106",
- "ovsdbPort" : "6640",
- "bridgeId" : "of:0000000000000002",
- "openstackNodeType" : "COMPUTENODE"
- },
- {
- "hostname" : "network",
- "ovsdbIp" : "192.168.56.108",
- "ovsdbPort" : "6640",
- "bridgeId" : "of:0000000000000003",
- "openstackNodeType" : "GATEWAYNODE",
- "gatewayExternalInterfaceName" : "eth1",
- "gatewayExternalInterfaceMac" : "00:00:00:00:00:10"
- }
+ {
+ "hostname" : "compute-01",
+ "type" : "COMPUTE",
+ "managementIp" : "10.203.25.244",
+ "dataIp" : "10.134.34.222",
+ "integrationBridge" : "of:00000000000000a1"
+ },
+ {
+ "hostname" : "compute-02",
+ "type" : "COMPUTE",
+ "managementIp" : "10.203.229.42",
+ "dataIp" : "10.134.34.223",
+ "integrationBridge" : "of:00000000000000a2"
+ },
+ {
+ "hostname" : "gateway-01",
+ "type" : "GATEWAY",
+ "managementIp" : "10.203.198.125",
+ "dataIp" : "10.134.33.208",
+ "integrationBridge" : "of:00000000000000a3",
+ "routerBridge" : "of:00000000000000b3"
+ },
+ {
+ "hostname" : "gateway-02",
+ "type" : "GATEWAY",
+ "managementIp" : "10.203.198.131",
+ "dataIp" : "10.134.33.209",
+ "integrationBridge" : "of:00000000000000a4",
+ "routerBridge" : "of:00000000000000b4"
+ }
]
}
}
diff --git a/apps/openstacknode/pom.xml b/apps/openstacknode/pom.xml
index 7dfb95a..5524b22 100644
--- a/apps/openstacknode/pom.xml
+++ b/apps/openstacknode/pom.xml
@@ -38,7 +38,7 @@
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.readme>SONA Openstack Node Bootstrap Application</onos.app.readme>
<onos.app.requires>
- org.onosproject.ovsdb
+ org.onosproject.ovsdb-base
</onos.app.requires>
</properties>
@@ -58,6 +58,11 @@
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
<artifactId>onos-ovsdb-api</artifactId>
<version>${project.version}</version>
</dependency>
@@ -69,5 +74,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ <version>3.0.5</version>
+ </dependency>
</dependencies>
</project>
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java
new file mode 100644
index 0000000..b4c3f04
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java
@@ -0,0 +1,31 @@
+/*
+ * 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.openstacknode;
+
+/**
+ * Provides constants used in OpenStack node services.
+ */
+public final class Constants {
+
+ private Constants() {
+ }
+
+ public static final String INTEGRATION_BRIDGE = "br-int";
+ public static final String ROUTER_BRIDGE = "br-router";
+ public static final String DEFAULT_TUNNEL = "vxlan";
+ public static final String PATCH_INTG_BRIDGE = "patch-intg";
+ public static final String PATCH_ROUT_BRIDGE = "patch-rout";
+}
\ No newline at end of file
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java
index a285c64..563dea7 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java
@@ -16,14 +16,16 @@
package org.onosproject.openstacknode;
import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.TpPort;
import org.onosproject.net.DeviceId;
+import org.onosproject.openstacknode.OpenstackNodeService.NodeType;
import java.util.Comparator;
import java.util.Objects;
+import java.util.Optional;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
@@ -31,118 +33,121 @@
*/
public final class OpenstackNode {
- private final String hostName;
- private final IpAddress ovsdbIp;
- private final TpPort ovsdbPort;
- private final DeviceId bridgeId;
- private final OpenstackNodeService.OpenstackNodeType openstackNodeType;
- private final String gatewayExternalInterfaceName;
- private final MacAddress gatewayExternalInterfaceMac;
- private static final String OVSDB = "ovsdb:";
-
+ private final String hostname;
+ private final NodeType type;
+ private final IpAddress managementIp;
+ private final IpAddress dataIp;
+ private final DeviceId integrationBridge;
+ private final Optional<DeviceId> routerBridge;
+ private final OpenstackNodeState state;
public static final Comparator<OpenstackNode> OPENSTACK_NODE_COMPARATOR =
- (node1, node2) -> node1.hostName().compareTo(node2.hostName());
+ (node1, node2) -> node1.hostname().compareTo(node2.hostname());
- /**
- * Creates a new node.
- *
- * @param hostName hostName
- * @param ovsdbIp OVSDB server IP address
- * @param ovsdbPort OVSDB server port number
- * @param bridgeId integration bridge identifier
- * @param openstackNodeType openstack node type
- * @param gatewayExternalInterfaceName gatewayExternalInterfaceName
- * @param gatewayExternalInterfaceMac gatewayExternalInterfaceMac
- */
- public OpenstackNode(String hostName, IpAddress ovsdbIp, TpPort ovsdbPort, DeviceId bridgeId,
- OpenstackNodeService.OpenstackNodeType openstackNodeType,
- String gatewayExternalInterfaceName,
- MacAddress gatewayExternalInterfaceMac) {
- this.hostName = checkNotNull(hostName, "hostName cannot be null");
- this.ovsdbIp = checkNotNull(ovsdbIp, "ovsdbIp cannot be null");
- this.ovsdbPort = checkNotNull(ovsdbPort, "ovsdbPort cannot be null");
- this.bridgeId = checkNotNull(bridgeId, "bridgeId cannot be null");
- this.openstackNodeType = checkNotNull(openstackNodeType, "openstackNodeType cannot be null");
- this.gatewayExternalInterfaceName = gatewayExternalInterfaceName;
- this.gatewayExternalInterfaceMac = gatewayExternalInterfaceMac;
-
- if (openstackNodeType == OpenstackNodeService.OpenstackNodeType.GATEWAYNODE) {
- checkNotNull(gatewayExternalInterfaceName, "gatewayExternalInterfaceName cannot be null");
- checkNotNull(gatewayExternalInterfaceMac, "gatewayExternalInterfaceMac cannot be null");
- }
+ private OpenstackNode(String hostname,
+ NodeType type,
+ IpAddress managementIp,
+ IpAddress dataIp,
+ DeviceId integrationBridge,
+ Optional<DeviceId> routerBridge,
+ OpenstackNodeState state) {
+ this.hostname = hostname;
+ this.type = type;
+ this.managementIp = managementIp;
+ this.dataIp = dataIp;
+ this.integrationBridge = integrationBridge;
+ this.routerBridge = routerBridge;
+ this.state = state;
}
/**
- * Returns the OVSDB server IP address.
+ * Returns OpenStack node with new state.
*
- * @return ip address
+ * @param node openstack node
+ * @param state openstack node init state
+ * @return openstack node
*/
- public IpAddress ovsdbIp() {
- return this.ovsdbIp;
+ public static OpenstackNode getUpdatedNode(OpenstackNode node, OpenstackNodeState state) {
+ return new OpenstackNode(node.hostname,
+ node.type,
+ node.managementIp,
+ node.dataIp,
+ node.integrationBridge,
+ node.routerBridge,
+ state);
}
/**
- * Returns the OVSDB server port number.
+ * Returns hostname of the node.
*
- * @return port number
+ * @return hostname
*/
- public TpPort ovsdbPort() {
- return this.ovsdbPort;
+ public String hostname() {
+ return hostname;
}
/**
- * Returns the hostName.
+ * Returns the type of the node.
*
- * @return hostName
+ * @return node type
*/
- public String hostName() {
- return this.hostName;
+ public NodeType type() {
+ return type;
}
/**
- * Returns the identifier of the integration bridge.
+ * Returns the management network IP address of the node.
+ *
+ * @return management network ip address
+ */
+ public IpAddress managementIp() {
+ return managementIp;
+ }
+
+ /**
+ * Returns the data network IP address of the node.
+ *
+ * @return data network ip address
+ */
+ public IpAddress dataIp() {
+ return dataIp;
+ }
+
+ /**
+ * Returns the integration bridge device ID.
*
* @return device id
*/
- public DeviceId intBrId() {
- return this.bridgeId;
+ public DeviceId intBridge() {
+ return integrationBridge;
}
/**
- * Returns the identifier of the OVSDB device.
+ * Returns the router bridge device ID.
+ * It returns valid value only if the node type is GATEWAY.
+ *
+ * @return device id; or empty device id
+ */
+ public Optional<DeviceId> routerBridge() {
+ return routerBridge;
+ }
+
+ /**
+ * Returns the init state of the node.
+ *
+ * @return init state
+ */
+ public OpenstackNodeState state() {
+ return state;
+ }
+
+ /**
+ * Returns the device ID of the OVSDB session of the node.
*
* @return device id
*/
public DeviceId ovsdbId() {
- return DeviceId.deviceId(OVSDB.concat(this.ovsdbIp.toString()));
- }
-
- /**
- * Returns the openstack node type.
- *
- * @return openstack node type
- */
- public OpenstackNodeService.OpenstackNodeType openstackNodeType() {
- return this.openstackNodeType;
- }
-
- /**
- * Returns the gatewayExternalInterfaceName.
- *
- * @return gatewayExternalInterfaceName
- */
- public String gatewayExternalInterfaceName() {
- return this.gatewayExternalInterfaceName;
- }
-
- /**
- * Returns the gatewayExternalInterfaceMac.
- *
- * @return gatewayExternalInterfaceMac
- */
- public MacAddress gatewayExternalInterfaceMac() {
- return this.gatewayExternalInterfaceMac;
+ return DeviceId.deviceId("ovsdb:" + managementIp.toString());
}
@Override
@@ -153,12 +158,12 @@
if (obj instanceof OpenstackNode) {
OpenstackNode that = (OpenstackNode) obj;
-
- if (Objects.equals(hostName, that.hostName) &&
- Objects.equals(ovsdbIp, that.ovsdbIp) &&
- Objects.equals(ovsdbPort, that.ovsdbPort) &&
- Objects.equals(bridgeId, that.bridgeId) &&
- Objects.equals(openstackNodeType, that.openstackNodeType)) {
+ if (Objects.equals(hostname, that.hostname) &&
+ Objects.equals(type, that.type) &&
+ Objects.equals(managementIp, that.managementIp) &&
+ Objects.equals(dataIp, that.dataIp) &&
+ Objects.equals(integrationBridge, that.integrationBridge) &&
+ Objects.equals(routerBridge, that.routerBridge)) {
return true;
}
}
@@ -167,29 +172,142 @@
@Override
public int hashCode() {
- return Objects.hash(hostName, ovsdbIp, ovsdbPort, bridgeId, openstackNodeType);
+ return Objects.hash(hostname,
+ type,
+ managementIp,
+ dataIp,
+ integrationBridge,
+ routerBridge);
}
@Override
public String toString() {
- if (openstackNodeType == OpenstackNodeService.OpenstackNodeType.COMPUTENODE) {
- return MoreObjects.toStringHelper(getClass())
- .add("host", hostName)
- .add("ip", ovsdbIp)
- .add("port", ovsdbPort)
- .add("bridgeId", bridgeId)
- .add("openstacknodetype", openstackNodeType)
- .toString();
- } else {
- return MoreObjects.toStringHelper(getClass())
- .add("host", hostName)
- .add("ip", ovsdbIp)
- .add("port", ovsdbPort)
- .add("bridgeId", bridgeId)
- .add("openstacknodetype", openstackNodeType)
- .add("gatewayExternalInterfaceName", gatewayExternalInterfaceName)
- .add("gatewayExternalInterfaceMac", gatewayExternalInterfaceMac)
- .toString();
+ return MoreObjects.toStringHelper(getClass())
+ .add("hostname", hostname)
+ .add("type", type)
+ .add("managementIp", managementIp)
+ .add("dataIp", dataIp)
+ .add("integrationBridge", integrationBridge)
+ .add("routerBridge", routerBridge)
+ .add("state", state)
+ .toString();
+ }
+
+ /**
+ * Returns a new builder instance.
+ *
+ * @return openstack node builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder of OpenStack node entities.
+ */
+ public static final class Builder {
+ private String hostname;
+ private NodeType type;
+ private IpAddress managementIp;
+ private IpAddress dataIp;
+ private DeviceId integrationBridge;
+ private Optional<DeviceId> routerBridge = Optional.empty();
+ private OpenstackNodeState state = OpenstackNodeState.noState();
+
+ private Builder() {
+ }
+
+ public OpenstackNode build() {
+ checkArgument(!Strings.isNullOrEmpty(hostname));
+ checkNotNull(type);
+ checkNotNull(managementIp);
+ checkNotNull(dataIp);
+ checkNotNull(integrationBridge);
+ checkNotNull(routerBridge);
+ return new OpenstackNode(hostname,
+ type,
+ managementIp,
+ dataIp,
+ integrationBridge,
+ routerBridge,
+ state);
+ }
+
+ /**
+ * Returns node builder with the hostname.
+ *
+ * @param hostname hostname
+ * @return openstack node builder
+ */
+ public Builder hostname(String hostname) {
+ this.hostname = hostname;
+ return this;
+ }
+
+ /**
+ * Returns node builder with the node type.
+ *
+ * @param type openstack node type
+ * @return openstack node builder
+ */
+ public Builder type(NodeType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Returns node builder with the management network IP address.
+ *
+ * @param managementIp management ip address
+ * @return openstack node builder
+ */
+ public Builder managementIp(IpAddress managementIp) {
+ this.managementIp = managementIp;
+ return this;
+ }
+
+ /**
+ * Returns node builder with the data network IP address.
+ *
+ * @param dataIp data network ip address
+ * @return openstack node builder
+ */
+ public Builder dataIp(IpAddress dataIp) {
+ this.dataIp = dataIp;
+ return this;
+ }
+
+ /**
+ * Returns node builder with the integration bridge ID.
+ *
+ * @param integrationBridge integration bridge device id
+ * @return openstack node builder
+ */
+ public Builder integrationBridge(DeviceId integrationBridge) {
+ this.integrationBridge = integrationBridge;
+ return this;
+ }
+
+ /**
+ * Returns node builder with the router bridge ID.
+ *
+ * @param routerBridge router bridge device ID
+ * @return openstack node builder
+ */
+ public Builder routerBridge(DeviceId routerBridge) {
+ this.routerBridge = Optional.ofNullable(routerBridge);
+ return this;
+ }
+
+ /**
+ * Returns node builder with the init state.
+ *
+ * @param state node init state
+ * @return openstack node builder
+ */
+ public Builder state(OpenstackNodeState state) {
+ this.state = state;
+ return this;
}
}
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java
index 5d4f6b7..ea2d30d 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java
@@ -17,73 +17,94 @@
import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Sets;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.TpPort;
+import org.onlab.packet.IpAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
-import org.slf4j.Logger;
+import org.onosproject.openstacknode.OpenstackNodeService.NodeType;
import java.util.Set;
import org.onosproject.net.config.Config;
-import static org.slf4j.LoggerFactory.getLogger;
+
+import static org.onosproject.net.config.Config.FieldPresence.MANDATORY;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
/**
* Configuration object for OpensatckNode service.
*/
-public class OpenstackNodeConfig extends Config<ApplicationId> {
+public final class OpenstackNodeConfig extends Config<ApplicationId> {
- protected final Logger log = getLogger(getClass());
+ private static final String NODES = "nodes";
+ private static final String HOST_NAME = "hostname";
+ private static final String TYPE = "type";
+ private static final String MANAGEMENT_IP = "managementIp";
+ private static final String DATA_IP = "dataIp";
+ private static final String INTEGRATION_BRIDGE = "integrationBridge";
+ private static final String ROUTER_BRIDGE = "routerBridge";
+ @Override
+ public boolean isValid() {
+ boolean result = hasOnlyFields(NODES);
- public static final String NODES = "nodes";
- public static final String HOST_NAME = "hostname";
- public static final String OVSDB_IP = "ovsdbIp";
- public static final String OVSDB_PORT = "ovsdbPort";
- public static final String BRIDGE_ID = "bridgeId";
- public static final String NODE_TYPE = "openstackNodeType";
- public static final String GATEWAY_EXTERNAL_INTERFACE_NAME = "gatewayExternalInterfaceName";
- public static final String GATEWAY_EXTERNAL_INTERFACE_MAC = "gatewayExternalInterfaceMac";
+ if (object.get(NODES) == null || object.get(NODES).size() < 1) {
+ final String msg = "No node is present";
+ throw new IllegalArgumentException(msg);
+ }
+
+ for (JsonNode node : object.get(NODES)) {
+ ObjectNode osNode = (ObjectNode) node;
+ result &= hasOnlyFields(osNode,
+ HOST_NAME,
+ TYPE,
+ MANAGEMENT_IP,
+ DATA_IP,
+ INTEGRATION_BRIDGE,
+ ROUTER_BRIDGE
+ );
+
+ result &= isString(osNode, HOST_NAME, MANDATORY);
+ result &= isString(osNode, TYPE, MANDATORY);
+ result &= isIpAddress(osNode, MANAGEMENT_IP, MANDATORY);
+ result &= result && isIpAddress(osNode, DATA_IP, MANDATORY);
+ result &= isString(osNode, INTEGRATION_BRIDGE, MANDATORY);
+
+ DeviceId.deviceId(osNode.get(INTEGRATION_BRIDGE).asText());
+ NodeType.valueOf(osNode.get(TYPE).asText());
+
+ if (osNode.get(TYPE).asText().equals(GATEWAY.name())) {
+ result &= isString(osNode, ROUTER_BRIDGE, MANDATORY);
+ DeviceId.deviceId(osNode.get(ROUTER_BRIDGE).asText());
+ }
+ }
+ return result;
+ }
/**
* Returns the set of nodes read from network config.
*
- * @return set of OpensatckNodeConfig or null
+ * @return set of openstack nodes
*/
public Set<OpenstackNode> openstackNodes() {
-
Set<OpenstackNode> nodes = Sets.newHashSet();
- JsonNode jsonNodes = object.get(NODES);
- if (jsonNodes == null) {
- return null;
- }
+ for (JsonNode node : object.get(NODES)) {
+ NodeType type = NodeType.valueOf(get(node, TYPE));
+ OpenstackNode.Builder nodeBuilder = OpenstackNode.builder()
+ .integrationBridge(DeviceId.deviceId(get(node, INTEGRATION_BRIDGE)))
+ .dataIp(IpAddress.valueOf(get(node, DATA_IP)))
+ .managementIp(IpAddress.valueOf(get(node, MANAGEMENT_IP)))
+ .type(type)
+ .hostname(get(node, HOST_NAME));
- jsonNodes.forEach(jsonNode -> {
- try {
- if (OpenstackNodeService.OpenstackNodeType.valueOf(jsonNode.path(NODE_TYPE).asText()) ==
- OpenstackNodeService.OpenstackNodeType.COMPUTENODE) {
- nodes.add(new OpenstackNode(
- jsonNode.path(HOST_NAME).asText(),
- Ip4Address.valueOf(jsonNode.path(OVSDB_IP).asText()),
- TpPort.tpPort(jsonNode.path(OVSDB_PORT).asInt()),
- DeviceId.deviceId(jsonNode.path(BRIDGE_ID).asText()),
- OpenstackNodeService.OpenstackNodeType.valueOf(jsonNode.path(NODE_TYPE).asText()),
- null, MacAddress.NONE));
- } else {
- nodes.add(new OpenstackNode(
- jsonNode.path(HOST_NAME).asText(),
- Ip4Address.valueOf(jsonNode.path(OVSDB_IP).asText()),
- TpPort.tpPort(jsonNode.path(OVSDB_PORT).asInt()),
- DeviceId.deviceId(jsonNode.path(BRIDGE_ID).asText()),
- OpenstackNodeService.OpenstackNodeType.valueOf(jsonNode.path(NODE_TYPE).asText()),
- jsonNode.path(GATEWAY_EXTERNAL_INTERFACE_NAME).asText(),
- MacAddress.valueOf(jsonNode.path(GATEWAY_EXTERNAL_INTERFACE_MAC).asText())));
- }
- } catch (IllegalArgumentException | NullPointerException e) {
- log.error("Failed to read {}", e.toString());
+ if (type.equals(GATEWAY)) {
+ nodeBuilder.routerBridge(DeviceId.deviceId(get(node, ROUTER_BRIDGE)));
}
- });
+ nodes.add(nodeBuilder.build());
+ }
return nodes;
}
+
+ private String get(JsonNode jsonNode, String path) {
+ return jsonNode.get(path).asText();
+ }
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
index a645ec5..1995111 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
@@ -15,14 +15,20 @@
*/
package org.onosproject.openstacknode;
+import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
-import org.onlab.util.ItemNotFoundException;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
import org.onlab.util.KryoNamespace;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.LeadershipService;
import org.onosproject.cluster.NodeId;
@@ -31,12 +37,15 @@
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.behaviour.BridgeConfig;
import org.onosproject.net.behaviour.BridgeDescription;
import org.onosproject.net.behaviour.ControllerInfo;
import org.onosproject.net.behaviour.DefaultBridgeDescription;
+import org.onosproject.net.behaviour.DefaultPatchDescription;
import org.onosproject.net.behaviour.DefaultTunnelDescription;
import org.onosproject.net.behaviour.InterfaceConfig;
+import org.onosproject.net.behaviour.PatchDescription;
import org.onosproject.net.behaviour.TunnelDescription;
import org.onosproject.net.behaviour.TunnelEndPoints;
import org.onosproject.net.behaviour.TunnelKeys;
@@ -44,57 +53,61 @@
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.config.basics.SubjectFactories;
-import org.onosproject.net.device.DeviceAdminService;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.driver.DriverHandler;
-import org.onosproject.net.driver.DriverService;
import org.onosproject.ovsdb.controller.OvsdbClientService;
import org.onosproject.ovsdb.controller.OvsdbController;
import org.onosproject.ovsdb.controller.OvsdbNodeId;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
import static org.onosproject.net.Device.Type.SWITCH;
import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
+import static org.onosproject.openstacknode.Constants.*;
import static org.slf4j.LoggerFactory.getLogger;
-import java.util.ArrayList;
+import java.util.Dictionary;
import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
-import static com.google.common.base.Preconditions.checkNotNull;
-
/**
* Initializes devices in compute/gateway nodes according to there type.
*/
@Component(immediate = true)
@Service
-public class OpenstackNodeManager implements OpenstackNodeService {
+public final class OpenstackNodeManager implements OpenstackNodeService {
protected final Logger log = getLogger(getClass());
+
private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
.register(OpenstackNode.class)
- .register(OpenstackNodeType.class)
+ .register(NodeType.class)
.register(NodeState.class);
- private static final String DEFAULT_BRIDGE = "br-int";
- private static final String DEFAULT_TUNNEL = "vxlan";
- private static final String PORT_NAME = "portName";
- private static final String OPENSTACK_NODESTORE = "openstacknode-nodestore";
- private static final String OPENSTACK_NODEMANAGER_ID = "org.onosproject.openstacknode";
+ private static final String OVSDB_PORT = "ovsdbPort";
+ private static final int DEFAULT_OVSDB_PORT = 6640;
+ private static final int DEFAULT_OFPORT = 6653;
private static final int DPID_BEGIN = 3;
- private static final int OFPORT = 6653;
+
+ private static final String APP_ID = "org.onosproject.openstacknode";
+ private static final Class<OpenstackNodeConfig> CONFIG_CLASS = OpenstackNodeConfig.class;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@@ -109,16 +122,10 @@
protected ClusterService clusterService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DriverService driverService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceAdminService adminService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected NetworkConfigService configService;
+ protected ComponentConfigService componentConfigService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry configRegistry;
@@ -126,378 +133,439 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LeadershipService leadershipService;
- private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
- private final BridgeHandler bridgeHandler = new BridgeHandler();
- private final NetworkConfigListener configListener = new InternalConfigListener();
+ @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
+ label = "OVSDB server listen port")
+ private int ovsdbPort = DEFAULT_OVSDB_PORT;
+
+ private final ExecutorService eventExecutor =
+ newSingleThreadScheduledExecutor(groupedThreads("onos/openstack-node", "event-handler"));
+
private final ConfigFactory configFactory =
- new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, OpenstackNodeConfig.class, "openstacknode") {
+ new ConfigFactory<ApplicationId, OpenstackNodeConfig>(
+ SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "openstacknode") {
@Override
public OpenstackNodeConfig createConfig() {
return new OpenstackNodeConfig();
}
};
- private final ExecutorService eventExecutor =
- newSingleThreadScheduledExecutor(groupedThreads("onos/openstacknode", "event-handler", log));
-
-
+ private final NetworkConfigListener configListener = new InternalConfigListener();
private final DeviceListener deviceListener = new InternalDeviceListener();
+ private final MapEventListener<String, OpenstackNode> nodeStoreListener = new InternalMapListener();
+ private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
+ private final BridgeHandler bridgeHandler = new BridgeHandler();
+
+ private ConsistentMap<String, OpenstackNode> nodeStore;
private ApplicationId appId;
- private ConsistentMap<OpenstackNode, NodeState> nodeStore;
private NodeId localNodeId;
- private enum NodeState {
+ private enum NodeState implements OpenstackNodeState {
INIT {
@Override
- public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
- openstackNodeManager.connect(node);
- }
- },
- OVSDB_CONNECTED {
- @Override
- public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
- if (!openstackNodeManager.getOvsdbConnectionState(node)) {
- openstackNodeManager.connect(node);
- } else {
- openstackNodeManager.createIntegrationBridge(node);
+ public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
+ // make sure there is OVSDB connection
+ if (!nodeManager.isOvsdbConnected(node)) {
+ nodeManager.connectOvsdb(node);
+ return;
+ }
+ nodeManager.createBridge(node,
+ INTEGRATION_BRIDGE,
+ node.intBridge().toString().substring(DPID_BEGIN));
+
+ // creates additional router bridge if the node type is GATEWAY
+ if (node.type().equals(NodeType.GATEWAY)) {
+ nodeManager.createBridge(node,
+ ROUTER_BRIDGE,
+ node.routerBridge().get().toString().substring(DPID_BEGIN));
}
}
},
BRIDGE_CREATED {
@Override
- public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
- if (!openstackNodeManager.getOvsdbConnectionState(node)) {
- openstackNodeManager.connect(node);
- } else {
- openstackNodeManager.createTunnelInterface(node);
+ public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
+ // make sure there is OVSDB connection
+ if (!nodeManager.isOvsdbConnected(node)) {
+ nodeManager.connectOvsdb(node);
+ return;
+ }
+ nodeManager.createTunnelInterface(node);
+ // creates additional patch ports connecting integration bridge and
+ // router bridge if the node type is GATEWAY
+ if (node.type().equals(NodeType.GATEWAY)) {
+ nodeManager.createPatchInterface(node);
}
}
},
COMPLETE {
@Override
- public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
- openstackNodeManager.postInit(node);
+ public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
+ nodeManager.postInit(node);
}
},
INCOMPLETE {
@Override
- public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
+ public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
}
};
- public abstract void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node);
+ public abstract void process(OpenstackNodeManager nodeManager, OpenstackNode node);
}
@Activate
protected void activate() {
- appId = coreService.registerApplication(OPENSTACK_NODEMANAGER_ID);
+ appId = coreService.getAppId(APP_ID);
+
localNodeId = clusterService.getLocalNode().id();
leadershipService.runForLeadership(appId.name());
- nodeStore = storageService.<OpenstackNode, NodeState>consistentMapBuilder()
+ nodeStore = storageService.<String, OpenstackNode>consistentMapBuilder()
.withSerializer(Serializer.using(NODE_SERIALIZER.build()))
- .withName(OPENSTACK_NODESTORE)
+ .withName("openstack-nodestore")
.withApplicationId(appId)
.build();
+ nodeStore.addListener(nodeStoreListener);
deviceService.addListener(deviceListener);
+
configRegistry.registerConfigFactory(configFactory);
- configService.addListener(configListener);
- readConfiguration();
+ configRegistry.addListener(configListener);
+ componentConfigService.registerProperties(getClass());
log.info("Started");
}
@Deactivate
protected void deactivate() {
+ configRegistry.removeListener(configListener);
deviceService.removeListener(deviceListener);
- eventExecutor.shutdown();
- nodeStore.clear();
+ nodeStore.removeListener(nodeStoreListener);
+ componentConfigService.unregisterProperties(getClass(), true);
configRegistry.unregisterConfigFactory(configFactory);
- configService.removeListener(configListener);
+
leadershipService.withdraw(appId.name());
+ eventExecutor.shutdown();
log.info("Stopped");
}
+ @Modified
+ protected void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
+ if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
+ ovsdbPort = updatedOvsdbPort;
+ }
+
+ log.info("Modified");
+ }
@Override
- public void addNode(OpenstackNode node) {
- checkNotNull(node, "Node cannot be null");
-
- NodeId leaderNodeId = leadershipService.getLeader(appId.name());
- log.debug("Node init requested, localNodeId: {}, leaderNodeId: {}", localNodeId, leaderNodeId);
-
- //TODO: Fix any node can engage this operation.
- if (!localNodeId.equals(leaderNodeId)) {
- log.debug("Only the leaderNode can perform addNode operation");
- return;
- }
- nodeStore.putIfAbsent(node, checkNodeState(node));
- NodeState state = checkNodeState(node);
- state.process(this, node);
+ public void addOrUpdateNode(OpenstackNode node) {
+ nodeStore.put(node.hostname(),
+ OpenstackNode.getUpdatedNode(node, nodeState(node)));
}
@Override
public void deleteNode(OpenstackNode node) {
- checkNotNull(node, "Node cannot be null");
-
- if (getOvsdbConnectionState(node)) {
- disconnect(node);
+ if (isOvsdbConnected(node)) {
+ OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
+ controller.getOvsdbClient(ovsdb).disconnect();
}
-
- nodeStore.remove(node);
+ nodeStore.remove(node.hostname());
}
@Override
- public List<OpenstackNode> getNodes(OpenstackNodeType openstackNodeType) {
- List<OpenstackNode> nodes = new ArrayList<>();
- nodes.addAll(nodeStore.keySet().stream().filter(node -> node.openstackNodeType()
- .equals(openstackNodeType)).collect(Collectors.toList()));
- return nodes;
- }
-
- private List<OpenstackNode> getNodesAll() {
- List<OpenstackNode> nodes = new ArrayList<>();
- nodes.addAll(nodeStore.keySet());
- return nodes;
+ public List<OpenstackNode> nodes() {
+ return nodeStore.values().stream().map(Versioned::value).collect(Collectors.toList());
}
@Override
- public boolean isComplete(OpenstackNode node) {
- checkNotNull(node, "Node cannot be null");
+ public Set<OpenstackNode> completeNodes() {
+ return nodeStore.values().stream().map(Versioned::value)
+ .filter(node -> node.state().equals(NodeState.COMPLETE))
+ .collect(Collectors.toSet());
+ }
- if (!nodeStore.containsKey(node)) {
- log.warn("Node {} does not exist", node.hostName());
+ @Override
+ public boolean isComplete(String hostname) {
+ Versioned<OpenstackNode> versionedNode = nodeStore.get(hostname);
+ if (versionedNode == null) {
+ log.warn("Node {} does not exist", hostname);
return false;
- } else if (nodeStore.get(node).equals(NodeState.COMPLETE)) {
- return true;
}
- return false;
+ OpenstackNodeState state = versionedNode.value().state();
+ return state != null && state.equals(NodeState.COMPLETE);
}
- /**
- * Checks current state of a given openstack node and returns it.
- *
- * @param node openstack node
- * @return node state
- */
- private NodeState checkNodeState(OpenstackNode node) {
- checkNotNull(node, "Node cannot be null");
+ @Override
+ public Optional<IpAddress> dataIp(DeviceId deviceId) {
+ OpenstackNode node = nodeByDeviceId(deviceId);
+ if (node == null) {
+ log.warn("Failed to get node for {}", deviceId);
+ return Optional.empty();
+ }
+ return Optional.of(node.dataIp());
+ }
- if (checkIntegrationBridge(node) && checkTunnelInterface(node)) {
- return NodeState.COMPLETE;
- } else if (checkIntegrationBridge(node)) {
- return NodeState.BRIDGE_CREATED;
- } else if (getOvsdbConnectionState(node)) {
- return NodeState.OVSDB_CONNECTED;
- } else {
+ @Override
+ public Optional<PortNumber> tunnelPort(DeviceId deviceId) {
+ return deviceService.getPorts(deviceId).stream()
+ .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL) &&
+ p.isEnabled())
+ .map(Port::number).findFirst();
+ }
+
+ @Override
+ public Optional<DeviceId> routerBridge(DeviceId intBridgeId) {
+ OpenstackNode node = nodeByDeviceId(intBridgeId);
+ if (node == null || node.type().equals(NodeType.COMPUTE)) {
+ log.warn("Failed to find router bridge connected to {}", intBridgeId);
+ return Optional.empty();
+ }
+ return node.routerBridge();
+ }
+
+ @Override
+ public Optional<PortNumber> externalPort(DeviceId intBridgeId) {
+ return deviceService.getPorts(intBridgeId).stream()
+ .filter(p -> p.annotations().value(PORT_NAME).equals(PATCH_INTG_BRIDGE) &&
+ p.isEnabled())
+ .map(Port::number).findFirst();
+ }
+
+ private void initNode(OpenstackNode node) {
+ NodeState state = (NodeState) node.state();
+ state.process(this, node);
+ log.debug("Processing node: {} state: {}", node.hostname(), state);
+ }
+
+ private void postInit(OpenstackNode node) {
+ if (isOvsdbConnected(node)) {
+ OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
+ controller.getOvsdbClient(ovsdb).disconnect();
+ }
+
+ // TODO add gateway node to scalable gateway pool
+ log.info("Finished init {}", node.hostname());
+ }
+
+ private void setNodeState(OpenstackNode node, NodeState newState) {
+ log.debug("Changed {} state: {}", node.hostname(), newState);
+ nodeStore.put(node.hostname(), OpenstackNode.getUpdatedNode(node, newState));
+ }
+
+ private NodeState nodeState(OpenstackNode node) {
+ if (!deviceService.isAvailable(node.intBridge())) {
return NodeState.INIT;
}
- }
-
-
- /**
- * Checks if integration bridge exists and available.
- *
- * @param node openstack node
- * @return true if the bridge is available, false otherwise
- */
- private boolean checkIntegrationBridge(OpenstackNode node) {
- return (deviceService.getDevice(node.intBrId()) != null
- && deviceService.isAvailable(node.intBrId()));
- }
- /**
- * Checks if tunnel interface exists.
- *
- * @param node openstack node
- * @return true if the interface exists, false otherwise
- */
- private boolean checkTunnelInterface(OpenstackNode node) {
- checkNotNull(node, "Node cannot be null");
- return deviceService.getPorts(node.intBrId())
- .stream()
- .filter(p -> p.annotations().value(PORT_NAME).contains(DEFAULT_TUNNEL) && p.isEnabled())
- .findAny().isPresent();
- }
-
- /**
- * Returns connection state of OVSDB server for a given node.
- *
- * @param node openstack node
- * @return true if it is connected, false otherwise
- */
- private boolean getOvsdbConnectionState(OpenstackNode node) {
- checkNotNull(node, "Node cannot be null");
-
- OvsdbClientService ovsdbClient = getOvsdbClient(node);
- return deviceService.isAvailable(node.ovsdbId()) &&
- ovsdbClient != null && ovsdbClient.isConnected();
- }
-
- /**
- * Returns OVSDB client for a given node.
- *
- * @param node openstack node
- * @return OVSDB client, or null if it fails to get OVSDB client
- */
- private OvsdbClientService getOvsdbClient(OpenstackNode node) {
- checkNotNull(node, "Node cannot be null");
-
- OvsdbClientService ovsdbClient = controller.getOvsdbClient(
- new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt()));
- if (ovsdbClient == null) {
- log.debug("Couldn't find OVSDB client for {}", node.hostName());
- }
- return ovsdbClient;
- }
-
- /**
- * Connects to OVSDB server for a given node.
- *
- * @param node openstack node
- */
- private void connect(OpenstackNode node) {
- checkNotNull(node, "Node cannot be null");
-
- if (!nodeStore.containsKey(node)) {
- log.warn("Node {} does not exist", node.hostName());
- return;
+ if (node.type().equals(NodeType.GATEWAY) &&
+ !deviceService.isAvailable(node.routerBridge().get())) {
+ return NodeState.INIT;
}
- if (!getOvsdbConnectionState(node)) {
- controller.connect(node.ovsdbIp(), node.ovsdbPort());
+ if (!isIfaceCreated(node.intBridge(), DEFAULT_TUNNEL)) {
+ return NodeState.BRIDGE_CREATED;
}
+ if (node.type().equals(NodeType.GATEWAY) && (
+ !isIfaceCreated(node.routerBridge().get(), PATCH_ROUT_BRIDGE) ||
+ !isIfaceCreated(node.intBridge(), PATCH_INTG_BRIDGE))) {
+ return NodeState.BRIDGE_CREATED;
+ }
+
+ return NodeState.COMPLETE;
}
- /**
- * Creates an integration bridge for a given node.
- *
- * @param node openstack node
- */
- private void createIntegrationBridge(OpenstackNode node) {
- if (checkIntegrationBridge(node)) {
+ private boolean isIfaceCreated(DeviceId deviceId, String ifaceName) {
+ return deviceService.getPorts(deviceId).stream()
+ .filter(p -> p.annotations().value(PORT_NAME).contains(ifaceName) &&
+ p.isEnabled())
+ .findAny()
+ .isPresent();
+ }
+
+ private void createBridge(OpenstackNode node, String bridgeName, String dpid) {
+ Device device = deviceService.getDevice(node.ovsdbId());
+ if (device == null || !device.is(BridgeConfig.class)) {
+ log.error("Failed to create integration bridge on {}", node.ovsdbId());
return;
}
List<ControllerInfo> controllers = clusterService.getNodes().stream()
- .map(controller -> new ControllerInfo(controller.ip(), OFPORT, "tcp"))
+ .map(controller -> new ControllerInfo(controller.ip(), DEFAULT_OFPORT, "tcp"))
.collect(Collectors.toList());
- String dpid = node.intBrId().toString().substring(DPID_BEGIN);
BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
- .name(DEFAULT_BRIDGE)
+ .name(bridgeName)
.failMode(BridgeDescription.FailMode.SECURE)
.datapathId(dpid)
.disableInBand()
.controllers(controllers)
.build();
- try {
- DriverHandler handler = driverService.createHandler(node.ovsdbId());
- BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
- bridgeConfig.addBridge(bridgeDesc);
- } catch (ItemNotFoundException e) {
- log.warn("Failed to create integration bridge on {}", node.ovsdbId());
- }
+ BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+ bridgeConfig.addBridge(bridgeDesc);
}
- /**
- * Creates tunnel interface to the integration bridge for a given node.
- *
- * @param node openstack node
- */
private void createTunnelInterface(OpenstackNode node) {
- if (checkTunnelInterface(node)) {
+ Device device = deviceService.getDevice(node.ovsdbId());
+ if (device == null || !device.is(InterfaceConfig.class)) {
+ log.error("Failed to create tunnel interface on {}", node.ovsdbId());
return;
}
- TunnelDescription description = DefaultTunnelDescription.builder()
- .deviceId(DEFAULT_BRIDGE)
+ TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
+ .deviceId(INTEGRATION_BRIDGE)
.ifaceName(DEFAULT_TUNNEL)
.type(VXLAN)
.remote(TunnelEndPoints.flowTunnelEndpoint())
.key(TunnelKeys.flowTunnelKey())
.build();
- try {
- DriverHandler handler = driverService.createHandler(node.ovsdbId());
- InterfaceConfig ifaceConfig = handler.behaviour(InterfaceConfig.class);
- ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, description);
- } catch (ItemNotFoundException e) {
- log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
- }
+
+ InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
+ ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
}
- /**
- * Performs tasks after node initialization.
- * First disconnect unnecessary OVSDB connection and then installs flow rules
- * for existing VMs if there are any.
- *
- * @param node openstack node
- */
- private void postInit(OpenstackNode node) {
- disconnect(node);
- log.info("Finished initializing {}", node.hostName());
- }
-
- /**
- * Sets a new state for a given openstack node.
- *
- * @param node openstack node
- * @param newState new node state
- */
- private void setNodeState(OpenstackNode node, NodeState newState) {
- checkNotNull(node, "Node cannot be null");
-
- log.debug("Changed {} state: {}", node.hostName(), newState.toString());
-
- nodeStore.put(node, newState);
- newState.process(this, node);
- }
-
- /**
- * Returns openstack node associated with a given OVSDB device.
- *
- * @param ovsdbId OVSDB device id
- * @return openstack node, null if it fails to find the node
- */
- private OpenstackNode getNodeByOvsdbId(DeviceId ovsdbId) {
-
- return getNodesAll().stream()
- .filter(node -> node.ovsdbId().equals(ovsdbId))
- .findFirst().orElse(null);
- }
-
- /**
- * Returns openstack node associated with a given integration bridge.
- *
- * @param bridgeId device id of integration bridge
- * @return openstack node, null if it fails to find the node
- */
- private OpenstackNode getNodeByBridgeId(DeviceId bridgeId) {
- return getNodesAll().stream()
- .filter(node -> node.intBrId().equals(bridgeId))
- .findFirst().orElse(null);
- }
- /**
- * Disconnects OVSDB server for a given node.
- *
- * @param node openstack node
- */
- private void disconnect(OpenstackNode node) {
- checkNotNull(node, "Node cannot be null");
-
- if (!nodeStore.containsKey(node)) {
- log.warn("Node {} does not exist", node.hostName());
+ private void createPatchInterface(OpenstackNode node) {
+ Device device = deviceService.getDevice(node.ovsdbId());
+ if (device == null || !device.is(InterfaceConfig.class)) {
+ log.error("Failed to create patch interfaces on {}", node.hostname());
return;
}
- if (getOvsdbConnectionState(node)) {
- OvsdbClientService ovsdbClient = getOvsdbClient(node);
- ovsdbClient.disconnect();
+ PatchDescription patchIntg = DefaultPatchDescription.builder()
+ .deviceId(INTEGRATION_BRIDGE)
+ .ifaceName(PATCH_INTG_BRIDGE)
+ .peer(PATCH_ROUT_BRIDGE)
+ .build();
+
+ PatchDescription patchRout = DefaultPatchDescription.builder()
+ .deviceId(ROUTER_BRIDGE)
+ .ifaceName(PATCH_ROUT_BRIDGE)
+ .peer(PATCH_INTG_BRIDGE)
+ .build();
+
+ InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
+ ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
+ ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
+ }
+
+ private boolean isOvsdbConnected(OpenstackNode node) {
+ OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
+ OvsdbClientService client = controller.getOvsdbClient(ovsdb);
+ return deviceService.isAvailable(node.ovsdbId()) &&
+ client != null &&
+ client.isConnected();
+ }
+
+ private void connectOvsdb(OpenstackNode node) {
+ controller.connect(node.managementIp(), TpPort.tpPort(ovsdbPort));
+ }
+
+ private Set<String> systemIfaces(OpenstackNode node) {
+ Set<String> ifaces = Sets.newHashSet(DEFAULT_TUNNEL);
+ if (node.type().equals(NodeType.GATEWAY)) {
+ ifaces.add(PATCH_INTG_BRIDGE);
+ ifaces.add(PATCH_ROUT_BRIDGE);
+ }
+ return ifaces;
+ }
+
+ private OpenstackNode nodeByDeviceId(DeviceId deviceId) {
+ OpenstackNode node = nodes().stream()
+ .filter(n -> n.intBridge().equals(deviceId))
+ .findFirst().orElseGet(() -> nodes().stream()
+ .filter(n -> n.routerBridge().isPresent())
+ .filter(n -> n.routerBridge().get().equals(deviceId))
+ .findFirst().orElse(null));
+
+ return node;
+ }
+
+ private class OvsdbHandler implements ConnectionHandler<Device> {
+
+ @Override
+ public void connected(Device device) {
+ OpenstackNode node = nodes().stream()
+ .filter(n -> n.ovsdbId().equals(device.id()))
+ .findFirst()
+ .orElse(null);
+ if (node != null) {
+ setNodeState(node, nodeState(node));
+ } else {
+ log.debug("{} is detected on unregistered node, ignore it.", device.id());
+ }
+ }
+
+ @Override
+ public void disconnected(Device device) {
+ log.debug("Device {} is disconnected", device.id());
+ }
+ }
+
+ private class BridgeHandler implements ConnectionHandler<Device> {
+
+ @Override
+ public void connected(Device device) {
+ OpenstackNode node = nodeByDeviceId(device.id());
+ if (node != null) {
+ setNodeState(node, nodeState(node));
+ } else {
+ log.debug("{} is detected on unregistered node, ignore it.", device.id());
+ }
+ }
+
+ @Override
+ public void disconnected(Device device) {
+ OpenstackNode node = nodeByDeviceId(device.id());
+ if (node != null) {
+ log.warn("Device {} is disconnected", device.id());
+ setNodeState(node, NodeState.INCOMPLETE);
+ }
+ }
+
+ /**
+ * Handles port added situation.
+ * If the added port is tunnel or data plane interface, proceed to the remaining
+ * node initialization. Otherwise, do nothing.
+ *
+ * @param port port
+ */
+ public void portAdded(Port port) {
+ OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
+ String portName = port.annotations().value(PORT_NAME);
+ if (node == null) {
+ log.debug("{} is added to unregistered node, ignore it.", portName);
+ return;
+ }
+
+ log.info("Port {} is added to {}", portName, node.hostname());
+ if (systemIfaces(node).contains(portName)) {
+ setNodeState(node, nodeState(node));
+ }
+ }
+
+ /**
+ * Handles port removed situation.
+ * If the removed port is tunnel or data plane interface, proceed to the remaining
+ * node initialization.Others, do nothing.
+ *
+ * @param port port
+ */
+ public void portRemoved(Port port) {
+ OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
+ String portName = port.annotations().value(PORT_NAME);
+
+ if (node == null) {
+ return;
+ }
+
+ log.info("Port {} is removed from {}", portName, node.hostname());
+ if (systemIfaces(node).contains(portName)) {
+ setNodeState(node, NodeState.INCOMPLETE);
+ }
}
}
@@ -505,11 +573,10 @@
@Override
public void event(DeviceEvent event) {
- NodeId leaderNodeId = leadershipService.getLeader(appId.name());
- //TODO: Fix any node can engage this operation.
- if (!localNodeId.equals(leaderNodeId)) {
- log.debug("Only the leaderNode can process events");
+ NodeId leaderNodeId = leadershipService.getLeader(appId.name());
+ if (!Objects.equals(localNodeId, leaderNodeId)) {
+ // do not allow to proceed without leadership
return;
}
@@ -519,122 +586,47 @@
switch (event.type()) {
case PORT_ADDED:
- eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
+ eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
break;
case PORT_UPDATED:
if (!event.port().isEnabled()) {
- eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
+ eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
}
break;
case DEVICE_ADDED:
case DEVICE_AVAILABILITY_CHANGED:
if (deviceService.isAvailable(device.id())) {
- eventExecutor.submit(() -> handler.connected(device));
+ eventExecutor.execute(() -> handler.connected(device));
} else {
- eventExecutor.submit(() -> handler.disconnected(device));
+ eventExecutor.execute(() -> handler.disconnected(device));
}
break;
default:
- log.debug("Unsupported event type {}", event.type().toString());
break;
}
}
}
- private class OvsdbHandler implements ConnectionHandler<Device> {
-
- @Override
- public void connected(Device device) {
- OpenstackNode node = getNodeByOvsdbId(device.id());
- if (node != null) {
- setNodeState(node, checkNodeState(node));
- }
- }
-
- @Override
- public void disconnected(Device device) {
- if (!deviceService.isAvailable(device.id())) {
- adminService.removeDevice(device.id());
- }
- }
- }
-
- private class BridgeHandler implements ConnectionHandler<Device> {
-
- @Override
- public void connected(Device device) {
- OpenstackNode node = getNodeByBridgeId(device.id());
- if (node != null) {
- setNodeState(node, checkNodeState(node));
- }
- }
-
- @Override
- public void disconnected(Device device) {
- OpenstackNode node = getNodeByBridgeId(device.id());
- if (node != null) {
- log.debug("Integration Bridge is disconnected from {}", node.hostName());
- setNodeState(node, NodeState.INCOMPLETE);
- }
- }
-
- /**
- * Handles port added situation.
- * If the added port is tunnel port, proceed remaining node initialization.
- * Otherwise, do nothing.
- *
- * @param port port
- */
- public void portAdded(Port port) {
- if (!port.annotations().value(PORT_NAME).contains(DEFAULT_TUNNEL)) {
- return;
- }
-
- OpenstackNode node = getNodeByBridgeId((DeviceId) port.element().id());
- if (node != null) {
- setNodeState(node, checkNodeState(node));
- }
- }
-
- /**
- * Handles port removed situation.
- * If the removed port is tunnel port, proceed remaining node initialization.
- * Others, do nothing.
- *
- * @param port port
- */
- public void portRemoved(Port port) {
- if (!port.annotations().value(PORT_NAME).contains(DEFAULT_TUNNEL)) {
- return;
- }
-
- OpenstackNode node = getNodeByBridgeId((DeviceId) port.element().id());
- if (node != null) {
- log.info("Tunnel interface is removed from {}", node.hostName());
- setNodeState(node, NodeState.INCOMPLETE);
- }
- }
- }
-
-
private void readConfiguration() {
- OpenstackNodeConfig config =
- configService.getConfig(appId, OpenstackNodeConfig.class);
-
+ OpenstackNodeConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
if (config == null) {
- log.error("No configuration found");
+ log.debug("No configuration found");
return;
}
-
- config.openstackNodes().stream().forEach(node -> addNode(node));
- log.info("Node configured");
+ config.openstackNodes().forEach(this::addOrUpdateNode);
}
private class InternalConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
- if (!event.configClass().equals(OpenstackNodeConfig.class)) {
+ NodeId leaderNodeId = leadershipService.getLeader(appId.name());
+ if (!Objects.equals(localNodeId, leaderNodeId)) {
+ // do not allow to proceed without leadership
+ return;
+ }
+
+ if (!event.configClass().equals(CONFIG_CLASS)) {
return;
}
@@ -649,6 +641,46 @@
}
}
+ private class InternalMapListener implements MapEventListener<String, OpenstackNode> {
+ @Override
+ public void event(MapEvent<String, OpenstackNode> event) {
+ NodeId leaderNodeId = leadershipService.getLeader(appId.name());
+ if (!Objects.equals(localNodeId, leaderNodeId)) {
+ // do not allow to proceed without leadership
+ return;
+ }
+
+ OpenstackNode oldNode;
+ OpenstackNode newNode;
+
+ switch (event.type()) {
+ case UPDATE:
+ oldNode = event.oldValue().value();
+ newNode = event.newValue().value();
+
+ log.debug("Reloaded {}", newNode.hostname());
+ if (!newNode.equals(oldNode)) {
+ log.debug("New node: {}", newNode);
+ }
+ // performs init procedure even if the node is not changed
+ // for robustness since it's no harm to run init procedure
+ // multiple times
+ eventExecutor.execute(() -> initNode(newNode));
+ break;
+ case INSERT:
+ newNode = event.newValue().value();
+ log.info("Added {}", newNode.hostname());
+ eventExecutor.execute(() -> initNode(newNode));
+ break;
+ case REMOVE:
+ oldNode = event.oldValue().value();
+ log.info("Removed {}", oldNode.hostname());
+ break;
+ default:
+ break;
+ }
+ }
+ }
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
index 7089f8e..1987150 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
@@ -15,26 +15,33 @@
*/
package org.onosproject.openstacknode;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
import java.util.List;
+import java.util.Optional;
+import java.util.Set;
/**
* Handles the bootstrap request for compute/gateway node.
*/
public interface OpenstackNodeService {
- public enum OpenstackNodeType {
+ enum NodeType {
/**
* Compute or Gateway Node.
*/
- COMPUTENODE,
- GATEWAYNODE
+ COMPUTE,
+ GATEWAY
}
+
/**
- * Adds a new node to the service.
+ * Adds or updates a new node to the service.
*
* @param node openstack node
*/
- void addNode(OpenstackNode node);
+ void addOrUpdateNode(OpenstackNode node);
/**
* Deletes a node from the service.
@@ -44,18 +51,58 @@
void deleteNode(OpenstackNode node);
/**
- * Returns nodes known to the service for designated openstacktype.
+ * Returns all nodes known to the service.
*
- * @param openstackNodeType openstack node type
* @return list of nodes
*/
- List<OpenstackNode> getNodes(OpenstackNodeType openstackNodeType);
+ List<OpenstackNode> nodes();
/**
- * Returns the NodeState for a given node.
+ * Returns all nodes in complete state.
*
- * @param node openstack node
- * @return true if the NodeState for a given node is COMPLETE, false otherwise
+ * @return set of nodes
*/
- boolean isComplete(OpenstackNode node);
+ Set<OpenstackNode> completeNodes();
+
+ /**
+ * Returns node initialization state is complete or not.
+ *
+ * @param hostname hostname of the node
+ * @return true if initial node setup is completed, otherwise false
+ */
+ boolean isComplete(String hostname);
+
+ /**
+ * Returns data network IP address of a given integration bridge device.
+ *
+ * @param intBridgeId integration bridge device id
+ * @return ip address; empty value otherwise
+ */
+ Optional<IpAddress> dataIp(DeviceId intBridgeId);
+
+ /**
+ * Returns tunnel port number of a given integration bridge device.
+ *
+ * @param intBridgeId integration bridge device id
+ * @return port number; or empty value
+ */
+ Optional<PortNumber> tunnelPort(DeviceId intBridgeId);
+
+ /**
+ * Returns router bridge device ID connected to a given integration bridge.
+ * It returns valid value only if the node type is GATEWAY.
+ *
+ * @param intBridgeId device id of the integration bridge
+ * @return device id of a router bridge; or empty value
+ */
+ Optional<DeviceId> routerBridge(DeviceId intBridgeId);
+
+ /**
+ * Returns port number connected to the router bridge.
+ * It returns valid value only if the node type is GATEWAY.
+ *
+ * @param intBridgeId integration bridge device id
+ * @return port number; or empty value
+ */
+ Optional<PortNumber> externalPort(DeviceId intBridgeId);
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeState.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeState.java
new file mode 100644
index 0000000..e8fdba9
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeState.java
@@ -0,0 +1,30 @@
+/*
+ * 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.openstacknode;
+
+/**
+ * Entity that defines possible init state of the OpenStack node.
+ */
+public interface OpenstackNodeState {
+ /**
+ * Returns null for no state.
+ *
+ * @return null
+ */
+ static OpenstackNodeState noState() {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java
new file mode 100644
index 0000000..d5cb8ed
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java
@@ -0,0 +1,120 @@
+/*
+ * 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.openstacknode.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.Device;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeService;
+
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.openstacknode.Constants.*;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+
+/**
+ * Checks detailed node init state.
+ */
+@Command(scope = "onos", name = "openstack-node-check",
+ description = "Shows detailed node init state")
+public class OpenstackNodeCheckCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "hostname", description = "Hostname",
+ required = true, multiValued = false)
+ private String hostname = null;
+
+ private static final String MSG_OK = "OK";
+ private static final String MSG_NO = "NO";
+
+ @Override
+ protected void execute() {
+ OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+ DeviceService deviceService = AbstractShellCommand.get(DeviceService.class);
+
+ OpenstackNode node = nodeService.nodes()
+ .stream()
+ .filter(n -> n.hostname().equals(hostname))
+ .findFirst()
+ .orElse(null);
+
+ if (node == null) {
+ print("Cannot find %s from registered nodes", hostname);
+ return;
+ }
+
+ print("%n[Integration Bridge Status]");
+ Device device = deviceService.getDevice(node.intBridge());
+ if (device != null) {
+ print("%s %s=%s available=%s %s",
+ deviceService.isAvailable(device.id()) ? MSG_OK : MSG_NO,
+ INTEGRATION_BRIDGE,
+ device.id(),
+ deviceService.isAvailable(device.id()),
+ device.annotations());
+
+ print(getPortState(deviceService, node.intBridge(), DEFAULT_TUNNEL));
+ } else {
+ print("%s %s=%s is not available",
+ MSG_NO,
+ INTEGRATION_BRIDGE,
+ node.intBridge());
+ }
+
+ if (node.type().equals(GATEWAY)) {
+ print("%n[Router Bridge Status]");
+ device = deviceService.getDevice(node.routerBridge().get());
+ if (device != null) {
+ print("%s %s=%s available=%s %s",
+ deviceService.isAvailable(device.id()) ? MSG_OK : MSG_NO,
+ ROUTER_BRIDGE,
+ device.id(),
+ deviceService.isAvailable(device.id()),
+ device.annotations());
+
+ print(getPortState(deviceService, node.routerBridge().get(), PATCH_ROUT_BRIDGE));
+ print(getPortState(deviceService, node.intBridge(), PATCH_INTG_BRIDGE));
+ } else {
+ print("%s %s=%s is not available",
+ MSG_NO,
+ ROUTER_BRIDGE,
+ node.intBridge());
+ }
+ }
+ }
+
+ private String getPortState(DeviceService deviceService, DeviceId deviceId, String portName) {
+ Port port = deviceService.getPorts(deviceId).stream()
+ .filter(p -> p.annotations().value(PORT_NAME).equals(portName) &&
+ p.isEnabled())
+ .findAny().orElse(null);
+
+ if (port != null) {
+ return String.format("%s %s portNum=%s enabled=%s %s",
+ port.isEnabled() ? MSG_OK : MSG_NO,
+ portName,
+ port.number(),
+ port.isEnabled() ? Boolean.TRUE : Boolean.FALSE,
+ port.annotations());
+ } else {
+ return String.format("%s %s does not exist", MSG_NO, portName);
+ }
+ }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java
new file mode 100644
index 0000000..8f4dbb1
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java
@@ -0,0 +1,57 @@
+/*
+ * 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.openstacknode.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeService;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Initializes nodes for OpenStack node service.
+ */
+@Command(scope = "onos", name = "openstack-node-init",
+ description = "Initializes nodes for OpenStack node service")
+public class OpenstackNodeInitCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "hostnames", description = "Hostname(s)",
+ required = true, multiValued = true)
+ private String[] hostnames = null;
+
+ @Override
+ protected void execute() {
+ OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+
+ for (String hostname : hostnames) {
+ OpenstackNode node;
+ try {
+ node = nodeService.nodes()
+ .stream()
+ .filter(n -> n.hostname().equals(hostname))
+ .findFirst().get();
+ } catch (NoSuchElementException e) {
+ print("Unable to find %s", hostname);
+ continue;
+ }
+
+ nodeService.addOrUpdateNode(node);
+ }
+ }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
new file mode 100644
index 0000000..bfb1070
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
@@ -0,0 +1,82 @@
+/*
+ * 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.openstacknode.cli;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeService;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Lists all nodes registered to the service.
+ */
+@Command(scope = "onos", name = "openstack-nodes",
+ description = "Lists all nodes registered in OpenStack node service")
+public class OpenstackNodeListCommand extends AbstractShellCommand {
+
+ private static final String COMPLETE = "COMPLETE";
+ private static final String INCOMPLETE = "INCOMPLETE";
+
+ @Override
+ protected void execute() {
+ OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+ List<OpenstackNode> nodes = nodeService.nodes();
+ Collections.sort(nodes, OpenstackNode.OPENSTACK_NODE_COMPARATOR);
+
+ if (outputJson()) {
+ print("%s", json(nodeService, nodes));
+ } else {
+ for (OpenstackNode node : nodes) {
+ print("hostname=%s, type=%s, managementIp=%s, dataIp=%s, intBridge=%s, routerBridge=%s init=%s",
+ node.hostname(),
+ node.type(),
+ node.managementIp(),
+ node.dataIp(),
+ node.intBridge(),
+ node.routerBridge(),
+ getState(nodeService, node));
+ }
+ print("Total %s nodes", nodeService.nodes().size());
+ }
+ }
+
+ private JsonNode json(OpenstackNodeService nodeService, List<OpenstackNode> nodes) {
+ ObjectMapper mapper = new ObjectMapper();
+ ArrayNode result = mapper.createArrayNode();
+ for (OpenstackNode node : nodes) {
+ result.add(mapper.createObjectNode()
+ .put("hostname", node.hostname())
+ .put("type", node.type().name())
+ .put("managementIp", node.managementIp().toString())
+ .put("dataIp", node.dataIp().toString())
+ .put("intBridge", node.intBridge().toString())
+ .put("routerBridge", node.routerBridge().toString())
+ .put("state", getState(nodeService, node)));
+ }
+ return result;
+ }
+
+ private String getState(OpenstackNodeService nodeService, OpenstackNode node) {
+ return nodeService.isComplete(node.hostname()) ? COMPLETE : INCOMPLETE;
+ }
+}
\ No newline at end of file
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/package-info.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/package-info.java
new file mode 100644
index 0000000..6a6e636
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 commands to manage OpenStack nodes.
+ */
+package org.onosproject.openstacknode.cli;
\ No newline at end of file
diff --git a/apps/openstacknode/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacknode/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..f6bf14b
--- /dev/null
+++ b/apps/openstacknode/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ 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.openstacknode.cli.OpenstackNodeListCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.openstacknode.cli.OpenstackNodeCheckCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.openstacknode.cli.OpenstackNodeInitCommand"/>
+ </command>
+ </command-bundle>
+</blueprint>