blob: 039c3eddab4992560f7d5c14c3d615f6f21ac322 [file] [log] [blame]
Daniel Parka7d6e9f2016-01-18 17:54:14 +09001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Daniel Parka7d6e9f2016-01-18 17:54:14 +09003 *
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 */
16package org.onosproject.openstacknode;
17
Hyunsun Moon34bbe172016-06-28 19:18:40 -070018import com.google.common.collect.Sets;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070022import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090024import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070027import org.onlab.packet.IpAddress;
28import org.onlab.packet.TpPort;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090029import org.onlab.util.KryoNamespace;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070030import org.onlab.util.Tools;
31import org.onosproject.cfg.ComponentConfigService;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090032import org.onosproject.cluster.ClusterService;
Daniel Parkad21c572016-03-09 10:18:24 +090033import org.onosproject.cluster.LeadershipService;
34import org.onosproject.cluster.NodeId;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090035import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090037import org.onosproject.net.Device;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.Port;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070040import org.onosproject.net.PortNumber;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090041import org.onosproject.net.behaviour.BridgeConfig;
Hyunsun Moon1251e192016-06-07 16:57:05 -070042import org.onosproject.net.behaviour.BridgeDescription;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090043import org.onosproject.net.behaviour.ControllerInfo;
Hyunsun Moon1251e192016-06-07 16:57:05 -070044import org.onosproject.net.behaviour.DefaultBridgeDescription;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070045import org.onosproject.net.behaviour.DefaultPatchDescription;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090046import org.onosproject.net.behaviour.DefaultTunnelDescription;
Hyunsun Moondd14e8e2016-06-09 16:17:32 -070047import org.onosproject.net.behaviour.InterfaceConfig;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070048import org.onosproject.net.behaviour.PatchDescription;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090049import org.onosproject.net.behaviour.TunnelDescription;
Hyunsun Moondd14e8e2016-06-09 16:17:32 -070050import org.onosproject.net.behaviour.TunnelEndPoints;
51import org.onosproject.net.behaviour.TunnelKeys;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090052import org.onosproject.net.config.ConfigFactory;
53import org.onosproject.net.config.NetworkConfigEvent;
54import org.onosproject.net.config.NetworkConfigListener;
55import org.onosproject.net.config.NetworkConfigRegistry;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090056import org.onosproject.net.config.basics.SubjectFactories;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090057import org.onosproject.net.device.DeviceEvent;
58import org.onosproject.net.device.DeviceListener;
59import org.onosproject.net.device.DeviceService;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090060import org.onosproject.ovsdb.controller.OvsdbClientService;
61import org.onosproject.ovsdb.controller.OvsdbController;
62import org.onosproject.ovsdb.controller.OvsdbNodeId;
63import org.onosproject.store.serializers.KryoNamespaces;
64import org.onosproject.store.service.ConsistentMap;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070065import org.onosproject.store.service.MapEvent;
66import org.onosproject.store.service.MapEventListener;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090067import org.onosproject.store.service.Serializer;
68import org.onosproject.store.service.StorageService;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070069import org.onosproject.store.service.Versioned;
70import org.osgi.service.component.ComponentContext;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090071import org.slf4j.Logger;
72
Daniel Parkad21c572016-03-09 10:18:24 +090073import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090074import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070075import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090076import static org.onosproject.net.Device.Type.SWITCH;
77import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070078import static org.onosproject.openstacknode.Constants.*;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090079import static org.slf4j.LoggerFactory.getLogger;
80
Hyunsun Moon34bbe172016-06-28 19:18:40 -070081import java.util.Dictionary;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090082import java.util.List;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070083import java.util.Objects;
84import java.util.Optional;
85import java.util.Set;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090086import java.util.concurrent.ExecutorService;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090087import java.util.stream.Collectors;
88
Daniel Parka7d6e9f2016-01-18 17:54:14 +090089
90/**
91 * Initializes devices in compute/gateway nodes according to there type.
92 */
93@Component(immediate = true)
94@Service
Hyunsun Moon34bbe172016-06-28 19:18:40 -070095public final class OpenstackNodeManager implements OpenstackNodeService {
Daniel Parka7d6e9f2016-01-18 17:54:14 +090096 protected final Logger log = getLogger(getClass());
Hyunsun Moon34bbe172016-06-28 19:18:40 -070097
Daniel Parka7d6e9f2016-01-18 17:54:14 +090098 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
99 .register(KryoNamespaces.API)
100 .register(OpenstackNode.class)
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700101 .register(NodeType.class)
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900102 .register(NodeState.class);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900103
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700104 private static final String OVSDB_PORT = "ovsdbPort";
105 private static final int DEFAULT_OVSDB_PORT = 6640;
106 private static final int DEFAULT_OFPORT = 6653;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900107 private static final int DPID_BEGIN = 3;
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700108
109 private static final String APP_ID = "org.onosproject.openstacknode";
110 private static final Class<OpenstackNodeConfig> CONFIG_CLASS = OpenstackNodeConfig.class;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected CoreService coreService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected DeviceService deviceService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected OvsdbController controller;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected ClusterService clusterService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900125 protected StorageService storageService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700128 protected ComponentConfigService componentConfigService;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected NetworkConfigRegistry configRegistry;
132
Daniel Parkad21c572016-03-09 10:18:24 +0900133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected LeadershipService leadershipService;
135
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700136 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
137 label = "OVSDB server listen port")
138 private int ovsdbPort = DEFAULT_OVSDB_PORT;
139
140 private final ExecutorService eventExecutor =
141 newSingleThreadScheduledExecutor(groupedThreads("onos/openstack-node", "event-handler"));
142
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900143 private final ConfigFactory configFactory =
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700144 new ConfigFactory<ApplicationId, OpenstackNodeConfig>(
145 SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "openstacknode") {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900146 @Override
147 public OpenstackNodeConfig createConfig() {
148 return new OpenstackNodeConfig();
149 }
150 };
151
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700152 private final NetworkConfigListener configListener = new InternalConfigListener();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900153 private final DeviceListener deviceListener = new InternalDeviceListener();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700154 private final MapEventListener<String, OpenstackNode> nodeStoreListener = new InternalMapListener();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900155
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700156 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
157 private final BridgeHandler bridgeHandler = new BridgeHandler();
158
159 private ConsistentMap<String, OpenstackNode> nodeStore;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900160 private ApplicationId appId;
Daniel Parkad21c572016-03-09 10:18:24 +0900161 private NodeId localNodeId;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900162
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700163 private enum NodeState implements OpenstackNodeState {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900164
165 INIT {
166 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700167 public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
168 // make sure there is OVSDB connection
169 if (!nodeManager.isOvsdbConnected(node)) {
170 nodeManager.connectOvsdb(node);
171 return;
172 }
173 nodeManager.createBridge(node,
174 INTEGRATION_BRIDGE,
175 node.intBridge().toString().substring(DPID_BEGIN));
176
177 // creates additional router bridge if the node type is GATEWAY
178 if (node.type().equals(NodeType.GATEWAY)) {
179 nodeManager.createBridge(node,
180 ROUTER_BRIDGE,
181 node.routerBridge().get().toString().substring(DPID_BEGIN));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900182 }
183 }
184 },
185 BRIDGE_CREATED {
186 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700187 public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
188 // make sure there is OVSDB connection
189 if (!nodeManager.isOvsdbConnected(node)) {
190 nodeManager.connectOvsdb(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);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900198 }
199 }
200 },
201 COMPLETE {
202 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700203 public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
204 nodeManager.postInit(node);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900205 }
206 },
207 INCOMPLETE {
208 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700209 public void process(OpenstackNodeManager nodeManager, OpenstackNode node) {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900210 }
211 };
212
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700213 public abstract void process(OpenstackNodeManager nodeManager, OpenstackNode node);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900214 }
215
216 @Activate
217 protected void activate() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700218 appId = coreService.getAppId(APP_ID);
219
Daniel Parkad21c572016-03-09 10:18:24 +0900220 localNodeId = clusterService.getLocalNode().id();
221 leadershipService.runForLeadership(appId.name());
222
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700223 nodeStore = storageService.<String, OpenstackNode>consistentMapBuilder()
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900224 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700225 .withName("openstack-nodestore")
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900226 .withApplicationId(appId)
227 .build();
228
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700229 nodeStore.addListener(nodeStoreListener);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900230 deviceService.addListener(deviceListener);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700231
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900232 configRegistry.registerConfigFactory(configFactory);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700233 configRegistry.addListener(configListener);
234 componentConfigService.registerProperties(getClass());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900235
236 log.info("Started");
237 }
238
239 @Deactivate
240 protected void deactivate() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700241 configRegistry.removeListener(configListener);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900242 deviceService.removeListener(deviceListener);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700243 nodeStore.removeListener(nodeStoreListener);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900244
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700245 componentConfigService.unregisterProperties(getClass(), false);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900246 configRegistry.unregisterConfigFactory(configFactory);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700247
Daniel Parkad21c572016-03-09 10:18:24 +0900248 leadershipService.withdraw(appId.name());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700249 eventExecutor.shutdown();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900250
251 log.info("Stopped");
252 }
253
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700254 @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 }
261
262 log.info("Modified");
263 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900264
265 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700266 public void addOrUpdateNode(OpenstackNode node) {
267 nodeStore.put(node.hostname(),
268 OpenstackNode.getUpdatedNode(node, nodeState(node)));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900269 }
270
271 @Override
272 public void deleteNode(OpenstackNode node) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700273 if (isOvsdbConnected(node)) {
274 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
275 controller.getOvsdbClient(ovsdb).disconnect();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900276 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700277 nodeStore.remove(node.hostname());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900278 }
279
280 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700281 public List<OpenstackNode> nodes() {
282 return nodeStore.values().stream().map(Versioned::value).collect(Collectors.toList());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900283 }
284
285 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700286 public Set<OpenstackNode> completeNodes() {
287 return nodeStore.values().stream().map(Versioned::value)
288 .filter(node -> node.state().equals(NodeState.COMPLETE))
289 .collect(Collectors.toSet());
290 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900291
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700292 @Override
293 public boolean isComplete(String hostname) {
294 Versioned<OpenstackNode> versionedNode = nodeStore.get(hostname);
295 if (versionedNode == null) {
296 log.warn("Node {} does not exist", hostname);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900297 return false;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900298 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700299 OpenstackNodeState state = versionedNode.value().state();
300 return state != null && state.equals(NodeState.COMPLETE);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900301 }
302
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700303 @Override
304 public Optional<IpAddress> dataIp(DeviceId deviceId) {
305 OpenstackNode node = nodeByDeviceId(deviceId);
306 if (node == null) {
307 log.warn("Failed to get node for {}", deviceId);
308 return Optional.empty();
309 }
310 return Optional.of(node.dataIp());
311 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900312
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700313 @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 }
320
321 @Override
322 public Optional<DeviceId> routerBridge(DeviceId intBridgeId) {
323 OpenstackNode node = nodeByDeviceId(intBridgeId);
324 if (node == null || node.type().equals(NodeType.COMPUTE)) {
325 log.warn("Failed to find router bridge connected to {}", intBridgeId);
326 return Optional.empty();
327 }
328 return node.routerBridge();
329 }
330
331 @Override
332 public Optional<PortNumber> externalPort(DeviceId intBridgeId) {
333 return deviceService.getPorts(intBridgeId).stream()
334 .filter(p -> p.annotations().value(PORT_NAME).equals(PATCH_INTG_BRIDGE) &&
335 p.isEnabled())
336 .map(Port::number).findFirst();
337 }
338
339 private void initNode(OpenstackNode node) {
340 NodeState state = (NodeState) node.state();
341 state.process(this, node);
342 log.debug("Processing node: {} state: {}", node.hostname(), state);
343 }
344
345 private void postInit(OpenstackNode node) {
346 if (isOvsdbConnected(node)) {
347 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
348 controller.getOvsdbClient(ovsdb).disconnect();
349 }
350
351 // TODO add gateway node to scalable gateway pool
352 log.info("Finished init {}", node.hostname());
353 }
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));
358 }
359
360 private NodeState nodeState(OpenstackNode node) {
361 if (!deviceService.isAvailable(node.intBridge())) {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900362 return NodeState.INIT;
363 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700364 if (node.type().equals(NodeType.GATEWAY) &&
365 !deviceService.isAvailable(node.routerBridge().get())) {
366 return NodeState.INIT;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900367 }
368
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700369 if (!isIfaceCreated(node.intBridge(), DEFAULT_TUNNEL)) {
370 return NodeState.BRIDGE_CREATED;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900371 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700372 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;
376 }
377
378 return NodeState.COMPLETE;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900379 }
380
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700381 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();
387 }
388
389 private void createBridge(OpenstackNode node, String bridgeName, String dpid) {
390 Device device = deviceService.getDevice(node.ovsdbId());
391 if (device == null || !device.is(BridgeConfig.class)) {
392 log.error("Failed to create integration bridge on {}", node.ovsdbId());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900393 return;
394 }
395
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700396 List<ControllerInfo> controllers = clusterService.getNodes().stream()
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700397 .map(controller -> new ControllerInfo(controller.ip(), DEFAULT_OFPORT, "tcp"))
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700398 .collect(Collectors.toList());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900399
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700400 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700401 .name(bridgeName)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700402 .failMode(BridgeDescription.FailMode.SECURE)
403 .datapathId(dpid)
404 .disableInBand()
405 .controllers(controllers)
406 .build();
407
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700408 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
409 bridgeConfig.addBridge(bridgeDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900410 }
411
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900412 private void createTunnelInterface(OpenstackNode node) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700413 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());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900416 return;
417 }
418
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700419 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
420 .deviceId(INTEGRATION_BRIDGE)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700421 .ifaceName(DEFAULT_TUNNEL)
422 .type(VXLAN)
423 .remote(TunnelEndPoints.flowTunnelEndpoint())
424 .key(TunnelKeys.flowTunnelKey())
425 .build();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700426
427 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
428 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900429 }
430
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700431 private void createPatchInterface(OpenstackNode node) {
432 Device device = deviceService.getDevice(node.ovsdbId());
433 if (device == null || !device.is(InterfaceConfig.class)) {
434 log.error("Failed to create patch interfaces on {}", node.hostname());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900435 return;
436 }
437
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700438 PatchDescription patchIntg = DefaultPatchDescription.builder()
439 .deviceId(INTEGRATION_BRIDGE)
440 .ifaceName(PATCH_INTG_BRIDGE)
441 .peer(PATCH_ROUT_BRIDGE)
442 .build();
443
444 PatchDescription patchRout = DefaultPatchDescription.builder()
445 .deviceId(ROUTER_BRIDGE)
446 .ifaceName(PATCH_ROUT_BRIDGE)
447 .peer(PATCH_INTG_BRIDGE)
448 .build();
449
450 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
451 ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
452 ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
453 }
454
455 private boolean isOvsdbConnected(OpenstackNode node) {
456 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
457 OvsdbClientService client = controller.getOvsdbClient(ovsdb);
458 return deviceService.isAvailable(node.ovsdbId()) &&
459 client != null &&
460 client.isConnected();
461 }
462
463 private void connectOvsdb(OpenstackNode node) {
464 controller.connect(node.managementIp(), TpPort.tpPort(ovsdbPort));
465 }
466
467 private Set<String> systemIfaces(OpenstackNode node) {
468 Set<String> ifaces = Sets.newHashSet(DEFAULT_TUNNEL);
469 if (node.type().equals(NodeType.GATEWAY)) {
470 ifaces.add(PATCH_INTG_BRIDGE);
471 ifaces.add(PATCH_ROUT_BRIDGE);
472 }
473 return ifaces;
474 }
475
476 private OpenstackNode nodeByDeviceId(DeviceId deviceId) {
477 OpenstackNode node = nodes().stream()
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));
483
484 return node;
485 }
486
487 private class OvsdbHandler implements ConnectionHandler<Device> {
488
489 @Override
490 public void connected(Device device) {
491 OpenstackNode node = nodes().stream()
492 .filter(n -> n.ovsdbId().equals(device.id()))
493 .findFirst()
494 .orElse(null);
495 if (node != null) {
496 setNodeState(node, nodeState(node));
497 } else {
498 log.debug("{} is detected on unregistered node, ignore it.", device.id());
499 }
500 }
501
502 @Override
503 public void disconnected(Device device) {
504 log.debug("Device {} is disconnected", device.id());
505 }
506 }
507
508 private class BridgeHandler implements ConnectionHandler<Device> {
509
510 @Override
511 public void connected(Device device) {
512 OpenstackNode node = nodeByDeviceId(device.id());
513 if (node != null) {
514 setNodeState(node, nodeState(node));
515 } else {
516 log.debug("{} is detected on unregistered node, ignore it.", device.id());
517 }
518 }
519
520 @Override
521 public void disconnected(Device device) {
522 OpenstackNode node = nodeByDeviceId(device.id());
523 if (node != null) {
524 log.warn("Device {} is disconnected", device.id());
525 setNodeState(node, NodeState.INCOMPLETE);
526 }
527 }
528
529 /**
530 * Handles port added situation.
531 * If the added port is tunnel or data plane interface, proceed to the remaining
532 * node initialization. Otherwise, do nothing.
533 *
534 * @param port port
535 */
536 public void portAdded(Port port) {
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);
541 return;
542 }
543
544 log.info("Port {} is added to {}", portName, node.hostname());
545 if (systemIfaces(node).contains(portName)) {
546 setNodeState(node, nodeState(node));
547 }
548 }
549
550 /**
551 * Handles port removed situation.
552 * If the removed port is tunnel or data plane interface, proceed to the remaining
553 * node initialization.Others, do nothing.
554 *
555 * @param port port
556 */
557 public void portRemoved(Port port) {
558 OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
559 String portName = port.annotations().value(PORT_NAME);
560
561 if (node == null) {
562 return;
563 }
564
565 log.info("Port {} is removed from {}", portName, node.hostname());
566 if (systemIfaces(node).contains(portName)) {
567 setNodeState(node, NodeState.INCOMPLETE);
568 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900569 }
570 }
571
572 private class InternalDeviceListener implements DeviceListener {
573
574 @Override
575 public void event(DeviceEvent event) {
Daniel Parkad21c572016-03-09 10:18:24 +0900576
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700577 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
578 if (!Objects.equals(localNodeId, leaderNodeId)) {
579 // do not allow to proceed without leadership
Daniel Parkad21c572016-03-09 10:18:24 +0900580 return;
581 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900582
583 Device device = event.subject();
584 ConnectionHandler<Device> handler =
585 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
586
587 switch (event.type()) {
588 case PORT_ADDED:
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700589 eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900590 break;
591 case PORT_UPDATED:
592 if (!event.port().isEnabled()) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700593 eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900594 }
595 break;
596 case DEVICE_ADDED:
597 case DEVICE_AVAILABILITY_CHANGED:
598 if (deviceService.isAvailable(device.id())) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700599 eventExecutor.execute(() -> handler.connected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900600 } else {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700601 eventExecutor.execute(() -> handler.disconnected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900602 }
603 break;
604 default:
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900605 break;
606 }
607 }
608 }
609
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900610 private void readConfiguration() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700611 OpenstackNodeConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900612 if (config == null) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700613 log.debug("No configuration found");
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900614 return;
615 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700616 config.openstackNodes().forEach(this::addOrUpdateNode);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900617 }
618
619 private class InternalConfigListener implements NetworkConfigListener {
620
621 @Override
622 public void event(NetworkConfigEvent event) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700623 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)) {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900630 return;
631 }
632
633 switch (event.type()) {
634 case CONFIG_ADDED:
635 case CONFIG_UPDATED:
636 eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
637 break;
638 default:
639 break;
640 }
641 }
642 }
643
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700644 private class InternalMapListener implements MapEventListener<String, OpenstackNode> {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900645
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700646 @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;
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 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900685}
686