Daniel Park
Committed by Gerrit Code Review

[ONOS-3688] Implement application for Openstack Node bootstrap

- Using OVSDB connnection, it makes bridge and vxlan tunnel for Openstack Nodes.
- Reading nodes information from network config supported

Change-Id: I1c0483b146ace19cd77ac91141182ee9200a9432
1 +{
2 + "apps" : {
3 + "org.onosproject.openstacknode" : {
4 + "openstacknode" : {
5 + "nodes" : [
6 + {
7 + "hostname" : "compute-01",
8 + "ovsdbIp" : "192.168.56.112",
9 + "ovsdbPort" : "6640",
10 + "bridgeId" : "of:0000000000000001",
11 + "openstackNodeType" : "COMPUTENODE"
12 + },
13 + {
14 + "hostname" : "compute-02",
15 + "ovsdbIp" : "192.168.56.106",
16 + "ovsdbPort" : "6640",
17 + "bridgeId" : "of:0000000000000002",
18 + "openstackNodeType" : "COMPUTENODE"
19 + },
20 + {
21 + "hostname" : "network",
22 + "ovsdbIp" : "192.168.56.108",
23 + "ovsdbPort" : "6640",
24 + "bridgeId" : "of:0000000000000003",
25 + "openstackNodeType" : "GATEWAYNODE",
26 + "gatewayExternalInterfaceName" : "eth1",
27 + "gatewayExternalInterfaceMac" : "00:00:00:00:00:10"
28 + }
29 + ]
30 + }
31 + }
32 + }
33 +}
34 +
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2016 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<project xmlns="http://maven.apache.org/POM/4.0.0"
18 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
20 + <modelVersion>4.0.0</modelVersion>
21 +
22 + <parent>
23 + <groupId>org.onosproject</groupId>
24 + <artifactId>onos-apps</artifactId>
25 + <version>1.5.0-SNAPSHOT</version>
26 + <relativePath>../pom.xml</relativePath>
27 + </parent>
28 +
29 + <artifactId>onos-app-openstacknode</artifactId>
30 + <packaging>bundle</packaging>
31 +
32 + <description>SONA Openstack Node Bootstrap Application</description>
33 +
34 +
35 + <properties>
36 + <onos.app.name>org.onosproject.openstacknode</onos.app.name>
37 + <onos.app.category>default</onos.app.category>
38 + <onos.app.url>http://onosproject.org</onos.app.url>
39 + <onos.app.readme>SONA Openstack Node Bootstrap Application</onos.app.readme>
40 + <onos.app.requires>
41 + org.onosproject.ovsdb
42 + </onos.app.requires>
43 + </properties>
44 +
45 + <dependencies>
46 + <dependency>
47 + <groupId>org.osgi</groupId>
48 + <artifactId>org.osgi.compendium</artifactId>
49 + </dependency>
50 + <dependency>
51 + <groupId>org.onosproject</groupId>
52 + <artifactId>onos-api</artifactId>
53 + </dependency>
54 + <dependency>
55 + <groupId>org.onosproject</groupId>
56 + <artifactId>onos-core-serializers</artifactId>
57 + <version>${project.version}</version>
58 + </dependency>
59 + <dependency>
60 + <groupId>org.onosproject</groupId>
61 + <artifactId>onos-ovsdb-api</artifactId>
62 + <version>${project.version}</version>
63 + </dependency>
64 + <dependency>
65 + <groupId>com.fasterxml.jackson.core</groupId>
66 + <artifactId>jackson-databind</artifactId>
67 + </dependency>
68 + <dependency>
69 + <groupId>com.fasterxml.jackson.core</groupId>
70 + <artifactId>jackson-annotations</artifactId>
71 + </dependency>
72 + </dependencies>
73 +</project>
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.openstacknode;
17 +
18 +/**
19 + * Entity capable of handling a subject connected and disconnected situation.
20 + */
21 +public interface ConnectionHandler<T> {
22 + /**
23 + * Processes the connected subject.
24 + *
25 + * @param subject subject
26 + */
27 + void connected(T subject);
28 +
29 + /**
30 + * Processes the disconnected subject.
31 + *
32 + * @param subject subject.
33 + */
34 + void disconnected(T subject);
35 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.openstacknode;
17 +
18 +import com.google.common.base.MoreObjects;
19 +import org.onlab.packet.IpAddress;
20 +import org.onlab.packet.MacAddress;
21 +import org.onlab.packet.TpPort;
22 +import org.onosproject.net.DeviceId;
23 +
24 +import java.util.Comparator;
25 +import java.util.Objects;
26 +
27 +import static com.google.common.base.Preconditions.checkNotNull;
28 +
29 +/**
30 + * Representation of a compute/gateway node for OpenstackSwitching/Routing service.
31 + */
32 +public final class OpenstackNode {
33 +
34 + private final String hostName;
35 + private final IpAddress ovsdbIp;
36 + private final TpPort ovsdbPort;
37 + private final DeviceId bridgeId;
38 + private final OpenstackNodeService.OpenstackNodeType openstackNodeType;
39 + private final String gatewayExternalInterfaceName;
40 + private final MacAddress gatewayExternalInterfaceMac;
41 + private static final String OVSDB = "ovsdb:";
42 +
43 +
44 + public static final Comparator<OpenstackNode> OPENSTACK_NODE_COMPARATOR =
45 + (node1, node2) -> node1.hostName().compareTo(node2.hostName());
46 +
47 + /**
48 + * Creates a new node.
49 + *
50 + * @param hostName hostName
51 + * @param ovsdbIp OVSDB server IP address
52 + * @param ovsdbPort OVSDB server port number
53 + * @param bridgeId integration bridge identifier
54 + * @param openstackNodeType openstack node type
55 + * @param gatewayExternalInterfaceName gatewayExternalInterfaceName
56 + * @param gatewayExternalInterfaceMac gatewayExternalInterfaceMac
57 + */
58 + public OpenstackNode(String hostName, IpAddress ovsdbIp, TpPort ovsdbPort, DeviceId bridgeId,
59 + OpenstackNodeService.OpenstackNodeType openstackNodeType,
60 + String gatewayExternalInterfaceName,
61 + MacAddress gatewayExternalInterfaceMac) {
62 + this.hostName = checkNotNull(hostName, "hostName cannot be null");
63 + this.ovsdbIp = checkNotNull(ovsdbIp, "ovsdbIp cannot be null");
64 + this.ovsdbPort = checkNotNull(ovsdbPort, "ovsdbPort cannot be null");
65 + this.bridgeId = checkNotNull(bridgeId, "bridgeId cannot be null");
66 + this.openstackNodeType = checkNotNull(openstackNodeType, "openstackNodeType cannot be null");
67 + this.gatewayExternalInterfaceName = gatewayExternalInterfaceName;
68 + this.gatewayExternalInterfaceMac = gatewayExternalInterfaceMac;
69 +
70 + if (openstackNodeType == OpenstackNodeService.OpenstackNodeType.GATEWAYNODE) {
71 + checkNotNull(gatewayExternalInterfaceName, "gatewayExternalInterfaceName cannot be null");
72 + checkNotNull(gatewayExternalInterfaceMac, "gatewayExternalInterfaceMac cannot be null");
73 + }
74 + }
75 +
76 + /**
77 + * Returns the OVSDB server IP address.
78 + *
79 + * @return ip address
80 + */
81 + public IpAddress ovsdbIp() {
82 + return this.ovsdbIp;
83 + }
84 +
85 + /**
86 + * Returns the OVSDB server port number.
87 + *
88 + * @return port number
89 + */
90 + public TpPort ovsdbPort() {
91 + return this.ovsdbPort;
92 + }
93 +
94 + /**
95 + * Returns the hostName.
96 + *
97 + * @return hostName
98 + */
99 + public String hostName() {
100 + return this.hostName;
101 + }
102 +
103 + /**
104 + * Returns the identifier of the integration bridge.
105 + *
106 + * @return device id
107 + */
108 + public DeviceId intBrId() {
109 + return this.bridgeId;
110 + }
111 +
112 + /**
113 + * Returns the identifier of the OVSDB device.
114 + *
115 + * @return device id
116 + */
117 + public DeviceId ovsdbId() {
118 + return DeviceId.deviceId(OVSDB.concat(this.ovsdbIp.toString()));
119 + }
120 +
121 + /**
122 + * Returns the openstack node type.
123 + *
124 + * @return openstack node type
125 + */
126 + public OpenstackNodeService.OpenstackNodeType openstackNodeType() {
127 + return this.openstackNodeType;
128 + }
129 +
130 + /**
131 + * Returns the gatewayExternalInterfaceName.
132 + *
133 + * @return gatewayExternalInterfaceName
134 + */
135 + public String gatewayExternalInterfaceName() {
136 + return this.gatewayExternalInterfaceName;
137 + }
138 +
139 + /**
140 + * Returns the gatewayExternalInterfaceMac.
141 + *
142 + * @return gatewayExternalInterfaceMac
143 + */
144 + public MacAddress gatewayExternalInterfaceMac() {
145 + return this.gatewayExternalInterfaceMac;
146 + }
147 +
148 + @Override
149 + public boolean equals(Object obj) {
150 + if (this == obj) {
151 + return true;
152 + }
153 +
154 + if (obj instanceof OpenstackNode) {
155 + OpenstackNode that = (OpenstackNode) obj;
156 +
157 + if (Objects.equals(hostName, that.hostName) &&
158 + Objects.equals(ovsdbIp, that.ovsdbIp) &&
159 + Objects.equals(ovsdbPort, that.ovsdbPort) &&
160 + Objects.equals(bridgeId, that.bridgeId) &&
161 + Objects.equals(openstackNodeType, that.openstackNodeType)) {
162 + return true;
163 + }
164 + }
165 + return false;
166 + }
167 +
168 + @Override
169 + public int hashCode() {
170 + return Objects.hash(hostName, ovsdbIp, ovsdbPort, bridgeId, openstackNodeType);
171 + }
172 +
173 + @Override
174 + public String toString() {
175 + if (openstackNodeType == OpenstackNodeService.OpenstackNodeType.COMPUTENODE) {
176 + return MoreObjects.toStringHelper(getClass())
177 + .add("host", hostName)
178 + .add("ip", ovsdbIp)
179 + .add("port", ovsdbPort)
180 + .add("bridgeId", bridgeId)
181 + .add("openstacknodetype", openstackNodeType)
182 + .toString();
183 + } else {
184 + return MoreObjects.toStringHelper(getClass())
185 + .add("host", hostName)
186 + .add("ip", ovsdbIp)
187 + .add("port", ovsdbPort)
188 + .add("bridgeId", bridgeId)
189 + .add("openstacknodetype", openstackNodeType)
190 + .add("gatewayExternalInterfaceName", gatewayExternalInterfaceName)
191 + .add("gatewayExternalInterfaceMac", gatewayExternalInterfaceMac)
192 + .toString();
193 + }
194 + }
195 +}
196 +
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.openstacknode;
17 +
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.google.common.collect.Sets;
21 +import org.onlab.packet.Ip4Address;
22 +import org.onlab.packet.MacAddress;
23 +import org.onlab.packet.TpPort;
24 +import org.onosproject.core.ApplicationId;
25 +import org.onosproject.net.DeviceId;
26 +import org.slf4j.Logger;
27 +import java.util.Set;
28 +import org.onosproject.net.config.Config;
29 +import static org.slf4j.LoggerFactory.getLogger;
30 +
31 +/**
32 + * Configuration object for OpensatckNode service.
33 + */
34 +public class OpenstackNodeConfig extends Config<ApplicationId> {
35 +
36 + protected final Logger log = getLogger(getClass());
37 +
38 +
39 + public static final String NODES = "nodes";
40 + public static final String HOST_NAME = "hostname";
41 + public static final String OVSDB_IP = "ovsdbIp";
42 + public static final String OVSDB_PORT = "ovsdbPort";
43 + public static final String BRIDGE_ID = "bridgeId";
44 + public static final String NODE_TYPE = "openstackNodeType";
45 + public static final String GATEWAY_EXTERNAL_INTERFACE_NAME = "gatewayExternalInterfaceName";
46 + public static final String GATEWAY_EXTERNAL_INTERFACE_MAC = "gatewayExternalInterfaceMac";
47 +
48 + /**
49 + * Returns the set of nodes read from network config.
50 + *
51 + * @return set of OpensatckNodeConfig or null
52 + */
53 + public Set<OpenstackNode> openstackNodes() {
54 +
55 + Set<OpenstackNode> nodes = Sets.newHashSet();
56 +
57 + JsonNode jsonNodes = object.get(NODES);
58 + if (jsonNodes == null) {
59 + return null;
60 + }
61 +
62 + jsonNodes.forEach(jsonNode -> {
63 + try {
64 + if (OpenstackNodeService.OpenstackNodeType.valueOf(jsonNode.path(NODE_TYPE).asText()) ==
65 + OpenstackNodeService.OpenstackNodeType.COMPUTENODE) {
66 + nodes.add(new OpenstackNode(
67 + jsonNode.path(HOST_NAME).asText(),
68 + Ip4Address.valueOf(jsonNode.path(OVSDB_IP).asText()),
69 + TpPort.tpPort(jsonNode.path(OVSDB_PORT).asInt()),
70 + DeviceId.deviceId(jsonNode.path(BRIDGE_ID).asText()),
71 + OpenstackNodeService.OpenstackNodeType.valueOf(jsonNode.path(NODE_TYPE).asText()),
72 + null, MacAddress.NONE));
73 + } else {
74 + nodes.add(new OpenstackNode(
75 + jsonNode.path(HOST_NAME).asText(),
76 + Ip4Address.valueOf(jsonNode.path(OVSDB_IP).asText()),
77 + TpPort.tpPort(jsonNode.path(OVSDB_PORT).asInt()),
78 + DeviceId.deviceId(jsonNode.path(BRIDGE_ID).asText()),
79 + OpenstackNodeService.OpenstackNodeType.valueOf(jsonNode.path(NODE_TYPE).asText()),
80 + jsonNode.path(GATEWAY_EXTERNAL_INTERFACE_NAME).asText(),
81 + MacAddress.valueOf(jsonNode.path(GATEWAY_EXTERNAL_INTERFACE_MAC).asText())));
82 + }
83 + } catch (IllegalArgumentException | NullPointerException e) {
84 + log.error("Failed to read {}", e.toString());
85 + }
86 + });
87 + return nodes;
88 + }
89 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.openstacknode;
17 +
18 +import com.google.common.collect.ImmutableMap;
19 +import com.google.common.collect.Sets;
20 +import org.apache.felix.scr.annotations.Activate;
21 +import org.apache.felix.scr.annotations.Component;
22 +import org.apache.felix.scr.annotations.Deactivate;
23 +import org.apache.felix.scr.annotations.Reference;
24 +import org.apache.felix.scr.annotations.ReferenceCardinality;
25 +import org.apache.felix.scr.annotations.Service;
26 +import org.onlab.util.ItemNotFoundException;
27 +import org.onlab.util.KryoNamespace;
28 +import org.onosproject.cluster.ClusterService;
29 +import org.onosproject.core.ApplicationId;
30 +import org.onosproject.core.CoreService;
31 +import org.onosproject.net.DefaultAnnotations;
32 +import org.onosproject.net.Device;
33 +import org.onosproject.net.DeviceId;
34 +import org.onosproject.net.Port;
35 +import org.onosproject.net.behaviour.BridgeConfig;
36 +import org.onosproject.net.behaviour.BridgeName;
37 +import org.onosproject.net.behaviour.ControllerInfo;
38 +import org.onosproject.net.behaviour.DefaultTunnelDescription;
39 +import org.onosproject.net.behaviour.TunnelConfig;
40 +import org.onosproject.net.behaviour.TunnelDescription;
41 +import org.onosproject.net.behaviour.TunnelName;
42 +import org.onosproject.net.config.ConfigFactory;
43 +import org.onosproject.net.config.NetworkConfigEvent;
44 +import org.onosproject.net.config.NetworkConfigListener;
45 +import org.onosproject.net.config.NetworkConfigRegistry;
46 +import org.onosproject.net.config.NetworkConfigService;
47 +import org.onosproject.net.config.basics.SubjectFactories;
48 +import org.onosproject.net.device.DeviceAdminService;
49 +import org.onosproject.net.device.DeviceEvent;
50 +import org.onosproject.net.device.DeviceListener;
51 +import org.onosproject.net.device.DeviceService;
52 +import org.onosproject.net.driver.DriverHandler;
53 +import org.onosproject.net.driver.DriverService;
54 +import org.onosproject.ovsdb.controller.OvsdbClientService;
55 +import org.onosproject.ovsdb.controller.OvsdbController;
56 +import org.onosproject.ovsdb.controller.OvsdbNodeId;
57 +import org.onosproject.store.serializers.KryoNamespaces;
58 +import org.onosproject.store.service.ConsistentMap;
59 +import org.onosproject.store.service.Serializer;
60 +import org.onosproject.store.service.StorageService;
61 +import org.slf4j.Logger;
62 +
63 +import static org.onlab.util.Tools.groupedThreads;
64 +import static org.onosproject.net.Device.Type.SWITCH;
65 +import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
66 +import static org.slf4j.LoggerFactory.getLogger;
67 +
68 +import java.util.ArrayList;
69 +import java.util.List;
70 +import java.util.Map;
71 +import java.util.concurrent.ExecutorService;
72 +import java.util.concurrent.Executors;
73 +import java.util.stream.Collectors;
74 +
75 +import static com.google.common.base.Preconditions.checkNotNull;
76 +
77 +
78 +/**
79 + * Initializes devices in compute/gateway nodes according to there type.
80 + */
81 +@Component(immediate = true)
82 +@Service
83 +public class OpenstackNodeManager implements OpenstackNodeService {
84 + protected final Logger log = getLogger(getClass());
85 + private static final int NUM_THREADS = 1;
86 + private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
87 + .register(KryoNamespaces.API)
88 + .register(OpenstackNode.class)
89 + .register(OpenstackNodeType.class)
90 + .register(NodeState.class);
91 + private static final String DEFAULT_BRIDGE = "br-int";
92 + private static final String DEFAULT_TUNNEL = "vxlan";
93 + private static final String PORT_NAME = "portName";
94 + private static final String OPENSTACK_NODESTORE = "openstacknode-nodestore";
95 + private static final String OPENSTACK_NODEMANAGER_ID = "org.onosproject.openstacknode";
96 +
97 + private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS
98 + = ImmutableMap.of("key", "flow", "remote_ip", "flow");
99 +
100 + private static final int DPID_BEGIN = 3;
101 + private static final int OFPORT = 6653;
102 +
103 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 + protected CoreService coreService;
105 +
106 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 + protected DeviceService deviceService;
108 +
109 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 + protected OvsdbController controller;
111 +
112 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 + protected ClusterService clusterService;
114 +
115 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 + protected DriverService driverService;
117 +
118 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 + protected DeviceAdminService adminService;
120 +
121 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 + protected StorageService storageService;
123 +
124 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 + protected NetworkConfigService configService;
126 +
127 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 + protected NetworkConfigRegistry configRegistry;
129 +
130 + private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
131 + private final BridgeHandler bridgeHandler = new BridgeHandler();
132 + private final NetworkConfigListener configListener = new InternalConfigListener();
133 + private final ConfigFactory configFactory =
134 + new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, OpenstackNodeConfig.class, "openstacknode") {
135 + @Override
136 + public OpenstackNodeConfig createConfig() {
137 + return new OpenstackNodeConfig();
138 + }
139 + };
140 +
141 + private final ExecutorService eventExecutor = Executors
142 + .newFixedThreadPool(NUM_THREADS, groupedThreads("onos/openstacknode", "event-handler"));
143 +
144 + private final DeviceListener deviceListener = new InternalDeviceListener();
145 +
146 + private ApplicationId appId;
147 + private ConsistentMap<OpenstackNode, NodeState> nodeStore;
148 +
149 + private enum NodeState {
150 +
151 + INIT {
152 + @Override
153 + public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
154 + openstackNodeManager.connect(node);
155 + }
156 + },
157 + OVSDB_CONNECTED {
158 + @Override
159 + public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
160 + if (!openstackNodeManager.getOvsdbConnectionState(node)) {
161 + openstackNodeManager.connect(node);
162 + } else {
163 + openstackNodeManager.createIntegrationBridge(node);
164 + }
165 + }
166 + },
167 + BRIDGE_CREATED {
168 + @Override
169 + public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
170 + if (!openstackNodeManager.getOvsdbConnectionState(node)) {
171 + openstackNodeManager.connect(node);
172 + } else {
173 + openstackNodeManager.createTunnelInterface(node);
174 + }
175 + }
176 + },
177 + COMPLETE {
178 + @Override
179 + public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
180 + openstackNodeManager.postInit(node);
181 + }
182 + },
183 + INCOMPLETE {
184 + @Override
185 + public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) {
186 + }
187 + };
188 +
189 + public abstract void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node);
190 + }
191 +
192 + @Activate
193 + protected void activate() {
194 + appId = coreService.registerApplication(OPENSTACK_NODEMANAGER_ID);
195 + nodeStore = storageService.<OpenstackNode, NodeState>consistentMapBuilder()
196 + .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
197 + .withName(OPENSTACK_NODESTORE)
198 + .withApplicationId(appId)
199 + .build();
200 +
201 + deviceService.addListener(deviceListener);
202 + configRegistry.registerConfigFactory(configFactory);
203 + configService.addListener(configListener);
204 + readConfiguration();
205 +
206 + log.info("Started");
207 + }
208 +
209 + @Deactivate
210 + protected void deactivate() {
211 + deviceService.removeListener(deviceListener);
212 + eventExecutor.shutdown();
213 + nodeStore.clear();
214 +
215 + configRegistry.unregisterConfigFactory(configFactory);
216 + configService.removeListener(configListener);
217 +
218 + log.info("Stopped");
219 + }
220 +
221 +
222 + @Override
223 + public void addNode(OpenstackNode node) {
224 + checkNotNull(node, "Node cannot be null");
225 +
226 + nodeStore.putIfAbsent(node, checkNodeState(node));
227 +
228 + NodeState state = checkNodeState(node);
229 + state.process(this, node);
230 + }
231 +
232 + @Override
233 + public void deleteNode(OpenstackNode node) {
234 + checkNotNull(node, "Node cannot be null");
235 +
236 + if (getOvsdbConnectionState(node)) {
237 + disconnect(node);
238 + }
239 +
240 + nodeStore.remove(node);
241 + }
242 +
243 + @Override
244 + public List<OpenstackNode> getNodes(OpenstackNodeType openstackNodeType) {
245 + List<OpenstackNode> nodes = new ArrayList<>();
246 + nodes.addAll(nodeStore.keySet().stream().filter(node -> node.openstackNodeType()
247 + .equals(openstackNodeType)).collect(Collectors.toList()));
248 + return nodes;
249 + }
250 +
251 + private List<OpenstackNode> getNodesAll() {
252 + List<OpenstackNode> nodes = new ArrayList<>();
253 + nodes.addAll(nodeStore.keySet());
254 + return nodes;
255 + }
256 +
257 + @Override
258 + public boolean isComplete(OpenstackNode node) {
259 + checkNotNull(node, "Node cannot be null");
260 +
261 + if (!nodeStore.containsKey(node)) {
262 + log.warn("Node {} does not exist", node.hostName());
263 + return false;
264 + } else if (nodeStore.get(node).equals(NodeState.COMPLETE)) {
265 + return true;
266 + }
267 + return false;
268 + }
269 +
270 + /**
271 + * Checks current state of a given openstack node and returns it.
272 + *
273 + * @param node openstack node
274 + * @return node state
275 + */
276 + private NodeState checkNodeState(OpenstackNode node) {
277 + checkNotNull(node, "Node cannot be null");
278 +
279 + if (checkIntegrationBridge(node) && checkTunnelInterface(node)) {
280 + return NodeState.COMPLETE;
281 + } else if (checkIntegrationBridge(node)) {
282 + return NodeState.BRIDGE_CREATED;
283 + } else if (getOvsdbConnectionState(node)) {
284 + return NodeState.OVSDB_CONNECTED;
285 + } else {
286 + return NodeState.INIT;
287 + }
288 + }
289 +
290 +
291 + /**
292 + * Checks if integration bridge exists and available.
293 + *
294 + * @param node openstack node
295 + * @return true if the bridge is available, false otherwise
296 + */
297 + private boolean checkIntegrationBridge(OpenstackNode node) {
298 + return (deviceService.getDevice(node.intBrId()) != null
299 + && deviceService.isAvailable(node.intBrId()));
300 + }
301 + /**
302 + * Checks if tunnel interface exists.
303 + *
304 + * @param node openstack node
305 + * @return true if the interface exists, false otherwise
306 + */
307 + private boolean checkTunnelInterface(OpenstackNode node) {
308 + checkNotNull(node, "Node cannot be null");
309 + return deviceService.getPorts(node.intBrId())
310 + .stream()
311 + .filter(p -> p.annotations().value(PORT_NAME).contains(DEFAULT_TUNNEL) && p.isEnabled())
312 + .findAny().isPresent();
313 + }
314 +
315 + /**
316 + * Returns connection state of OVSDB server for a given node.
317 + *
318 + * @param node openstack node
319 + * @return true if it is connected, false otherwise
320 + */
321 + private boolean getOvsdbConnectionState(OpenstackNode node) {
322 + checkNotNull(node, "Node cannot be null");
323 +
324 + OvsdbClientService ovsdbClient = getOvsdbClient(node);
325 + return deviceService.isAvailable(node.ovsdbId()) &&
326 + ovsdbClient != null && ovsdbClient.isConnected();
327 + }
328 +
329 + /**
330 + * Returns OVSDB client for a given node.
331 + *
332 + * @param node openstack node
333 + * @return OVSDB client, or null if it fails to get OVSDB client
334 + */
335 + private OvsdbClientService getOvsdbClient(OpenstackNode node) {
336 + checkNotNull(node, "Node cannot be null");
337 +
338 + OvsdbClientService ovsdbClient = controller.getOvsdbClient(
339 + new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt()));
340 + if (ovsdbClient == null) {
341 + log.debug("Couldn't find OVSDB client for {}", node.hostName());
342 + }
343 + return ovsdbClient;
344 + }
345 +
346 + /**
347 + * Connects to OVSDB server for a given node.
348 + *
349 + * @param node openstack node
350 + */
351 + private void connect(OpenstackNode node) {
352 + checkNotNull(node, "Node cannot be null");
353 +
354 + if (!nodeStore.containsKey(node)) {
355 + log.warn("Node {} does not exist", node.hostName());
356 + return;
357 + }
358 +
359 + if (!getOvsdbConnectionState(node)) {
360 + controller.connect(node.ovsdbIp(), node.ovsdbPort());
361 + }
362 + }
363 +
364 + /**
365 + * Creates an integration bridge for a given node.
366 + *
367 + * @param node openstack node
368 + */
369 + private void createIntegrationBridge(OpenstackNode node) {
370 + if (checkIntegrationBridge(node)) {
371 + return;
372 + }
373 +
374 + List<ControllerInfo> controllers = new ArrayList<>();
375 + Sets.newHashSet(clusterService.getNodes()).stream()
376 + .forEach(controller -> {
377 + ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
378 + controllers.add(ctrlInfo);
379 + });
380 + String dpid = node.intBrId().toString().substring(DPID_BEGIN);
381 +
382 + try {
383 + DriverHandler handler = driverService.createHandler(node.ovsdbId());
384 + BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
385 + bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
386 + } catch (ItemNotFoundException e) {
387 + log.warn("Failed to create integration bridge on {}", node.ovsdbId());
388 + }
389 + }
390 +
391 + /**
392 + * Creates tunnel interface to the integration bridge for a given node.
393 + *
394 + * @param node openstack node
395 + */
396 + private void createTunnelInterface(OpenstackNode node) {
397 + if (checkTunnelInterface(node)) {
398 + return;
399 + }
400 +
401 + DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
402 + for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
403 + optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
404 + }
405 + TunnelDescription description =
406 + new DefaultTunnelDescription(null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
407 + optionBuilder.build());
408 + try {
409 + DriverHandler handler = driverService.createHandler(node.ovsdbId());
410 + TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
411 + tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
412 + } catch (ItemNotFoundException e) {
413 + log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
414 + }
415 + }
416 +
417 + /**
418 + * Performs tasks after node initialization.
419 + * First disconnect unnecessary OVSDB connection and then installs flow rules
420 + * for existing VMs if there are any.
421 + *
422 + * @param node openstack node
423 + */
424 + private void postInit(OpenstackNode node) {
425 + disconnect(node);
426 + log.info("Finished initializing {}", node.hostName());
427 + }
428 +
429 + /**
430 + * Sets a new state for a given openstack node.
431 + *
432 + * @param node openstack node
433 + * @param newState new node state
434 + */
435 + private void setNodeState(OpenstackNode node, NodeState newState) {
436 + checkNotNull(node, "Node cannot be null");
437 +
438 + log.debug("Changed {} state: {}", node.hostName(), newState.toString());
439 +
440 + nodeStore.put(node, newState);
441 + newState.process(this, node);
442 + }
443 +
444 + /**
445 + * Returns openstack node associated with a given OVSDB device.
446 + *
447 + * @param ovsdbId OVSDB device id
448 + * @return openstack node, null if it fails to find the node
449 + */
450 + private OpenstackNode getNodeByOvsdbId(DeviceId ovsdbId) {
451 +
452 + return getNodesAll().stream()
453 + .filter(node -> node.ovsdbId().equals(ovsdbId))
454 + .findFirst().orElse(null);
455 + }
456 +
457 + /**
458 + * Returns openstack node associated with a given integration bridge.
459 + *
460 + * @param bridgeId device id of integration bridge
461 + * @return openstack node, null if it fails to find the node
462 + */
463 + private OpenstackNode getNodeByBridgeId(DeviceId bridgeId) {
464 + return getNodesAll().stream()
465 + .filter(node -> node.intBrId().equals(bridgeId))
466 + .findFirst().orElse(null);
467 + }
468 + /**
469 + * Disconnects OVSDB server for a given node.
470 + *
471 + * @param node openstack node
472 + */
473 + private void disconnect(OpenstackNode node) {
474 + checkNotNull(node, "Node cannot be null");
475 +
476 + if (!nodeStore.containsKey(node)) {
477 + log.warn("Node {} does not exist", node.hostName());
478 + return;
479 + }
480 +
481 + if (getOvsdbConnectionState(node)) {
482 + OvsdbClientService ovsdbClient = getOvsdbClient(node);
483 + ovsdbClient.disconnect();
484 + }
485 + }
486 +
487 + private class InternalDeviceListener implements DeviceListener {
488 +
489 + @Override
490 + public void event(DeviceEvent event) {
491 +
492 + Device device = event.subject();
493 + ConnectionHandler<Device> handler =
494 + (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
495 +
496 + switch (event.type()) {
497 + case PORT_ADDED:
498 + eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
499 + break;
500 + case PORT_UPDATED:
501 + if (!event.port().isEnabled()) {
502 + eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
503 + }
504 + break;
505 + case DEVICE_ADDED:
506 + case DEVICE_AVAILABILITY_CHANGED:
507 + if (deviceService.isAvailable(device.id())) {
508 + eventExecutor.submit(() -> handler.connected(device));
509 + } else {
510 + eventExecutor.submit(() -> handler.disconnected(device));
511 + }
512 + break;
513 + default:
514 + log.debug("Unsupported event type {}", event.type().toString());
515 + break;
516 + }
517 + }
518 + }
519 +
520 + private class OvsdbHandler implements ConnectionHandler<Device> {
521 +
522 + @Override
523 + public void connected(Device device) {
524 + OpenstackNode node = getNodeByOvsdbId(device.id());
525 + if (node != null) {
526 + setNodeState(node, checkNodeState(node));
527 + }
528 + }
529 +
530 + @Override
531 + public void disconnected(Device device) {
532 + if (!deviceService.isAvailable(device.id())) {
533 + adminService.removeDevice(device.id());
534 + }
535 + }
536 + }
537 +
538 + private class BridgeHandler implements ConnectionHandler<Device> {
539 +
540 + @Override
541 + public void connected(Device device) {
542 + OpenstackNode node = getNodeByBridgeId(device.id());
543 + if (node != null) {
544 + setNodeState(node, checkNodeState(node));
545 + }
546 + }
547 +
548 + @Override
549 + public void disconnected(Device device) {
550 + OpenstackNode node = getNodeByBridgeId(device.id());
551 + if (node != null) {
552 + log.debug("Integration Bridge is disconnected from {}", node.hostName());
553 + setNodeState(node, NodeState.INCOMPLETE);
554 + }
555 + }
556 +
557 + /**
558 + * Handles port added situation.
559 + * If the added port is tunnel port, proceed remaining node initialization.
560 + * Otherwise, do nothing.
561 + *
562 + * @param port port
563 + */
564 + public void portAdded(Port port) {
565 + if (!port.annotations().value(PORT_NAME).contains(DEFAULT_TUNNEL)) {
566 + return;
567 + }
568 +
569 + OpenstackNode node = getNodeByBridgeId((DeviceId) port.element().id());
570 + if (node != null) {
571 + setNodeState(node, checkNodeState(node));
572 + }
573 + }
574 +
575 + /**
576 + * Handles port removed situation.
577 + * If the removed port is tunnel port, proceed remaining node initialization.
578 + * Others, do nothing.
579 + *
580 + * @param port port
581 + */
582 + public void portRemoved(Port port) {
583 + if (!port.annotations().value(PORT_NAME).contains(DEFAULT_TUNNEL)) {
584 + return;
585 + }
586 +
587 + OpenstackNode node = getNodeByBridgeId((DeviceId) port.element().id());
588 + if (node != null) {
589 + log.info("Tunnel interface is removed from {}", node.hostName());
590 + setNodeState(node, NodeState.INCOMPLETE);
591 + }
592 + }
593 + }
594 +
595 +
596 + private void readConfiguration() {
597 + OpenstackNodeConfig config =
598 + configService.getConfig(appId, OpenstackNodeConfig.class);
599 +
600 + if (config == null) {
601 + log.error("No configuration found");
602 + return;
603 + }
604 +
605 + config.openstackNodes().stream().forEach(node -> addNode(node));
606 + log.info("Node configured");
607 + }
608 +
609 + private class InternalConfigListener implements NetworkConfigListener {
610 +
611 + @Override
612 + public void event(NetworkConfigEvent event) {
613 + if (!event.configClass().equals(OpenstackNodeConfig.class)) {
614 + return;
615 + }
616 +
617 + switch (event.type()) {
618 + case CONFIG_ADDED:
619 + case CONFIG_UPDATED:
620 + eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
621 + break;
622 + default:
623 + break;
624 + }
625 + }
626 + }
627 +
628 +
629 +}
630 +
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.openstacknode;
17 +
18 +import java.util.List;
19 +
20 +/**
21 + * Handles the bootstrap request for compute/gateway node.
22 + */
23 +public interface OpenstackNodeService {
24 +
25 + public enum OpenstackNodeType {
26 + /**
27 + * Compute or Gateway Node.
28 + */
29 + COMPUTENODE,
30 + GATEWAYNODE
31 + }
32 + /**
33 + * Adds a new node to the service.
34 + *
35 + * @param node openstack node
36 + */
37 + void addNode(OpenstackNode node);
38 +
39 + /**
40 + * Deletes a node from the service.
41 + *
42 + * @param node openstack node
43 + */
44 + void deleteNode(OpenstackNode node);
45 +
46 + /**
47 + * Returns nodes known to the service for designated openstacktype.
48 + *
49 + * @param openstackNodeType openstack node type
50 + * @return list of nodes
51 + */
52 + List<OpenstackNode> getNodes(OpenstackNodeType openstackNodeType);
53 +
54 + /**
55 + * Returns the NodeState for a given node.
56 + *
57 + * @param node openstack node
58 + * @return true if the NodeState for a given node is COMPLETE, false otherwise
59 + */
60 + boolean isComplete(OpenstackNode node);
61 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Application for bootstrapping Compute/Gateway Node in OpenStack.
19 + */
20 +package org.onosproject.openstacknode;
...\ No newline at end of file ...\ No newline at end of file
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
68 <module>openstackrouting</module> 68 <module>openstackrouting</module>
69 <module>cordmcast</module> 69 <module>cordmcast</module>
70 <module>vpls</module> 70 <module>vpls</module>
71 + <module>openstacknode</module>
71 </modules> 72 </modules>
72 73
73 <properties> 74 <properties>
......