Hyunsun Moon
Committed by Jonathan Hart

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
1 COMPILE_DEPS = [ 1 COMPILE_DEPS = [
2 '//lib:CORE_DEPS', 2 '//lib:CORE_DEPS',
3 + '//lib:org.apache.karaf.shell.console',
4 + '//cli:onos-cli',
3 '//protocols/ovsdb/api:onos-protocols-ovsdb-api', 5 '//protocols/ovsdb/api:onos-protocols-ovsdb-api',
4 '//protocols/ovsdb/rfc:onos-protocols-ovsdb-rfc', 6 '//protocols/ovsdb/rfc:onos-protocols-ovsdb-rfc',
5 '//core/store/serializers:onos-core-serializers', 7 '//core/store/serializers:onos-core-serializers',
...@@ -14,5 +16,5 @@ onos_app ( ...@@ -14,5 +16,5 @@ onos_app (
14 category = 'Utility', 16 category = 'Utility',
15 url = 'http://onosproject.org', 17 url = 'http://onosproject.org',
16 description = 'SONA Openstack Node Bootstrap Application.', 18 description = 'SONA Openstack Node Bootstrap Application.',
17 - required_app = [ 'org.onosproject.ovsdb' ], 19 + required_app = [ 'org.onosproject.ovsdb-base' ],
18 ) 20 )
......
...@@ -5,26 +5,33 @@ ...@@ -5,26 +5,33 @@
5 "nodes" : [ 5 "nodes" : [
6 { 6 {
7 "hostname" : "compute-01", 7 "hostname" : "compute-01",
8 - "ovsdbIp" : "192.168.56.112", 8 + "type" : "COMPUTE",
9 - "ovsdbPort" : "6640", 9 + "managementIp" : "10.203.25.244",
10 - "bridgeId" : "of:0000000000000001", 10 + "dataIp" : "10.134.34.222",
11 - "openstackNodeType" : "COMPUTENODE" 11 + "integrationBridge" : "of:00000000000000a1"
12 }, 12 },
13 { 13 {
14 "hostname" : "compute-02", 14 "hostname" : "compute-02",
15 - "ovsdbIp" : "192.168.56.106", 15 + "type" : "COMPUTE",
16 - "ovsdbPort" : "6640", 16 + "managementIp" : "10.203.229.42",
17 - "bridgeId" : "of:0000000000000002", 17 + "dataIp" : "10.134.34.223",
18 - "openstackNodeType" : "COMPUTENODE" 18 + "integrationBridge" : "of:00000000000000a2"
19 }, 19 },
20 { 20 {
21 - "hostname" : "network", 21 + "hostname" : "gateway-01",
22 - "ovsdbIp" : "192.168.56.108", 22 + "type" : "GATEWAY",
23 - "ovsdbPort" : "6640", 23 + "managementIp" : "10.203.198.125",
24 - "bridgeId" : "of:0000000000000003", 24 + "dataIp" : "10.134.33.208",
25 - "openstackNodeType" : "GATEWAYNODE", 25 + "integrationBridge" : "of:00000000000000a3",
26 - "gatewayExternalInterfaceName" : "eth1", 26 + "routerBridge" : "of:00000000000000b3"
27 - "gatewayExternalInterfaceMac" : "00:00:00:00:00:10" 27 + },
28 + {
29 + "hostname" : "gateway-02",
30 + "type" : "GATEWAY",
31 + "managementIp" : "10.203.198.131",
32 + "dataIp" : "10.134.33.209",
33 + "integrationBridge" : "of:00000000000000a4",
34 + "routerBridge" : "of:00000000000000b4"
28 } 35 }
29 ] 36 ]
30 } 37 }
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
38 <onos.app.url>http://onosproject.org</onos.app.url> 38 <onos.app.url>http://onosproject.org</onos.app.url>
39 <onos.app.readme>SONA Openstack Node Bootstrap Application</onos.app.readme> 39 <onos.app.readme>SONA Openstack Node Bootstrap Application</onos.app.readme>
40 <onos.app.requires> 40 <onos.app.requires>
41 - org.onosproject.ovsdb 41 + org.onosproject.ovsdb-base
42 </onos.app.requires> 42 </onos.app.requires>
43 </properties> 43 </properties>
44 44
...@@ -58,6 +58,11 @@ ...@@ -58,6 +58,11 @@
58 </dependency> 58 </dependency>
59 <dependency> 59 <dependency>
60 <groupId>org.onosproject</groupId> 60 <groupId>org.onosproject</groupId>
61 + <artifactId>onos-cli</artifactId>
62 + <version>${project.version}</version>
63 + </dependency>
64 + <dependency>
65 + <groupId>org.onosproject</groupId>
61 <artifactId>onos-ovsdb-api</artifactId> 66 <artifactId>onos-ovsdb-api</artifactId>
62 <version>${project.version}</version> 67 <version>${project.version}</version>
63 </dependency> 68 </dependency>
...@@ -69,5 +74,10 @@ ...@@ -69,5 +74,10 @@
69 <groupId>com.fasterxml.jackson.core</groupId> 74 <groupId>com.fasterxml.jackson.core</groupId>
70 <artifactId>jackson-annotations</artifactId> 75 <artifactId>jackson-annotations</artifactId>
71 </dependency> 76 </dependency>
77 + <dependency>
78 + <groupId>org.apache.karaf.shell</groupId>
79 + <artifactId>org.apache.karaf.shell.console</artifactId>
80 + <version>3.0.5</version>
81 + </dependency>
72 </dependencies> 82 </dependencies>
73 </project> 83 </project>
......
1 +/*
2 + * Copyright 2016-present 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 + * Provides constants used in OpenStack node services.
20 + */
21 +public final class Constants {
22 +
23 + private Constants() {
24 + }
25 +
26 + public static final String INTEGRATION_BRIDGE = "br-int";
27 + public static final String ROUTER_BRIDGE = "br-router";
28 + public static final String DEFAULT_TUNNEL = "vxlan";
29 + public static final String PATCH_INTG_BRIDGE = "patch-intg";
30 + public static final String PATCH_ROUT_BRIDGE = "patch-rout";
31 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -16,14 +16,16 @@ ...@@ -16,14 +16,16 @@
16 package org.onosproject.openstacknode; 16 package org.onosproject.openstacknode;
17 17
18 import com.google.common.base.MoreObjects; 18 import com.google.common.base.MoreObjects;
19 +import com.google.common.base.Strings;
19 import org.onlab.packet.IpAddress; 20 import org.onlab.packet.IpAddress;
20 -import org.onlab.packet.MacAddress;
21 -import org.onlab.packet.TpPort;
22 import org.onosproject.net.DeviceId; 21 import org.onosproject.net.DeviceId;
22 +import org.onosproject.openstacknode.OpenstackNodeService.NodeType;
23 23
24 import java.util.Comparator; 24 import java.util.Comparator;
25 import java.util.Objects; 25 import java.util.Objects;
26 +import java.util.Optional;
26 27
28 +import static com.google.common.base.Preconditions.checkArgument;
27 import static com.google.common.base.Preconditions.checkNotNull; 29 import static com.google.common.base.Preconditions.checkNotNull;
28 30
29 /** 31 /**
...@@ -31,118 +33,121 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -31,118 +33,121 @@ import static com.google.common.base.Preconditions.checkNotNull;
31 */ 33 */
32 public final class OpenstackNode { 34 public final class OpenstackNode {
33 35
34 - private final String hostName; 36 + private final String hostname;
35 - private final IpAddress ovsdbIp; 37 + private final NodeType type;
36 - private final TpPort ovsdbPort; 38 + private final IpAddress managementIp;
37 - private final DeviceId bridgeId; 39 + private final IpAddress dataIp;
38 - private final OpenstackNodeService.OpenstackNodeType openstackNodeType; 40 + private final DeviceId integrationBridge;
39 - private final String gatewayExternalInterfaceName; 41 + private final Optional<DeviceId> routerBridge;
40 - private final MacAddress gatewayExternalInterfaceMac; 42 + private final OpenstackNodeState state;
41 - private static final String OVSDB = "ovsdb:";
42 -
43 43
44 public static final Comparator<OpenstackNode> OPENSTACK_NODE_COMPARATOR = 44 public static final Comparator<OpenstackNode> OPENSTACK_NODE_COMPARATOR =
45 - (node1, node2) -> node1.hostName().compareTo(node2.hostName()); 45 + (node1, node2) -> node1.hostname().compareTo(node2.hostname());
46 +
47 + private OpenstackNode(String hostname,
48 + NodeType type,
49 + IpAddress managementIp,
50 + IpAddress dataIp,
51 + DeviceId integrationBridge,
52 + Optional<DeviceId> routerBridge,
53 + OpenstackNodeState state) {
54 + this.hostname = hostname;
55 + this.type = type;
56 + this.managementIp = managementIp;
57 + this.dataIp = dataIp;
58 + this.integrationBridge = integrationBridge;
59 + this.routerBridge = routerBridge;
60 + this.state = state;
61 + }
46 62
47 /** 63 /**
48 - * Creates a new node. 64 + * Returns OpenStack node with new state.
49 * 65 *
50 - * @param hostName hostName 66 + * @param node openstack node
51 - * @param ovsdbIp OVSDB server IP address 67 + * @param state openstack node init state
52 - * @param ovsdbPort OVSDB server port number 68 + * @return openstack node
53 - * @param bridgeId integration bridge identifier
54 - * @param openstackNodeType openstack node type
55 - * @param gatewayExternalInterfaceName gatewayExternalInterfaceName
56 - * @param gatewayExternalInterfaceMac gatewayExternalInterfaceMac
57 */ 69 */
58 - public OpenstackNode(String hostName, IpAddress ovsdbIp, TpPort ovsdbPort, DeviceId bridgeId, 70 + public static OpenstackNode getUpdatedNode(OpenstackNode node, OpenstackNodeState state) {
59 - OpenstackNodeService.OpenstackNodeType openstackNodeType, 71 + return new OpenstackNode(node.hostname,
60 - String gatewayExternalInterfaceName, 72 + node.type,
61 - MacAddress gatewayExternalInterfaceMac) { 73 + node.managementIp,
62 - this.hostName = checkNotNull(hostName, "hostName cannot be null"); 74 + node.dataIp,
63 - this.ovsdbIp = checkNotNull(ovsdbIp, "ovsdbIp cannot be null"); 75 + node.integrationBridge,
64 - this.ovsdbPort = checkNotNull(ovsdbPort, "ovsdbPort cannot be null"); 76 + node.routerBridge,
65 - this.bridgeId = checkNotNull(bridgeId, "bridgeId cannot be null"); 77 + state);
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 } 78 }
75 79
76 /** 80 /**
77 - * Returns the OVSDB server IP address. 81 + * Returns hostname of the node.
78 * 82 *
79 - * @return ip address 83 + * @return hostname
80 */ 84 */
81 - public IpAddress ovsdbIp() { 85 + public String hostname() {
82 - return this.ovsdbIp; 86 + return hostname;
83 } 87 }
84 88
85 /** 89 /**
86 - * Returns the OVSDB server port number. 90 + * Returns the type of the node.
87 * 91 *
88 - * @return port number 92 + * @return node type
89 */ 93 */
90 - public TpPort ovsdbPort() { 94 + public NodeType type() {
91 - return this.ovsdbPort; 95 + return type;
92 } 96 }
93 97
94 /** 98 /**
95 - * Returns the hostName. 99 + * Returns the management network IP address of the node.
96 * 100 *
97 - * @return hostName 101 + * @return management network ip address
98 */ 102 */
99 - public String hostName() { 103 + public IpAddress managementIp() {
100 - return this.hostName; 104 + return managementIp;
101 } 105 }
102 106
103 /** 107 /**
104 - * Returns the identifier of the integration bridge. 108 + * Returns the data network IP address of the node.
105 * 109 *
106 - * @return device id 110 + * @return data network ip address
107 */ 111 */
108 - public DeviceId intBrId() { 112 + public IpAddress dataIp() {
109 - return this.bridgeId; 113 + return dataIp;
110 } 114 }
111 115
112 /** 116 /**
113 - * Returns the identifier of the OVSDB device. 117 + * Returns the integration bridge device ID.
114 * 118 *
115 * @return device id 119 * @return device id
116 */ 120 */
117 - public DeviceId ovsdbId() { 121 + public DeviceId intBridge() {
118 - return DeviceId.deviceId(OVSDB.concat(this.ovsdbIp.toString())); 122 + return integrationBridge;
119 } 123 }
120 124
121 /** 125 /**
122 - * Returns the openstack node type. 126 + * Returns the router bridge device ID.
127 + * It returns valid value only if the node type is GATEWAY.
123 * 128 *
124 - * @return openstack node type 129 + * @return device id; or empty device id
125 */ 130 */
126 - public OpenstackNodeService.OpenstackNodeType openstackNodeType() { 131 + public Optional<DeviceId> routerBridge() {
127 - return this.openstackNodeType; 132 + return routerBridge;
128 } 133 }
129 134
130 /** 135 /**
131 - * Returns the gatewayExternalInterfaceName. 136 + * Returns the init state of the node.
132 * 137 *
133 - * @return gatewayExternalInterfaceName 138 + * @return init state
134 */ 139 */
135 - public String gatewayExternalInterfaceName() { 140 + public OpenstackNodeState state() {
136 - return this.gatewayExternalInterfaceName; 141 + return state;
137 } 142 }
138 143
139 /** 144 /**
140 - * Returns the gatewayExternalInterfaceMac. 145 + * Returns the device ID of the OVSDB session of the node.
141 * 146 *
142 - * @return gatewayExternalInterfaceMac 147 + * @return device id
143 */ 148 */
144 - public MacAddress gatewayExternalInterfaceMac() { 149 + public DeviceId ovsdbId() {
145 - return this.gatewayExternalInterfaceMac; 150 + return DeviceId.deviceId("ovsdb:" + managementIp.toString());
146 } 151 }
147 152
148 @Override 153 @Override
...@@ -153,12 +158,12 @@ public final class OpenstackNode { ...@@ -153,12 +158,12 @@ public final class OpenstackNode {
153 158
154 if (obj instanceof OpenstackNode) { 159 if (obj instanceof OpenstackNode) {
155 OpenstackNode that = (OpenstackNode) obj; 160 OpenstackNode that = (OpenstackNode) obj;
156 - 161 + if (Objects.equals(hostname, that.hostname) &&
157 - if (Objects.equals(hostName, that.hostName) && 162 + Objects.equals(type, that.type) &&
158 - Objects.equals(ovsdbIp, that.ovsdbIp) && 163 + Objects.equals(managementIp, that.managementIp) &&
159 - Objects.equals(ovsdbPort, that.ovsdbPort) && 164 + Objects.equals(dataIp, that.dataIp) &&
160 - Objects.equals(bridgeId, that.bridgeId) && 165 + Objects.equals(integrationBridge, that.integrationBridge) &&
161 - Objects.equals(openstackNodeType, that.openstackNodeType)) { 166 + Objects.equals(routerBridge, that.routerBridge)) {
162 return true; 167 return true;
163 } 168 }
164 } 169 }
...@@ -167,30 +172,143 @@ public final class OpenstackNode { ...@@ -167,30 +172,143 @@ public final class OpenstackNode {
167 172
168 @Override 173 @Override
169 public int hashCode() { 174 public int hashCode() {
170 - return Objects.hash(hostName, ovsdbIp, ovsdbPort, bridgeId, openstackNodeType); 175 + return Objects.hash(hostname,
176 + type,
177 + managementIp,
178 + dataIp,
179 + integrationBridge,
180 + routerBridge);
171 } 181 }
172 182
173 @Override 183 @Override
174 public String toString() { 184 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 return MoreObjects.toStringHelper(getClass())
185 - .add("host", hostName) 186 + .add("hostname", hostname)
186 - .add("ip", ovsdbIp) 187 + .add("type", type)
187 - .add("port", ovsdbPort) 188 + .add("managementIp", managementIp)
188 - .add("bridgeId", bridgeId) 189 + .add("dataIp", dataIp)
189 - .add("openstacknodetype", openstackNodeType) 190 + .add("integrationBridge", integrationBridge)
190 - .add("gatewayExternalInterfaceName", gatewayExternalInterfaceName) 191 + .add("routerBridge", routerBridge)
191 - .add("gatewayExternalInterfaceMac", gatewayExternalInterfaceMac) 192 + .add("state", state)
192 .toString(); 193 .toString();
193 } 194 }
195 +
196 + /**
197 + * Returns a new builder instance.
198 + *
199 + * @return openstack node builder
200 + */
201 + public static Builder builder() {
202 + return new Builder();
203 + }
204 +
205 + /**
206 + * Builder of OpenStack node entities.
207 + */
208 + public static final class Builder {
209 + private String hostname;
210 + private NodeType type;
211 + private IpAddress managementIp;
212 + private IpAddress dataIp;
213 + private DeviceId integrationBridge;
214 + private Optional<DeviceId> routerBridge = Optional.empty();
215 + private OpenstackNodeState state = OpenstackNodeState.noState();
216 +
217 + private Builder() {
218 + }
219 +
220 + public OpenstackNode build() {
221 + checkArgument(!Strings.isNullOrEmpty(hostname));
222 + checkNotNull(type);
223 + checkNotNull(managementIp);
224 + checkNotNull(dataIp);
225 + checkNotNull(integrationBridge);
226 + checkNotNull(routerBridge);
227 + return new OpenstackNode(hostname,
228 + type,
229 + managementIp,
230 + dataIp,
231 + integrationBridge,
232 + routerBridge,
233 + state);
234 + }
235 +
236 + /**
237 + * Returns node builder with the hostname.
238 + *
239 + * @param hostname hostname
240 + * @return openstack node builder
241 + */
242 + public Builder hostname(String hostname) {
243 + this.hostname = hostname;
244 + return this;
245 + }
246 +
247 + /**
248 + * Returns node builder with the node type.
249 + *
250 + * @param type openstack node type
251 + * @return openstack node builder
252 + */
253 + public Builder type(NodeType type) {
254 + this.type = type;
255 + return this;
256 + }
257 +
258 + /**
259 + * Returns node builder with the management network IP address.
260 + *
261 + * @param managementIp management ip address
262 + * @return openstack node builder
263 + */
264 + public Builder managementIp(IpAddress managementIp) {
265 + this.managementIp = managementIp;
266 + return this;
267 + }
268 +
269 + /**
270 + * Returns node builder with the data network IP address.
271 + *
272 + * @param dataIp data network ip address
273 + * @return openstack node builder
274 + */
275 + public Builder dataIp(IpAddress dataIp) {
276 + this.dataIp = dataIp;
277 + return this;
278 + }
279 +
280 + /**
281 + * Returns node builder with the integration bridge ID.
282 + *
283 + * @param integrationBridge integration bridge device id
284 + * @return openstack node builder
285 + */
286 + public Builder integrationBridge(DeviceId integrationBridge) {
287 + this.integrationBridge = integrationBridge;
288 + return this;
289 + }
290 +
291 + /**
292 + * Returns node builder with the router bridge ID.
293 + *
294 + * @param routerBridge router bridge device ID
295 + * @return openstack node builder
296 + */
297 + public Builder routerBridge(DeviceId routerBridge) {
298 + this.routerBridge = Optional.ofNullable(routerBridge);
299 + return this;
300 + }
301 +
302 + /**
303 + * Returns node builder with the init state.
304 + *
305 + * @param state node init state
306 + * @return openstack node builder
307 + */
308 + public Builder state(OpenstackNodeState state) {
309 + this.state = state;
310 + return this;
311 + }
194 } 312 }
195 } 313 }
196 314
......
...@@ -17,73 +17,94 @@ package org.onosproject.openstacknode; ...@@ -17,73 +17,94 @@ package org.onosproject.openstacknode;
17 17
18 18
19 import com.fasterxml.jackson.databind.JsonNode; 19 import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 import com.google.common.collect.Sets; 21 import com.google.common.collect.Sets;
21 -import org.onlab.packet.Ip4Address; 22 +import org.onlab.packet.IpAddress;
22 -import org.onlab.packet.MacAddress;
23 -import org.onlab.packet.TpPort;
24 import org.onosproject.core.ApplicationId; 23 import org.onosproject.core.ApplicationId;
25 import org.onosproject.net.DeviceId; 24 import org.onosproject.net.DeviceId;
26 -import org.slf4j.Logger; 25 +import org.onosproject.openstacknode.OpenstackNodeService.NodeType;
27 import java.util.Set; 26 import java.util.Set;
28 import org.onosproject.net.config.Config; 27 import org.onosproject.net.config.Config;
29 -import static org.slf4j.LoggerFactory.getLogger; 28 +
29 +import static org.onosproject.net.config.Config.FieldPresence.MANDATORY;
30 +import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
30 31
31 /** 32 /**
32 * Configuration object for OpensatckNode service. 33 * Configuration object for OpensatckNode service.
33 */ 34 */
34 -public class OpenstackNodeConfig extends Config<ApplicationId> { 35 +public final class OpenstackNodeConfig extends Config<ApplicationId> {
36 +
37 + private static final String NODES = "nodes";
38 + private static final String HOST_NAME = "hostname";
39 + private static final String TYPE = "type";
40 + private static final String MANAGEMENT_IP = "managementIp";
41 + private static final String DATA_IP = "dataIp";
42 + private static final String INTEGRATION_BRIDGE = "integrationBridge";
43 + private static final String ROUTER_BRIDGE = "routerBridge";
35 44
36 - protected final Logger log = getLogger(getClass()); 45 + @Override
46 + public boolean isValid() {
47 + boolean result = hasOnlyFields(NODES);
48 +
49 + if (object.get(NODES) == null || object.get(NODES).size() < 1) {
50 + final String msg = "No node is present";
51 + throw new IllegalArgumentException(msg);
52 + }
37 53
54 + for (JsonNode node : object.get(NODES)) {
55 + ObjectNode osNode = (ObjectNode) node;
56 + result &= hasOnlyFields(osNode,
57 + HOST_NAME,
58 + TYPE,
59 + MANAGEMENT_IP,
60 + DATA_IP,
61 + INTEGRATION_BRIDGE,
62 + ROUTER_BRIDGE
63 + );
38 64
39 - public static final String NODES = "nodes"; 65 + result &= isString(osNode, HOST_NAME, MANDATORY);
40 - public static final String HOST_NAME = "hostname"; 66 + result &= isString(osNode, TYPE, MANDATORY);
41 - public static final String OVSDB_IP = "ovsdbIp"; 67 + result &= isIpAddress(osNode, MANAGEMENT_IP, MANDATORY);
42 - public static final String OVSDB_PORT = "ovsdbPort"; 68 + result &= result && isIpAddress(osNode, DATA_IP, MANDATORY);
43 - public static final String BRIDGE_ID = "bridgeId"; 69 + result &= isString(osNode, INTEGRATION_BRIDGE, MANDATORY);
44 - public static final String NODE_TYPE = "openstackNodeType"; 70 +
45 - public static final String GATEWAY_EXTERNAL_INTERFACE_NAME = "gatewayExternalInterfaceName"; 71 + DeviceId.deviceId(osNode.get(INTEGRATION_BRIDGE).asText());
46 - public static final String GATEWAY_EXTERNAL_INTERFACE_MAC = "gatewayExternalInterfaceMac"; 72 + NodeType.valueOf(osNode.get(TYPE).asText());
73 +
74 + if (osNode.get(TYPE).asText().equals(GATEWAY.name())) {
75 + result &= isString(osNode, ROUTER_BRIDGE, MANDATORY);
76 + DeviceId.deviceId(osNode.get(ROUTER_BRIDGE).asText());
77 + }
78 + }
79 + return result;
80 + }
47 81
48 /** 82 /**
49 * Returns the set of nodes read from network config. 83 * Returns the set of nodes read from network config.
50 * 84 *
51 - * @return set of OpensatckNodeConfig or null 85 + * @return set of openstack nodes
52 */ 86 */
53 public Set<OpenstackNode> openstackNodes() { 87 public Set<OpenstackNode> openstackNodes() {
54 -
55 Set<OpenstackNode> nodes = Sets.newHashSet(); 88 Set<OpenstackNode> nodes = Sets.newHashSet();
56 89
57 - JsonNode jsonNodes = object.get(NODES); 90 + for (JsonNode node : object.get(NODES)) {
58 - if (jsonNodes == null) { 91 + NodeType type = NodeType.valueOf(get(node, TYPE));
59 - return null; 92 + OpenstackNode.Builder nodeBuilder = OpenstackNode.builder()
60 - } 93 + .integrationBridge(DeviceId.deviceId(get(node, INTEGRATION_BRIDGE)))
94 + .dataIp(IpAddress.valueOf(get(node, DATA_IP)))
95 + .managementIp(IpAddress.valueOf(get(node, MANAGEMENT_IP)))
96 + .type(type)
97 + .hostname(get(node, HOST_NAME));
61 98
62 - jsonNodes.forEach(jsonNode -> { 99 + if (type.equals(GATEWAY)) {
63 - try { 100 + nodeBuilder.routerBridge(DeviceId.deviceId(get(node, ROUTER_BRIDGE)));
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 } 101 }
83 - } catch (IllegalArgumentException | NullPointerException e) { 102 + nodes.add(nodeBuilder.build());
84 - log.error("Failed to read {}", e.toString());
85 } 103 }
86 - });
87 return nodes; 104 return nodes;
88 } 105 }
106 +
107 + private String get(JsonNode jsonNode, String path) {
108 + return jsonNode.get(path).asText();
109 + }
89 } 110 }
......
...@@ -15,14 +15,20 @@ ...@@ -15,14 +15,20 @@
15 */ 15 */
16 package org.onosproject.openstacknode; 16 package org.onosproject.openstacknode;
17 17
18 +import com.google.common.collect.Sets;
18 import org.apache.felix.scr.annotations.Activate; 19 import org.apache.felix.scr.annotations.Activate;
19 import org.apache.felix.scr.annotations.Component; 20 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Deactivate; 21 import org.apache.felix.scr.annotations.Deactivate;
22 +import org.apache.felix.scr.annotations.Modified;
23 +import org.apache.felix.scr.annotations.Property;
21 import org.apache.felix.scr.annotations.Reference; 24 import org.apache.felix.scr.annotations.Reference;
22 import org.apache.felix.scr.annotations.ReferenceCardinality; 25 import org.apache.felix.scr.annotations.ReferenceCardinality;
23 import org.apache.felix.scr.annotations.Service; 26 import org.apache.felix.scr.annotations.Service;
24 -import org.onlab.util.ItemNotFoundException; 27 +import org.onlab.packet.IpAddress;
28 +import org.onlab.packet.TpPort;
25 import org.onlab.util.KryoNamespace; 29 import org.onlab.util.KryoNamespace;
30 +import org.onlab.util.Tools;
31 +import org.onosproject.cfg.ComponentConfigService;
26 import org.onosproject.cluster.ClusterService; 32 import org.onosproject.cluster.ClusterService;
27 import org.onosproject.cluster.LeadershipService; 33 import org.onosproject.cluster.LeadershipService;
28 import org.onosproject.cluster.NodeId; 34 import org.onosproject.cluster.NodeId;
...@@ -31,12 +37,15 @@ import org.onosproject.core.CoreService; ...@@ -31,12 +37,15 @@ import org.onosproject.core.CoreService;
31 import org.onosproject.net.Device; 37 import org.onosproject.net.Device;
32 import org.onosproject.net.DeviceId; 38 import org.onosproject.net.DeviceId;
33 import org.onosproject.net.Port; 39 import org.onosproject.net.Port;
40 +import org.onosproject.net.PortNumber;
34 import org.onosproject.net.behaviour.BridgeConfig; 41 import org.onosproject.net.behaviour.BridgeConfig;
35 import org.onosproject.net.behaviour.BridgeDescription; 42 import org.onosproject.net.behaviour.BridgeDescription;
36 import org.onosproject.net.behaviour.ControllerInfo; 43 import org.onosproject.net.behaviour.ControllerInfo;
37 import org.onosproject.net.behaviour.DefaultBridgeDescription; 44 import org.onosproject.net.behaviour.DefaultBridgeDescription;
45 +import org.onosproject.net.behaviour.DefaultPatchDescription;
38 import org.onosproject.net.behaviour.DefaultTunnelDescription; 46 import org.onosproject.net.behaviour.DefaultTunnelDescription;
39 import org.onosproject.net.behaviour.InterfaceConfig; 47 import org.onosproject.net.behaviour.InterfaceConfig;
48 +import org.onosproject.net.behaviour.PatchDescription;
40 import org.onosproject.net.behaviour.TunnelDescription; 49 import org.onosproject.net.behaviour.TunnelDescription;
41 import org.onosproject.net.behaviour.TunnelEndPoints; 50 import org.onosproject.net.behaviour.TunnelEndPoints;
42 import org.onosproject.net.behaviour.TunnelKeys; 51 import org.onosproject.net.behaviour.TunnelKeys;
...@@ -44,57 +53,61 @@ import org.onosproject.net.config.ConfigFactory; ...@@ -44,57 +53,61 @@ import org.onosproject.net.config.ConfigFactory;
44 import org.onosproject.net.config.NetworkConfigEvent; 53 import org.onosproject.net.config.NetworkConfigEvent;
45 import org.onosproject.net.config.NetworkConfigListener; 54 import org.onosproject.net.config.NetworkConfigListener;
46 import org.onosproject.net.config.NetworkConfigRegistry; 55 import org.onosproject.net.config.NetworkConfigRegistry;
47 -import org.onosproject.net.config.NetworkConfigService;
48 import org.onosproject.net.config.basics.SubjectFactories; 56 import org.onosproject.net.config.basics.SubjectFactories;
49 -import org.onosproject.net.device.DeviceAdminService;
50 import org.onosproject.net.device.DeviceEvent; 57 import org.onosproject.net.device.DeviceEvent;
51 import org.onosproject.net.device.DeviceListener; 58 import org.onosproject.net.device.DeviceListener;
52 import org.onosproject.net.device.DeviceService; 59 import org.onosproject.net.device.DeviceService;
53 -import org.onosproject.net.driver.DriverHandler;
54 -import org.onosproject.net.driver.DriverService;
55 import org.onosproject.ovsdb.controller.OvsdbClientService; 60 import org.onosproject.ovsdb.controller.OvsdbClientService;
56 import org.onosproject.ovsdb.controller.OvsdbController; 61 import org.onosproject.ovsdb.controller.OvsdbController;
57 import org.onosproject.ovsdb.controller.OvsdbNodeId; 62 import org.onosproject.ovsdb.controller.OvsdbNodeId;
58 import org.onosproject.store.serializers.KryoNamespaces; 63 import org.onosproject.store.serializers.KryoNamespaces;
59 import org.onosproject.store.service.ConsistentMap; 64 import org.onosproject.store.service.ConsistentMap;
65 +import org.onosproject.store.service.MapEvent;
66 +import org.onosproject.store.service.MapEventListener;
60 import org.onosproject.store.service.Serializer; 67 import org.onosproject.store.service.Serializer;
61 import org.onosproject.store.service.StorageService; 68 import org.onosproject.store.service.StorageService;
69 +import org.onosproject.store.service.Versioned;
70 +import org.osgi.service.component.ComponentContext;
62 import org.slf4j.Logger; 71 import org.slf4j.Logger;
63 72
64 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; 73 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
65 import static org.onlab.util.Tools.groupedThreads; 74 import static org.onlab.util.Tools.groupedThreads;
75 +import static org.onosproject.net.AnnotationKeys.PORT_NAME;
66 import static org.onosproject.net.Device.Type.SWITCH; 76 import static org.onosproject.net.Device.Type.SWITCH;
67 import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN; 77 import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
78 +import static org.onosproject.openstacknode.Constants.*;
68 import static org.slf4j.LoggerFactory.getLogger; 79 import static org.slf4j.LoggerFactory.getLogger;
69 80
70 -import java.util.ArrayList; 81 +import java.util.Dictionary;
71 import java.util.List; 82 import java.util.List;
83 +import java.util.Objects;
84 +import java.util.Optional;
85 +import java.util.Set;
72 import java.util.concurrent.ExecutorService; 86 import java.util.concurrent.ExecutorService;
73 import java.util.stream.Collectors; 87 import java.util.stream.Collectors;
74 88
75 -import static com.google.common.base.Preconditions.checkNotNull;
76 -
77 89
78 /** 90 /**
79 * Initializes devices in compute/gateway nodes according to there type. 91 * Initializes devices in compute/gateway nodes according to there type.
80 */ 92 */
81 @Component(immediate = true) 93 @Component(immediate = true)
82 @Service 94 @Service
83 -public class OpenstackNodeManager implements OpenstackNodeService { 95 +public final class OpenstackNodeManager implements OpenstackNodeService {
84 protected final Logger log = getLogger(getClass()); 96 protected final Logger log = getLogger(getClass());
97 +
85 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder() 98 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
86 .register(KryoNamespaces.API) 99 .register(KryoNamespaces.API)
87 .register(OpenstackNode.class) 100 .register(OpenstackNode.class)
88 - .register(OpenstackNodeType.class) 101 + .register(NodeType.class)
89 .register(NodeState.class); 102 .register(NodeState.class);
90 - private static final String DEFAULT_BRIDGE = "br-int";
91 - private static final String DEFAULT_TUNNEL = "vxlan";
92 - private static final String PORT_NAME = "portName";
93 - private static final String OPENSTACK_NODESTORE = "openstacknode-nodestore";
94 - private static final String OPENSTACK_NODEMANAGER_ID = "org.onosproject.openstacknode";
95 103
104 + private static final String OVSDB_PORT = "ovsdbPort";
105 + private static final int DEFAULT_OVSDB_PORT = 6640;
106 + private static final int DEFAULT_OFPORT = 6653;
96 private static final int DPID_BEGIN = 3; 107 private static final int DPID_BEGIN = 3;
97 - private static final int OFPORT = 6653; 108 +
109 + private static final String APP_ID = "org.onosproject.openstacknode";
110 + private static final Class<OpenstackNodeConfig> CONFIG_CLASS = OpenstackNodeConfig.class;
98 111
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected CoreService coreService; 113 protected CoreService coreService;
...@@ -109,16 +122,10 @@ public class OpenstackNodeManager implements OpenstackNodeService { ...@@ -109,16 +122,10 @@ public class OpenstackNodeManager implements OpenstackNodeService {
109 protected ClusterService clusterService; 122 protected ClusterService clusterService;
110 123
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 - protected DriverService driverService;
113 -
114 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 - protected DeviceAdminService adminService;
116 -
117 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected StorageService storageService; 125 protected StorageService storageService;
119 126
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 - protected NetworkConfigService configService; 128 + protected ComponentConfigService componentConfigService;
122 129
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected NetworkConfigRegistry configRegistry; 131 protected NetworkConfigRegistry configRegistry;
...@@ -126,436 +133,375 @@ public class OpenstackNodeManager implements OpenstackNodeService { ...@@ -126,436 +133,375 @@ public class OpenstackNodeManager implements OpenstackNodeService {
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected LeadershipService leadershipService; 134 protected LeadershipService leadershipService;
128 135
129 - private final OvsdbHandler ovsdbHandler = new OvsdbHandler(); 136 + @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
130 - private final BridgeHandler bridgeHandler = new BridgeHandler(); 137 + label = "OVSDB server listen port")
131 - private final NetworkConfigListener configListener = new InternalConfigListener(); 138 + private int ovsdbPort = DEFAULT_OVSDB_PORT;
139 +
140 + private final ExecutorService eventExecutor =
141 + newSingleThreadScheduledExecutor(groupedThreads("onos/openstack-node", "event-handler"));
142 +
132 private final ConfigFactory configFactory = 143 private final ConfigFactory configFactory =
133 - new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, OpenstackNodeConfig.class, "openstacknode") { 144 + new ConfigFactory<ApplicationId, OpenstackNodeConfig>(
145 + SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "openstacknode") {
134 @Override 146 @Override
135 public OpenstackNodeConfig createConfig() { 147 public OpenstackNodeConfig createConfig() {
136 return new OpenstackNodeConfig(); 148 return new OpenstackNodeConfig();
137 } 149 }
138 }; 150 };
139 151
140 - private final ExecutorService eventExecutor = 152 + private final NetworkConfigListener configListener = new InternalConfigListener();
141 - newSingleThreadScheduledExecutor(groupedThreads("onos/openstacknode", "event-handler", log));
142 -
143 -
144 private final DeviceListener deviceListener = new InternalDeviceListener(); 153 private final DeviceListener deviceListener = new InternalDeviceListener();
154 + private final MapEventListener<String, OpenstackNode> nodeStoreListener = new InternalMapListener();
145 155
156 + private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
157 + private final BridgeHandler bridgeHandler = new BridgeHandler();
158 +
159 + private ConsistentMap<String, OpenstackNode> nodeStore;
146 private ApplicationId appId; 160 private ApplicationId appId;
147 - private ConsistentMap<OpenstackNode, NodeState> nodeStore;
148 private NodeId localNodeId; 161 private NodeId localNodeId;
149 162
150 - private enum NodeState { 163 + private enum NodeState implements OpenstackNodeState {
151 164
152 INIT { 165 INIT {
153 @Override 166 @Override
154 - public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) { 167 + public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
155 - openstackNodeManager.connect(node); 168 + // make sure there is OVSDB connection
169 + if (!nodeManager.isOvsdbConnected(node)) {
170 + nodeManager.connectOvsdb(node);
171 + return;
156 } 172 }
157 - }, 173 + nodeManager.createBridge(node,
158 - OVSDB_CONNECTED { 174 + INTEGRATION_BRIDGE,
159 - @Override 175 + node.intBridge().toString().substring(DPID_BEGIN));
160 - public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) { 176 +
161 - if (!openstackNodeManager.getOvsdbConnectionState(node)) { 177 + // creates additional router bridge if the node type is GATEWAY
162 - openstackNodeManager.connect(node); 178 + if (node.type().equals(NodeType.GATEWAY)) {
163 - } else { 179 + nodeManager.createBridge(node,
164 - openstackNodeManager.createIntegrationBridge(node); 180 + ROUTER_BRIDGE,
181 + node.routerBridge().get().toString().substring(DPID_BEGIN));
165 } 182 }
166 } 183 }
167 }, 184 },
168 BRIDGE_CREATED { 185 BRIDGE_CREATED {
169 @Override 186 @Override
170 - public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) { 187 + public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
171 - if (!openstackNodeManager.getOvsdbConnectionState(node)) { 188 + // make sure there is OVSDB connection
172 - openstackNodeManager.connect(node); 189 + if (!nodeManager.isOvsdbConnected(node)) {
173 - } else { 190 + nodeManager.connectOvsdb(node);
174 - openstackNodeManager.createTunnelInterface(node); 191 + return;
192 + }
193 + nodeManager.createTunnelInterface(node);
194 + // creates additional patch ports connecting integration bridge and
195 + // router bridge if the node type is GATEWAY
196 + if (node.type().equals(NodeType.GATEWAY)) {
197 + nodeManager.createPatchInterface(node);
175 } 198 }
176 } 199 }
177 }, 200 },
178 COMPLETE { 201 COMPLETE {
179 @Override 202 @Override
180 - public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) { 203 + public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
181 - openstackNodeManager.postInit(node); 204 + nodeManager.postInit(node);
182 } 205 }
183 }, 206 },
184 INCOMPLETE { 207 INCOMPLETE {
185 @Override 208 @Override
186 - public void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node) { 209 + public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
187 } 210 }
188 }; 211 };
189 212
190 - public abstract void process(OpenstackNodeManager openstackNodeManager, OpenstackNode node); 213 + public abstract void process(OpenstackNodeManager nodeManager, OpenstackNode node);
191 } 214 }
192 215
193 @Activate 216 @Activate
194 protected void activate() { 217 protected void activate() {
195 - appId = coreService.registerApplication(OPENSTACK_NODEMANAGER_ID); 218 + appId = coreService.getAppId(APP_ID);
219 +
196 localNodeId = clusterService.getLocalNode().id(); 220 localNodeId = clusterService.getLocalNode().id();
197 leadershipService.runForLeadership(appId.name()); 221 leadershipService.runForLeadership(appId.name());
198 222
199 - nodeStore = storageService.<OpenstackNode, NodeState>consistentMapBuilder() 223 + nodeStore = storageService.<String, OpenstackNode>consistentMapBuilder()
200 .withSerializer(Serializer.using(NODE_SERIALIZER.build())) 224 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
201 - .withName(OPENSTACK_NODESTORE) 225 + .withName("openstack-nodestore")
202 .withApplicationId(appId) 226 .withApplicationId(appId)
203 .build(); 227 .build();
204 228
229 + nodeStore.addListener(nodeStoreListener);
205 deviceService.addListener(deviceListener); 230 deviceService.addListener(deviceListener);
231 +
206 configRegistry.registerConfigFactory(configFactory); 232 configRegistry.registerConfigFactory(configFactory);
207 - configService.addListener(configListener); 233 + configRegistry.addListener(configListener);
208 - readConfiguration(); 234 + componentConfigService.registerProperties(getClass());
209 235
210 log.info("Started"); 236 log.info("Started");
211 } 237 }
212 238
213 @Deactivate 239 @Deactivate
214 protected void deactivate() { 240 protected void deactivate() {
241 + configRegistry.removeListener(configListener);
215 deviceService.removeListener(deviceListener); 242 deviceService.removeListener(deviceListener);
216 - eventExecutor.shutdown(); 243 + nodeStore.removeListener(nodeStoreListener);
217 - nodeStore.clear();
218 244
245 + componentConfigService.unregisterProperties(getClass(), true);
219 configRegistry.unregisterConfigFactory(configFactory); 246 configRegistry.unregisterConfigFactory(configFactory);
220 - configService.removeListener(configListener); 247 +
221 leadershipService.withdraw(appId.name()); 248 leadershipService.withdraw(appId.name());
249 + eventExecutor.shutdown();
222 250
223 log.info("Stopped"); 251 log.info("Stopped");
224 } 252 }
225 253
254 + @Modified
255 + protected void modified(ComponentContext context) {
256 + Dictionary<?, ?> properties = context.getProperties();
257 + int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
258 + if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
259 + ovsdbPort = updatedOvsdbPort;
260 + }
226 261
227 - @Override 262 + log.info("Modified");
228 - public void addNode(OpenstackNode node) {
229 - checkNotNull(node, "Node cannot be null");
230 -
231 - NodeId leaderNodeId = leadershipService.getLeader(appId.name());
232 - log.debug("Node init requested, localNodeId: {}, leaderNodeId: {}", localNodeId, leaderNodeId);
233 -
234 - //TODO: Fix any node can engage this operation.
235 - if (!localNodeId.equals(leaderNodeId)) {
236 - log.debug("Only the leaderNode can perform addNode operation");
237 - return;
238 } 263 }
239 - nodeStore.putIfAbsent(node, checkNodeState(node)); 264 +
240 - NodeState state = checkNodeState(node); 265 + @Override
241 - state.process(this, node); 266 + public void addOrUpdateNode(OpenstackNode node) {
267 + nodeStore.put(node.hostname(),
268 + OpenstackNode.getUpdatedNode(node, nodeState(node)));
242 } 269 }
243 270
244 @Override 271 @Override
245 public void deleteNode(OpenstackNode node) { 272 public void deleteNode(OpenstackNode node) {
246 - checkNotNull(node, "Node cannot be null"); 273 + if (isOvsdbConnected(node)) {
247 - 274 + OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
248 - if (getOvsdbConnectionState(node)) { 275 + controller.getOvsdbClient(ovsdb).disconnect();
249 - disconnect(node);
250 } 276 }
251 - 277 + nodeStore.remove(node.hostname());
252 - nodeStore.remove(node);
253 } 278 }
254 279
255 @Override 280 @Override
256 - public List<OpenstackNode> getNodes(OpenstackNodeType openstackNodeType) { 281 + public List<OpenstackNode> nodes() {
257 - List<OpenstackNode> nodes = new ArrayList<>(); 282 + return nodeStore.values().stream().map(Versioned::value).collect(Collectors.toList());
258 - nodes.addAll(nodeStore.keySet().stream().filter(node -> node.openstackNodeType()
259 - .equals(openstackNodeType)).collect(Collectors.toList()));
260 - return nodes;
261 } 283 }
262 284
263 - private List<OpenstackNode> getNodesAll() { 285 + @Override
264 - List<OpenstackNode> nodes = new ArrayList<>(); 286 + public Set<OpenstackNode> completeNodes() {
265 - nodes.addAll(nodeStore.keySet()); 287 + return nodeStore.values().stream().map(Versioned::value)
266 - return nodes; 288 + .filter(node -> node.state().equals(NodeState.COMPLETE))
289 + .collect(Collectors.toSet());
267 } 290 }
268 291
269 @Override 292 @Override
270 - public boolean isComplete(OpenstackNode node) { 293 + public boolean isComplete(String hostname) {
271 - checkNotNull(node, "Node cannot be null"); 294 + Versioned<OpenstackNode> versionedNode = nodeStore.get(hostname);
272 - 295 + if (versionedNode == null) {
273 - if (!nodeStore.containsKey(node)) { 296 + log.warn("Node {} does not exist", hostname);
274 - log.warn("Node {} does not exist", node.hostName());
275 return false; 297 return false;
276 - } else if (nodeStore.get(node).equals(NodeState.COMPLETE)) {
277 - return true;
278 } 298 }
279 - return false; 299 + OpenstackNodeState state = versionedNode.value().state();
300 + return state != null && state.equals(NodeState.COMPLETE);
280 } 301 }
281 302
282 - /** 303 + @Override
283 - * Checks current state of a given openstack node and returns it. 304 + public Optional<IpAddress> dataIp(DeviceId deviceId) {
284 - * 305 + OpenstackNode node = nodeByDeviceId(deviceId);
285 - * @param node openstack node 306 + if (node == null) {
286 - * @return node state 307 + log.warn("Failed to get node for {}", deviceId);
287 - */ 308 + return Optional.empty();
288 - private NodeState checkNodeState(OpenstackNode node) {
289 - checkNotNull(node, "Node cannot be null");
290 -
291 - if (checkIntegrationBridge(node) && checkTunnelInterface(node)) {
292 - return NodeState.COMPLETE;
293 - } else if (checkIntegrationBridge(node)) {
294 - return NodeState.BRIDGE_CREATED;
295 - } else if (getOvsdbConnectionState(node)) {
296 - return NodeState.OVSDB_CONNECTED;
297 - } else {
298 - return NodeState.INIT;
299 } 309 }
310 + return Optional.of(node.dataIp());
300 } 311 }
301 312
313 + @Override
314 + public Optional<PortNumber> tunnelPort(DeviceId deviceId) {
315 + return deviceService.getPorts(deviceId).stream()
316 + .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL) &&
317 + p.isEnabled())
318 + .map(Port::number).findFirst();
319 + }
302 320
303 - /** 321 + @Override
304 - * Checks if integration bridge exists and available. 322 + public Optional<DeviceId> routerBridge(DeviceId intBridgeId) {
305 - * 323 + OpenstackNode node = nodeByDeviceId(intBridgeId);
306 - * @param node openstack node 324 + if (node == null || node.type().equals(NodeType.COMPUTE)) {
307 - * @return true if the bridge is available, false otherwise 325 + log.warn("Failed to find router bridge connected to {}", intBridgeId);
308 - */ 326 + return Optional.empty();
309 - private boolean checkIntegrationBridge(OpenstackNode node) {
310 - return (deviceService.getDevice(node.intBrId()) != null
311 - && deviceService.isAvailable(node.intBrId()));
312 } 327 }
313 - /** 328 + return node.routerBridge();
314 - * Checks if tunnel interface exists.
315 - *
316 - * @param node openstack node
317 - * @return true if the interface exists, false otherwise
318 - */
319 - private boolean checkTunnelInterface(OpenstackNode node) {
320 - checkNotNull(node, "Node cannot be null");
321 - return deviceService.getPorts(node.intBrId())
322 - .stream()
323 - .filter(p -> p.annotations().value(PORT_NAME).contains(DEFAULT_TUNNEL) && p.isEnabled())
324 - .findAny().isPresent();
325 } 329 }
326 330
327 - /** 331 + @Override
328 - * Returns connection state of OVSDB server for a given node. 332 + public Optional<PortNumber> externalPort(DeviceId intBridgeId) {
329 - * 333 + return deviceService.getPorts(intBridgeId).stream()
330 - * @param node openstack node 334 + .filter(p -> p.annotations().value(PORT_NAME).equals(PATCH_INTG_BRIDGE) &&
331 - * @return true if it is connected, false otherwise 335 + p.isEnabled())
332 - */ 336 + .map(Port::number).findFirst();
333 - private boolean getOvsdbConnectionState(OpenstackNode node) { 337 + }
334 - checkNotNull(node, "Node cannot be null");
335 338
336 - OvsdbClientService ovsdbClient = getOvsdbClient(node); 339 + private void initNode(OpenstackNode node) {
337 - return deviceService.isAvailable(node.ovsdbId()) && 340 + NodeState state = (NodeState) node.state();
338 - ovsdbClient != null && ovsdbClient.isConnected(); 341 + state.process(this, node);
342 + log.debug("Processing node: {} state: {}", node.hostname(), state);
339 } 343 }
340 344
341 - /** 345 + private void postInit(OpenstackNode node) {
342 - * Returns OVSDB client for a given node. 346 + if (isOvsdbConnected(node)) {
343 - * 347 + OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
344 - * @param node openstack node 348 + controller.getOvsdbClient(ovsdb).disconnect();
345 - * @return OVSDB client, or null if it fails to get OVSDB client 349 + }
346 - */
347 - private OvsdbClientService getOvsdbClient(OpenstackNode node) {
348 - checkNotNull(node, "Node cannot be null");
349 350
350 - OvsdbClientService ovsdbClient = controller.getOvsdbClient( 351 + // TODO add gateway node to scalable gateway pool
351 - new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt())); 352 + log.info("Finished init {}", node.hostname());
352 - if (ovsdbClient == null) {
353 - log.debug("Couldn't find OVSDB client for {}", node.hostName());
354 } 353 }
355 - return ovsdbClient; 354 +
355 + private void setNodeState(OpenstackNode node, NodeState newState) {
356 + log.debug("Changed {} state: {}", node.hostname(), newState);
357 + nodeStore.put(node.hostname(), OpenstackNode.getUpdatedNode(node, newState));
356 } 358 }
357 359
358 - /** 360 + private NodeState nodeState(OpenstackNode node) {
359 - * Connects to OVSDB server for a given node. 361 + if (!deviceService.isAvailable(node.intBridge())) {
360 - * 362 + return NodeState.INIT;
361 - * @param node openstack node 363 + }
362 - */ 364 + if (node.type().equals(NodeType.GATEWAY) &&
363 - private void connect(OpenstackNode node) { 365 + !deviceService.isAvailable(node.routerBridge().get())) {
364 - checkNotNull(node, "Node cannot be null"); 366 + return NodeState.INIT;
367 + }
365 368
366 - if (!nodeStore.containsKey(node)) { 369 + if (!isIfaceCreated(node.intBridge(), DEFAULT_TUNNEL)) {
367 - log.warn("Node {} does not exist", node.hostName()); 370 + return NodeState.BRIDGE_CREATED;
368 - return; 371 + }
372 + if (node.type().equals(NodeType.GATEWAY) && (
373 + !isIfaceCreated(node.routerBridge().get(), PATCH_ROUT_BRIDGE) ||
374 + !isIfaceCreated(node.intBridge(), PATCH_INTG_BRIDGE))) {
375 + return NodeState.BRIDGE_CREATED;
369 } 376 }
370 377
371 - if (!getOvsdbConnectionState(node)) { 378 + return NodeState.COMPLETE;
372 - controller.connect(node.ovsdbIp(), node.ovsdbPort());
373 } 379 }
380 +
381 + private boolean isIfaceCreated(DeviceId deviceId, String ifaceName) {
382 + return deviceService.getPorts(deviceId).stream()
383 + .filter(p -> p.annotations().value(PORT_NAME).contains(ifaceName) &&
384 + p.isEnabled())
385 + .findAny()
386 + .isPresent();
374 } 387 }
375 388
376 - /** 389 + private void createBridge(OpenstackNode node, String bridgeName, String dpid) {
377 - * Creates an integration bridge for a given node. 390 + Device device = deviceService.getDevice(node.ovsdbId());
378 - * 391 + if (device == null || !device.is(BridgeConfig.class)) {
379 - * @param node openstack node 392 + log.error("Failed to create integration bridge on {}", node.ovsdbId());
380 - */
381 - private void createIntegrationBridge(OpenstackNode node) {
382 - if (checkIntegrationBridge(node)) {
383 return; 393 return;
384 } 394 }
385 395
386 List<ControllerInfo> controllers = clusterService.getNodes().stream() 396 List<ControllerInfo> controllers = clusterService.getNodes().stream()
387 - .map(controller -> new ControllerInfo(controller.ip(), OFPORT, "tcp")) 397 + .map(controller -> new ControllerInfo(controller.ip(), DEFAULT_OFPORT, "tcp"))
388 .collect(Collectors.toList()); 398 .collect(Collectors.toList());
389 - String dpid = node.intBrId().toString().substring(DPID_BEGIN);
390 399
391 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder() 400 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
392 - .name(DEFAULT_BRIDGE) 401 + .name(bridgeName)
393 .failMode(BridgeDescription.FailMode.SECURE) 402 .failMode(BridgeDescription.FailMode.SECURE)
394 .datapathId(dpid) 403 .datapathId(dpid)
395 .disableInBand() 404 .disableInBand()
396 .controllers(controllers) 405 .controllers(controllers)
397 .build(); 406 .build();
398 407
399 - try { 408 + BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
400 - DriverHandler handler = driverService.createHandler(node.ovsdbId());
401 - BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
402 bridgeConfig.addBridge(bridgeDesc); 409 bridgeConfig.addBridge(bridgeDesc);
403 - } catch (ItemNotFoundException e) {
404 - log.warn("Failed to create integration bridge on {}", node.ovsdbId());
405 - }
406 } 410 }
407 411
408 - /**
409 - * Creates tunnel interface to the integration bridge for a given node.
410 - *
411 - * @param node openstack node
412 - */
413 private void createTunnelInterface(OpenstackNode node) { 412 private void createTunnelInterface(OpenstackNode node) {
414 - if (checkTunnelInterface(node)) { 413 + Device device = deviceService.getDevice(node.ovsdbId());
414 + if (device == null || !device.is(InterfaceConfig.class)) {
415 + log.error("Failed to create tunnel interface on {}", node.ovsdbId());
415 return; 416 return;
416 } 417 }
417 418
418 - TunnelDescription description = DefaultTunnelDescription.builder() 419 + TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
419 - .deviceId(DEFAULT_BRIDGE) 420 + .deviceId(INTEGRATION_BRIDGE)
420 .ifaceName(DEFAULT_TUNNEL) 421 .ifaceName(DEFAULT_TUNNEL)
421 .type(VXLAN) 422 .type(VXLAN)
422 .remote(TunnelEndPoints.flowTunnelEndpoint()) 423 .remote(TunnelEndPoints.flowTunnelEndpoint())
423 .key(TunnelKeys.flowTunnelKey()) 424 .key(TunnelKeys.flowTunnelKey())
424 .build(); 425 .build();
425 - try {
426 - DriverHandler handler = driverService.createHandler(node.ovsdbId());
427 - InterfaceConfig ifaceConfig = handler.behaviour(InterfaceConfig.class);
428 - ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, description);
429 - } catch (ItemNotFoundException e) {
430 - log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
431 - }
432 - }
433 426
434 - /** 427 + InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
435 - * Performs tasks after node initialization. 428 + ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
436 - * First disconnect unnecessary OVSDB connection and then installs flow rules
437 - * for existing VMs if there are any.
438 - *
439 - * @param node openstack node
440 - */
441 - private void postInit(OpenstackNode node) {
442 - disconnect(node);
443 - log.info("Finished initializing {}", node.hostName());
444 } 429 }
445 430
446 - /** 431 + private void createPatchInterface(OpenstackNode node) {
447 - * Sets a new state for a given openstack node. 432 + Device device = deviceService.getDevice(node.ovsdbId());
448 - * 433 + if (device == null || !device.is(InterfaceConfig.class)) {
449 - * @param node openstack node 434 + log.error("Failed to create patch interfaces on {}", node.hostname());
450 - * @param newState new node state 435 + return;
451 - */
452 - private void setNodeState(OpenstackNode node, NodeState newState) {
453 - checkNotNull(node, "Node cannot be null");
454 -
455 - log.debug("Changed {} state: {}", node.hostName(), newState.toString());
456 -
457 - nodeStore.put(node, newState);
458 - newState.process(this, node);
459 } 436 }
460 437
461 - /** 438 + PatchDescription patchIntg = DefaultPatchDescription.builder()
462 - * Returns openstack node associated with a given OVSDB device. 439 + .deviceId(INTEGRATION_BRIDGE)
463 - * 440 + .ifaceName(PATCH_INTG_BRIDGE)
464 - * @param ovsdbId OVSDB device id 441 + .peer(PATCH_ROUT_BRIDGE)
465 - * @return openstack node, null if it fails to find the node 442 + .build();
466 - */
467 - private OpenstackNode getNodeByOvsdbId(DeviceId ovsdbId) {
468 443
469 - return getNodesAll().stream() 444 + PatchDescription patchRout = DefaultPatchDescription.builder()
470 - .filter(node -> node.ovsdbId().equals(ovsdbId)) 445 + .deviceId(ROUTER_BRIDGE)
471 - .findFirst().orElse(null); 446 + .ifaceName(PATCH_ROUT_BRIDGE)
472 - } 447 + .peer(PATCH_INTG_BRIDGE)
448 + .build();
473 449
474 - /** 450 + InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
475 - * Returns openstack node associated with a given integration bridge. 451 + ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
476 - * 452 + ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
477 - * @param bridgeId device id of integration bridge
478 - * @return openstack node, null if it fails to find the node
479 - */
480 - private OpenstackNode getNodeByBridgeId(DeviceId bridgeId) {
481 - return getNodesAll().stream()
482 - .filter(node -> node.intBrId().equals(bridgeId))
483 - .findFirst().orElse(null);
484 } 453 }
485 - /**
486 - * Disconnects OVSDB server for a given node.
487 - *
488 - * @param node openstack node
489 - */
490 - private void disconnect(OpenstackNode node) {
491 - checkNotNull(node, "Node cannot be null");
492 454
493 - if (!nodeStore.containsKey(node)) { 455 + private boolean isOvsdbConnected(OpenstackNode node) {
494 - log.warn("Node {} does not exist", node.hostName()); 456 + OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
495 - return; 457 + OvsdbClientService client = controller.getOvsdbClient(ovsdb);
458 + return deviceService.isAvailable(node.ovsdbId()) &&
459 + client != null &&
460 + client.isConnected();
496 } 461 }
497 462
498 - if (getOvsdbConnectionState(node)) { 463 + private void connectOvsdb(OpenstackNode node) {
499 - OvsdbClientService ovsdbClient = getOvsdbClient(node); 464 + controller.connect(node.managementIp(), TpPort.tpPort(ovsdbPort));
500 - ovsdbClient.disconnect();
501 - }
502 } 465 }
503 466
504 - private class InternalDeviceListener implements DeviceListener { 467 + private Set<String> systemIfaces(OpenstackNode node) {
505 - 468 + Set<String> ifaces = Sets.newHashSet(DEFAULT_TUNNEL);
506 - @Override 469 + if (node.type().equals(NodeType.GATEWAY)) {
507 - public void event(DeviceEvent event) { 470 + ifaces.add(PATCH_INTG_BRIDGE);
508 - NodeId leaderNodeId = leadershipService.getLeader(appId.name()); 471 + ifaces.add(PATCH_ROUT_BRIDGE);
509 - 472 + }
510 - //TODO: Fix any node can engage this operation. 473 + return ifaces;
511 - if (!localNodeId.equals(leaderNodeId)) {
512 - log.debug("Only the leaderNode can process events");
513 - return;
514 } 474 }
515 475
516 - Device device = event.subject(); 476 + private OpenstackNode nodeByDeviceId(DeviceId deviceId) {
517 - ConnectionHandler<Device> handler = 477 + OpenstackNode node = nodes().stream()
518 - (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler); 478 + .filter(n -> n.intBridge().equals(deviceId))
479 + .findFirst().orElseGet(() -> nodes().stream()
480 + .filter(n -> n.routerBridge().isPresent())
481 + .filter(n -> n.routerBridge().get().equals(deviceId))
482 + .findFirst().orElse(null));
519 483
520 - switch (event.type()) { 484 + return node;
521 - case PORT_ADDED:
522 - eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
523 - break;
524 - case PORT_UPDATED:
525 - if (!event.port().isEnabled()) {
526 - eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
527 - }
528 - break;
529 - case DEVICE_ADDED:
530 - case DEVICE_AVAILABILITY_CHANGED:
531 - if (deviceService.isAvailable(device.id())) {
532 - eventExecutor.submit(() -> handler.connected(device));
533 - } else {
534 - eventExecutor.submit(() -> handler.disconnected(device));
535 - }
536 - break;
537 - default:
538 - log.debug("Unsupported event type {}", event.type().toString());
539 - break;
540 - }
541 - }
542 } 485 }
543 486
544 private class OvsdbHandler implements ConnectionHandler<Device> { 487 private class OvsdbHandler implements ConnectionHandler<Device> {
545 488
546 @Override 489 @Override
547 public void connected(Device device) { 490 public void connected(Device device) {
548 - OpenstackNode node = getNodeByOvsdbId(device.id()); 491 + OpenstackNode node = nodes().stream()
492 + .filter(n -> n.ovsdbId().equals(device.id()))
493 + .findFirst()
494 + .orElse(null);
549 if (node != null) { 495 if (node != null) {
550 - setNodeState(node, checkNodeState(node)); 496 + setNodeState(node, nodeState(node));
497 + } else {
498 + log.debug("{} is detected on unregistered node, ignore it.", device.id());
551 } 499 }
552 } 500 }
553 501
554 @Override 502 @Override
555 public void disconnected(Device device) { 503 public void disconnected(Device device) {
556 - if (!deviceService.isAvailable(device.id())) { 504 + log.debug("Device {} is disconnected", device.id());
557 - adminService.removeDevice(device.id());
558 - }
559 } 505 }
560 } 506 }
561 507
...@@ -563,78 +509,124 @@ public class OpenstackNodeManager implements OpenstackNodeService { ...@@ -563,78 +509,124 @@ public class OpenstackNodeManager implements OpenstackNodeService {
563 509
564 @Override 510 @Override
565 public void connected(Device device) { 511 public void connected(Device device) {
566 - OpenstackNode node = getNodeByBridgeId(device.id()); 512 + OpenstackNode node = nodeByDeviceId(device.id());
567 if (node != null) { 513 if (node != null) {
568 - setNodeState(node, checkNodeState(node)); 514 + setNodeState(node, nodeState(node));
515 + } else {
516 + log.debug("{} is detected on unregistered node, ignore it.", device.id());
569 } 517 }
570 } 518 }
571 519
572 @Override 520 @Override
573 public void disconnected(Device device) { 521 public void disconnected(Device device) {
574 - OpenstackNode node = getNodeByBridgeId(device.id()); 522 + OpenstackNode node = nodeByDeviceId(device.id());
575 if (node != null) { 523 if (node != null) {
576 - log.debug("Integration Bridge is disconnected from {}", node.hostName()); 524 + log.warn("Device {} is disconnected", device.id());
577 setNodeState(node, NodeState.INCOMPLETE); 525 setNodeState(node, NodeState.INCOMPLETE);
578 } 526 }
579 } 527 }
580 528
581 /** 529 /**
582 * Handles port added situation. 530 * Handles port added situation.
583 - * If the added port is tunnel port, proceed remaining node initialization. 531 + * If the added port is tunnel or data plane interface, proceed to the remaining
584 - * Otherwise, do nothing. 532 + * node initialization. Otherwise, do nothing.
585 * 533 *
586 * @param port port 534 * @param port port
587 */ 535 */
588 public void portAdded(Port port) { 536 public void portAdded(Port port) {
589 - if (!port.annotations().value(PORT_NAME).contains(DEFAULT_TUNNEL)) { 537 + OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
538 + String portName = port.annotations().value(PORT_NAME);
539 + if (node == null) {
540 + log.debug("{} is added to unregistered node, ignore it.", portName);
590 return; 541 return;
591 } 542 }
592 543
593 - OpenstackNode node = getNodeByBridgeId((DeviceId) port.element().id()); 544 + log.info("Port {} is added to {}", portName, node.hostname());
594 - if (node != null) { 545 + if (systemIfaces(node).contains(portName)) {
595 - setNodeState(node, checkNodeState(node)); 546 + setNodeState(node, nodeState(node));
596 } 547 }
597 } 548 }
598 549
599 /** 550 /**
600 * Handles port removed situation. 551 * Handles port removed situation.
601 - * If the removed port is tunnel port, proceed remaining node initialization. 552 + * If the removed port is tunnel or data plane interface, proceed to the remaining
602 - * Others, do nothing. 553 + * node initialization.Others, do nothing.
603 * 554 *
604 * @param port port 555 * @param port port
605 */ 556 */
606 public void portRemoved(Port port) { 557 public void portRemoved(Port port) {
607 - if (!port.annotations().value(PORT_NAME).contains(DEFAULT_TUNNEL)) { 558 + OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
559 + String portName = port.annotations().value(PORT_NAME);
560 +
561 + if (node == null) {
608 return; 562 return;
609 } 563 }
610 564
611 - OpenstackNode node = getNodeByBridgeId((DeviceId) port.element().id()); 565 + log.info("Port {} is removed from {}", portName, node.hostname());
612 - if (node != null) { 566 + if (systemIfaces(node).contains(portName)) {
613 - log.info("Tunnel interface is removed from {}", node.hostName());
614 setNodeState(node, NodeState.INCOMPLETE); 567 setNodeState(node, NodeState.INCOMPLETE);
615 } 568 }
616 } 569 }
617 } 570 }
618 571
572 + private class InternalDeviceListener implements DeviceListener {
619 573
620 - private void readConfiguration() { 574 + @Override
621 - OpenstackNodeConfig config = 575 + public void event(DeviceEvent event) {
622 - configService.getConfig(appId, OpenstackNodeConfig.class);
623 576
624 - if (config == null) { 577 + NodeId leaderNodeId = leadershipService.getLeader(appId.name());
625 - log.error("No configuration found"); 578 + if (!Objects.equals(localNodeId, leaderNodeId)) {
579 + // do not allow to proceed without leadership
626 return; 580 return;
627 } 581 }
628 582
629 - config.openstackNodes().stream().forEach(node -> addNode(node)); 583 + Device device = event.subject();
630 - log.info("Node configured"); 584 + ConnectionHandler<Device> handler =
585 + (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
586 +
587 + switch (event.type()) {
588 + case PORT_ADDED:
589 + eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
590 + break;
591 + case PORT_UPDATED:
592 + if (!event.port().isEnabled()) {
593 + eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
594 + }
595 + break;
596 + case DEVICE_ADDED:
597 + case DEVICE_AVAILABILITY_CHANGED:
598 + if (deviceService.isAvailable(device.id())) {
599 + eventExecutor.execute(() -> handler.connected(device));
600 + } else {
601 + eventExecutor.execute(() -> handler.disconnected(device));
602 + }
603 + break;
604 + default:
605 + break;
606 + }
607 + }
608 + }
609 +
610 + private void readConfiguration() {
611 + OpenstackNodeConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
612 + if (config == null) {
613 + log.debug("No configuration found");
614 + return;
615 + }
616 + config.openstackNodes().forEach(this::addOrUpdateNode);
631 } 617 }
632 618
633 private class InternalConfigListener implements NetworkConfigListener { 619 private class InternalConfigListener implements NetworkConfigListener {
634 620
635 @Override 621 @Override
636 public void event(NetworkConfigEvent event) { 622 public void event(NetworkConfigEvent event) {
637 - if (!event.configClass().equals(OpenstackNodeConfig.class)) { 623 + NodeId leaderNodeId = leadershipService.getLeader(appId.name());
624 + if (!Objects.equals(localNodeId, leaderNodeId)) {
625 + // do not allow to proceed without leadership
626 + return;
627 + }
628 +
629 + if (!event.configClass().equals(CONFIG_CLASS)) {
638 return; 630 return;
639 } 631 }
640 632
...@@ -649,6 +641,46 @@ public class OpenstackNodeManager implements OpenstackNodeService { ...@@ -649,6 +641,46 @@ public class OpenstackNodeManager implements OpenstackNodeService {
649 } 641 }
650 } 642 }
651 643
644 + private class InternalMapListener implements MapEventListener<String, OpenstackNode> {
645 +
646 + @Override
647 + public void event(MapEvent<String, OpenstackNode> event) {
648 + NodeId leaderNodeId = leadershipService.getLeader(appId.name());
649 + if (!Objects.equals(localNodeId, leaderNodeId)) {
650 + // do not allow to proceed without leadership
651 + return;
652 + }
653 +
654 + OpenstackNode oldNode;
655 + OpenstackNode newNode;
652 656
657 + switch (event.type()) {
658 + case UPDATE:
659 + oldNode = event.oldValue().value();
660 + newNode = event.newValue().value();
661 +
662 + log.debug("Reloaded {}", newNode.hostname());
663 + if (!newNode.equals(oldNode)) {
664 + log.debug("New node: {}", newNode);
665 + }
666 + // performs init procedure even if the node is not changed
667 + // for robustness since it's no harm to run init procedure
668 + // multiple times
669 + eventExecutor.execute(() -> initNode(newNode));
670 + break;
671 + case INSERT:
672 + newNode = event.newValue().value();
673 + log.info("Added {}", newNode.hostname());
674 + eventExecutor.execute(() -> initNode(newNode));
675 + break;
676 + case REMOVE:
677 + oldNode = event.oldValue().value();
678 + log.info("Removed {}", oldNode.hostname());
679 + break;
680 + default:
681 + break;
682 + }
683 + }
684 + }
653 } 685 }
654 686
......
...@@ -15,26 +15,33 @@ ...@@ -15,26 +15,33 @@
15 */ 15 */
16 package org.onosproject.openstacknode; 16 package org.onosproject.openstacknode;
17 17
18 +import org.onlab.packet.IpAddress;
19 +import org.onosproject.net.DeviceId;
20 +import org.onosproject.net.PortNumber;
21 +
18 import java.util.List; 22 import java.util.List;
23 +import java.util.Optional;
24 +import java.util.Set;
19 25
20 /** 26 /**
21 * Handles the bootstrap request for compute/gateway node. 27 * Handles the bootstrap request for compute/gateway node.
22 */ 28 */
23 public interface OpenstackNodeService { 29 public interface OpenstackNodeService {
24 30
25 - public enum OpenstackNodeType { 31 + enum NodeType {
26 /** 32 /**
27 * Compute or Gateway Node. 33 * Compute or Gateway Node.
28 */ 34 */
29 - COMPUTENODE, 35 + COMPUTE,
30 - GATEWAYNODE 36 + GATEWAY
31 } 37 }
38 +
32 /** 39 /**
33 - * Adds a new node to the service. 40 + * Adds or updates a new node to the service.
34 * 41 *
35 * @param node openstack node 42 * @param node openstack node
36 */ 43 */
37 - void addNode(OpenstackNode node); 44 + void addOrUpdateNode(OpenstackNode node);
38 45
39 /** 46 /**
40 * Deletes a node from the service. 47 * Deletes a node from the service.
...@@ -44,18 +51,58 @@ public interface OpenstackNodeService { ...@@ -44,18 +51,58 @@ public interface OpenstackNodeService {
44 void deleteNode(OpenstackNode node); 51 void deleteNode(OpenstackNode node);
45 52
46 /** 53 /**
47 - * Returns nodes known to the service for designated openstacktype. 54 + * Returns all nodes known to the service.
48 * 55 *
49 - * @param openstackNodeType openstack node type
50 * @return list of nodes 56 * @return list of nodes
51 */ 57 */
52 - List<OpenstackNode> getNodes(OpenstackNodeType openstackNodeType); 58 + List<OpenstackNode> nodes();
53 59
54 /** 60 /**
55 - * Returns the NodeState for a given node. 61 + * Returns all nodes in complete state.
56 * 62 *
57 - * @param node openstack node 63 + * @return set of nodes
58 - * @return true if the NodeState for a given node is COMPLETE, false otherwise 64 + */
65 + Set<OpenstackNode> completeNodes();
66 +
67 + /**
68 + * Returns node initialization state is complete or not.
69 + *
70 + * @param hostname hostname of the node
71 + * @return true if initial node setup is completed, otherwise false
72 + */
73 + boolean isComplete(String hostname);
74 +
75 + /**
76 + * Returns data network IP address of a given integration bridge device.
77 + *
78 + * @param intBridgeId integration bridge device id
79 + * @return ip address; empty value otherwise
80 + */
81 + Optional<IpAddress> dataIp(DeviceId intBridgeId);
82 +
83 + /**
84 + * Returns tunnel port number of a given integration bridge device.
85 + *
86 + * @param intBridgeId integration bridge device id
87 + * @return port number; or empty value
88 + */
89 + Optional<PortNumber> tunnelPort(DeviceId intBridgeId);
90 +
91 + /**
92 + * Returns router bridge device ID connected to a given integration bridge.
93 + * It returns valid value only if the node type is GATEWAY.
94 + *
95 + * @param intBridgeId device id of the integration bridge
96 + * @return device id of a router bridge; or empty value
97 + */
98 + Optional<DeviceId> routerBridge(DeviceId intBridgeId);
99 +
100 + /**
101 + * Returns port number connected to the router bridge.
102 + * It returns valid value only if the node type is GATEWAY.
103 + *
104 + * @param intBridgeId integration bridge device id
105 + * @return port number; or empty value
59 */ 106 */
60 - boolean isComplete(OpenstackNode node); 107 + Optional<PortNumber> externalPort(DeviceId intBridgeId);
61 } 108 }
......
1 +/*
2 + * Copyright 2016-present 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 that defines possible init state of the OpenStack node.
20 + */
21 +public interface OpenstackNodeState {
22 + /**
23 + * Returns null for no state.
24 + *
25 + * @return null
26 + */
27 + static OpenstackNodeState noState() {
28 + return null;
29 + }
30 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2016-present 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 +package org.onosproject.openstacknode.cli;
18 +
19 +import org.apache.karaf.shell.commands.Argument;
20 +import org.apache.karaf.shell.commands.Command;
21 +import org.onosproject.cli.AbstractShellCommand;
22 +import org.onosproject.net.DeviceId;
23 +import org.onosproject.net.Port;
24 +import org.onosproject.net.Device;
25 +import org.onosproject.net.device.DeviceService;
26 +import org.onosproject.openstacknode.OpenstackNode;
27 +import org.onosproject.openstacknode.OpenstackNodeService;
28 +
29 +import static org.onosproject.net.AnnotationKeys.PORT_NAME;
30 +import static org.onosproject.openstacknode.Constants.*;
31 +import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
32 +
33 +/**
34 + * Checks detailed node init state.
35 + */
36 +@Command(scope = "onos", name = "openstack-node-check",
37 + description = "Shows detailed node init state")
38 +public class OpenstackNodeCheckCommand extends AbstractShellCommand {
39 +
40 + @Argument(index = 0, name = "hostname", description = "Hostname",
41 + required = true, multiValued = false)
42 + private String hostname = null;
43 +
44 + private static final String MSG_OK = "OK";
45 + private static final String MSG_NO = "NO";
46 +
47 + @Override
48 + protected void execute() {
49 + OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
50 + DeviceService deviceService = AbstractShellCommand.get(DeviceService.class);
51 +
52 + OpenstackNode node = nodeService.nodes()
53 + .stream()
54 + .filter(n -> n.hostname().equals(hostname))
55 + .findFirst()
56 + .orElse(null);
57 +
58 + if (node == null) {
59 + print("Cannot find %s from registered nodes", hostname);
60 + return;
61 + }
62 +
63 + print("%n[Integration Bridge Status]");
64 + Device device = deviceService.getDevice(node.intBridge());
65 + if (device != null) {
66 + print("%s %s=%s available=%s %s",
67 + deviceService.isAvailable(device.id()) ? MSG_OK : MSG_NO,
68 + INTEGRATION_BRIDGE,
69 + device.id(),
70 + deviceService.isAvailable(device.id()),
71 + device.annotations());
72 +
73 + print(getPortState(deviceService, node.intBridge(), DEFAULT_TUNNEL));
74 + } else {
75 + print("%s %s=%s is not available",
76 + MSG_NO,
77 + INTEGRATION_BRIDGE,
78 + node.intBridge());
79 + }
80 +
81 + if (node.type().equals(GATEWAY)) {
82 + print("%n[Router Bridge Status]");
83 + device = deviceService.getDevice(node.routerBridge().get());
84 + if (device != null) {
85 + print("%s %s=%s available=%s %s",
86 + deviceService.isAvailable(device.id()) ? MSG_OK : MSG_NO,
87 + ROUTER_BRIDGE,
88 + device.id(),
89 + deviceService.isAvailable(device.id()),
90 + device.annotations());
91 +
92 + print(getPortState(deviceService, node.routerBridge().get(), PATCH_ROUT_BRIDGE));
93 + print(getPortState(deviceService, node.intBridge(), PATCH_INTG_BRIDGE));
94 + } else {
95 + print("%s %s=%s is not available",
96 + MSG_NO,
97 + ROUTER_BRIDGE,
98 + node.intBridge());
99 + }
100 + }
101 + }
102 +
103 + private String getPortState(DeviceService deviceService, DeviceId deviceId, String portName) {
104 + Port port = deviceService.getPorts(deviceId).stream()
105 + .filter(p -> p.annotations().value(PORT_NAME).equals(portName) &&
106 + p.isEnabled())
107 + .findAny().orElse(null);
108 +
109 + if (port != null) {
110 + return String.format("%s %s portNum=%s enabled=%s %s",
111 + port.isEnabled() ? MSG_OK : MSG_NO,
112 + portName,
113 + port.number(),
114 + port.isEnabled() ? Boolean.TRUE : Boolean.FALSE,
115 + port.annotations());
116 + } else {
117 + return String.format("%s %s does not exist", MSG_NO, portName);
118 + }
119 + }
120 +}
1 +/*
2 + * Copyright 2016-present 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 +package org.onosproject.openstacknode.cli;
18 +
19 +import org.apache.karaf.shell.commands.Argument;
20 +import org.apache.karaf.shell.commands.Command;
21 +import org.onosproject.cli.AbstractShellCommand;
22 +import org.onosproject.openstacknode.OpenstackNode;
23 +import org.onosproject.openstacknode.OpenstackNodeService;
24 +
25 +import java.util.NoSuchElementException;
26 +
27 +/**
28 + * Initializes nodes for OpenStack node service.
29 + */
30 +@Command(scope = "onos", name = "openstack-node-init",
31 + description = "Initializes nodes for OpenStack node service")
32 +public class OpenstackNodeInitCommand extends AbstractShellCommand {
33 +
34 + @Argument(index = 0, name = "hostnames", description = "Hostname(s)",
35 + required = true, multiValued = true)
36 + private String[] hostnames = null;
37 +
38 + @Override
39 + protected void execute() {
40 + OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
41 +
42 + for (String hostname : hostnames) {
43 + OpenstackNode node;
44 + try {
45 + node = nodeService.nodes()
46 + .stream()
47 + .filter(n -> n.hostname().equals(hostname))
48 + .findFirst().get();
49 + } catch (NoSuchElementException e) {
50 + print("Unable to find %s", hostname);
51 + continue;
52 + }
53 +
54 + nodeService.addOrUpdateNode(node);
55 + }
56 + }
57 +}
1 +/*
2 + * Copyright 2016-present 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 +package org.onosproject.openstacknode.cli;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.ObjectMapper;
21 +import com.fasterxml.jackson.databind.node.ArrayNode;
22 +import org.apache.karaf.shell.commands.Command;
23 +import org.onosproject.cli.AbstractShellCommand;
24 +import org.onosproject.openstacknode.OpenstackNode;
25 +import org.onosproject.openstacknode.OpenstackNodeService;
26 +
27 +import java.util.Collections;
28 +import java.util.List;
29 +
30 +/**
31 + * Lists all nodes registered to the service.
32 + */
33 +@Command(scope = "onos", name = "openstack-nodes",
34 + description = "Lists all nodes registered in OpenStack node service")
35 +public class OpenstackNodeListCommand extends AbstractShellCommand {
36 +
37 + private static final String COMPLETE = "COMPLETE";
38 + private static final String INCOMPLETE = "INCOMPLETE";
39 +
40 + @Override
41 + protected void execute() {
42 + OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
43 + List<OpenstackNode> nodes = nodeService.nodes();
44 + Collections.sort(nodes, OpenstackNode.OPENSTACK_NODE_COMPARATOR);
45 +
46 + if (outputJson()) {
47 + print("%s", json(nodeService, nodes));
48 + } else {
49 + for (OpenstackNode node : nodes) {
50 + print("hostname=%s, type=%s, managementIp=%s, dataIp=%s, intBridge=%s, routerBridge=%s init=%s",
51 + node.hostname(),
52 + node.type(),
53 + node.managementIp(),
54 + node.dataIp(),
55 + node.intBridge(),
56 + node.routerBridge(),
57 + getState(nodeService, node));
58 + }
59 + print("Total %s nodes", nodeService.nodes().size());
60 + }
61 + }
62 +
63 + private JsonNode json(OpenstackNodeService nodeService, List<OpenstackNode> nodes) {
64 + ObjectMapper mapper = new ObjectMapper();
65 + ArrayNode result = mapper.createArrayNode();
66 + for (OpenstackNode node : nodes) {
67 + result.add(mapper.createObjectNode()
68 + .put("hostname", node.hostname())
69 + .put("type", node.type().name())
70 + .put("managementIp", node.managementIp().toString())
71 + .put("dataIp", node.dataIp().toString())
72 + .put("intBridge", node.intBridge().toString())
73 + .put("routerBridge", node.routerBridge().toString())
74 + .put("state", getState(nodeService, node)));
75 + }
76 + return result;
77 + }
78 +
79 + private String getState(OpenstackNodeService nodeService, OpenstackNode node) {
80 + return nodeService.isComplete(node.hostname()) ? COMPLETE : INCOMPLETE;
81 + }
82 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2016-present 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 + * Console commands to manage OpenStack nodes.
19 + */
20 +package org.onosproject.openstacknode.cli;
...\ No newline at end of file ...\ No newline at end of file
1 +<!--
2 + ~ Copyright 2016-present 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 +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
17 +
18 + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
19 + <command>
20 + <action class="org.onosproject.openstacknode.cli.OpenstackNodeListCommand"/>
21 + </command>
22 + <command>
23 + <action class="org.onosproject.openstacknode.cli.OpenstackNodeCheckCommand"/>
24 + </command>
25 + <command>
26 + <action class="org.onosproject.openstacknode.cli.OpenstackNodeInitCommand"/>
27 + </command>
28 + </command-bundle>
29 +</blueprint>