blob: 56c422c6863ad7ed623093a13457e40344caf21a [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;
Hyunsun Moon05d9b262016-07-03 18:38:44 -070037import org.onosproject.event.ListenerRegistry;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090038import org.onosproject.net.Device;
39import org.onosproject.net.DeviceId;
40import org.onosproject.net.Port;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070041import org.onosproject.net.PortNumber;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090042import org.onosproject.net.behaviour.BridgeConfig;
Hyunsun Moon1251e192016-06-07 16:57:05 -070043import org.onosproject.net.behaviour.BridgeDescription;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090044import org.onosproject.net.behaviour.ControllerInfo;
Hyunsun Moon1251e192016-06-07 16:57:05 -070045import org.onosproject.net.behaviour.DefaultBridgeDescription;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070046import org.onosproject.net.behaviour.DefaultPatchDescription;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090047import org.onosproject.net.behaviour.DefaultTunnelDescription;
Hyunsun Moondd14e8e2016-06-09 16:17:32 -070048import org.onosproject.net.behaviour.InterfaceConfig;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070049import org.onosproject.net.behaviour.PatchDescription;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090050import org.onosproject.net.behaviour.TunnelDescription;
Hyunsun Moondd14e8e2016-06-09 16:17:32 -070051import org.onosproject.net.behaviour.TunnelEndPoints;
52import org.onosproject.net.behaviour.TunnelKeys;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090053import org.onosproject.net.config.ConfigFactory;
54import org.onosproject.net.config.NetworkConfigEvent;
55import org.onosproject.net.config.NetworkConfigListener;
56import org.onosproject.net.config.NetworkConfigRegistry;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090057import org.onosproject.net.config.basics.SubjectFactories;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090058import org.onosproject.net.device.DeviceEvent;
59import org.onosproject.net.device.DeviceListener;
60import org.onosproject.net.device.DeviceService;
Hyunsun Moon05d9b262016-07-03 18:38:44 -070061import org.onosproject.openstacknode.OpenstackNodeEvent.NodeState;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090062import org.onosproject.ovsdb.controller.OvsdbClientService;
63import org.onosproject.ovsdb.controller.OvsdbController;
64import org.onosproject.ovsdb.controller.OvsdbNodeId;
65import org.onosproject.store.serializers.KryoNamespaces;
66import org.onosproject.store.service.ConsistentMap;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070067import org.onosproject.store.service.MapEvent;
68import org.onosproject.store.service.MapEventListener;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090069import org.onosproject.store.service.Serializer;
70import org.onosproject.store.service.StorageService;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070071import org.onosproject.store.service.Versioned;
72import org.osgi.service.component.ComponentContext;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090073import org.slf4j.Logger;
74
Daniel Parkad21c572016-03-09 10:18:24 +090075import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090076import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070077import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090078import static org.onosproject.net.Device.Type.SWITCH;
79import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070080import static org.onosproject.openstacknode.Constants.*;
Hyunsun Moon05d9b262016-07-03 18:38:44 -070081import static org.onosproject.openstacknode.OpenstackNodeEvent.NodeState.*;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090082import static org.slf4j.LoggerFactory.getLogger;
83
Hyunsun Moon34bbe172016-06-28 19:18:40 -070084import java.util.Dictionary;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090085import java.util.List;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070086import java.util.Objects;
87import java.util.Optional;
88import java.util.Set;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090089import java.util.concurrent.ExecutorService;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090090import java.util.stream.Collectors;
91
Daniel Parka7d6e9f2016-01-18 17:54:14 +090092
93/**
94 * Initializes devices in compute/gateway nodes according to there type.
95 */
96@Component(immediate = true)
97@Service
Hyunsun Moon05d9b262016-07-03 18:38:44 -070098public final class OpenstackNodeManager extends ListenerRegistry<OpenstackNodeEvent, OpenstackNodeListener>
99 implements OpenstackNodeService {
100 private final Logger log = getLogger(getClass());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700101
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900102 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
103 .register(KryoNamespaces.API)
104 .register(OpenstackNode.class)
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700105 .register(NodeType.class)
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900106 .register(NodeState.class);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900107
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700108 private static final String OVSDB_PORT = "ovsdbPort";
109 private static final int DEFAULT_OVSDB_PORT = 6640;
110 private static final int DEFAULT_OFPORT = 6653;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900111 private static final int DPID_BEGIN = 3;
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700112
113 private static final String APP_ID = "org.onosproject.openstacknode";
114 private static final Class<OpenstackNodeConfig> CONFIG_CLASS = OpenstackNodeConfig.class;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected CoreService coreService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected DeviceService deviceService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected OvsdbController controller;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected ClusterService clusterService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900129 protected StorageService storageService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700132 protected ComponentConfigService componentConfigService;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected NetworkConfigRegistry configRegistry;
136
Daniel Parkad21c572016-03-09 10:18:24 +0900137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected LeadershipService leadershipService;
139
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700140 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
141 label = "OVSDB server listen port")
142 private int ovsdbPort = DEFAULT_OVSDB_PORT;
143
144 private final ExecutorService eventExecutor =
145 newSingleThreadScheduledExecutor(groupedThreads("onos/openstack-node", "event-handler"));
146
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900147 private final ConfigFactory configFactory =
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700148 new ConfigFactory<ApplicationId, OpenstackNodeConfig>(
149 SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "openstacknode") {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900150 @Override
151 public OpenstackNodeConfig createConfig() {
152 return new OpenstackNodeConfig();
153 }
154 };
155
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700156 private final NetworkConfigListener configListener = new InternalConfigListener();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900157 private final DeviceListener deviceListener = new InternalDeviceListener();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700158 private final MapEventListener<String, OpenstackNode> nodeStoreListener = new InternalMapListener();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900159
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700160 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
161 private final BridgeHandler bridgeHandler = new BridgeHandler();
162
163 private ConsistentMap<String, OpenstackNode> nodeStore;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900164 private ApplicationId appId;
Daniel Parkad21c572016-03-09 10:18:24 +0900165 private NodeId localNodeId;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900166
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900167 @Activate
168 protected void activate() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700169 appId = coreService.getAppId(APP_ID);
170
Daniel Parkad21c572016-03-09 10:18:24 +0900171 localNodeId = clusterService.getLocalNode().id();
172 leadershipService.runForLeadership(appId.name());
173
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700174 nodeStore = storageService.<String, OpenstackNode>consistentMapBuilder()
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900175 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700176 .withName("openstack-nodestore")
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900177 .withApplicationId(appId)
178 .build();
179
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700180 nodeStore.addListener(nodeStoreListener);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900181 deviceService.addListener(deviceListener);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700182
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900183 configRegistry.registerConfigFactory(configFactory);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700184 configRegistry.addListener(configListener);
185 componentConfigService.registerProperties(getClass());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900186
187 log.info("Started");
188 }
189
190 @Deactivate
191 protected void deactivate() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700192 configRegistry.removeListener(configListener);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900193 deviceService.removeListener(deviceListener);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700194 nodeStore.removeListener(nodeStoreListener);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900195
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700196 componentConfigService.unregisterProperties(getClass(), false);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900197 configRegistry.unregisterConfigFactory(configFactory);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700198
Daniel Parkad21c572016-03-09 10:18:24 +0900199 leadershipService.withdraw(appId.name());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700200 eventExecutor.shutdown();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900201
202 log.info("Stopped");
203 }
204
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700205 @Modified
206 protected void modified(ComponentContext context) {
207 Dictionary<?, ?> properties = context.getProperties();
208 int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
209 if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
210 ovsdbPort = updatedOvsdbPort;
211 }
212
213 log.info("Modified");
214 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900215
216 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700217 public void addOrUpdateNode(OpenstackNode node) {
218 nodeStore.put(node.hostname(),
219 OpenstackNode.getUpdatedNode(node, nodeState(node)));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900220 }
221
222 @Override
223 public void deleteNode(OpenstackNode node) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700224 if (isOvsdbConnected(node)) {
225 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
226 controller.getOvsdbClient(ovsdb).disconnect();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900227 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700228 nodeStore.remove(node.hostname());
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700229 process(new OpenstackNodeEvent(INCOMPLETE, node));
230 }
231
232 @Override
233 public void processInitState(OpenstackNode node) {
234 // make sure there is OVSDB connection
235 if (!isOvsdbConnected(node)) {
236 connectOvsdb(node);
237 return;
238 }
239
240 process(new OpenstackNodeEvent(INIT, node));
241 createBridge(node, INTEGRATION_BRIDGE,
242 node.intBridge().toString().substring(DPID_BEGIN));
243
244 // creates additional router bridge if the node type is GATEWAY
245 if (node.type().equals(NodeType.GATEWAY)) {
246 createBridge(node, ROUTER_BRIDGE,
247 node.routerBridge().get().toString().substring(DPID_BEGIN));
248 }
249 }
250
251 @Override
252 public void processDeviceCreatedState(OpenstackNode node) {
253 // make sure there is OVSDB connection
254 if (!isOvsdbConnected(node)) {
255 connectOvsdb(node);
256 return;
257 }
258 process(new OpenstackNodeEvent(DEVICE_CREATED, node));
259 createTunnelInterface(node);
260 // creates additional patch ports connecting integration bridge and
261 // router bridge if the node type is GATEWAY
262 if (node.type().equals(NodeType.GATEWAY)) {
263 createPatchInterface(node);
264 }
265 }
266
267 @Override
268 public void processCompleteState(OpenstackNode node) {
269 if (isOvsdbConnected(node)) {
270 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
271 controller.getOvsdbClient(ovsdb).disconnect();
272 }
273 process(new OpenstackNodeEvent(COMPLETE, node));
274 log.info("Finished init {}", node.hostname());
275 }
276
277 @Override
278 public void processIncompleteState(OpenstackNode node) {
279 process(new OpenstackNodeEvent(INCOMPLETE, node));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900280 }
281
282 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700283 public List<OpenstackNode> nodes() {
284 return nodeStore.values().stream().map(Versioned::value).collect(Collectors.toList());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900285 }
286
287 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700288 public Set<OpenstackNode> completeNodes() {
289 return nodeStore.values().stream().map(Versioned::value)
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700290 .filter(node -> node.state().equals(COMPLETE))
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700291 .collect(Collectors.toSet());
292 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900293
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700294 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700295 public Optional<IpAddress> dataIp(DeviceId deviceId) {
296 OpenstackNode node = nodeByDeviceId(deviceId);
297 if (node == null) {
298 log.warn("Failed to get node for {}", deviceId);
299 return Optional.empty();
300 }
301 return Optional.of(node.dataIp());
302 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900303
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700304 @Override
305 public Optional<PortNumber> tunnelPort(DeviceId deviceId) {
306 return deviceService.getPorts(deviceId).stream()
307 .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL) &&
308 p.isEnabled())
309 .map(Port::number).findFirst();
310 }
311
312 @Override
313 public Optional<DeviceId> routerBridge(DeviceId intBridgeId) {
314 OpenstackNode node = nodeByDeviceId(intBridgeId);
315 if (node == null || node.type().equals(NodeType.COMPUTE)) {
316 log.warn("Failed to find router bridge connected to {}", intBridgeId);
317 return Optional.empty();
318 }
319 return node.routerBridge();
320 }
321
322 @Override
323 public Optional<PortNumber> externalPort(DeviceId intBridgeId) {
324 return deviceService.getPorts(intBridgeId).stream()
325 .filter(p -> p.annotations().value(PORT_NAME).equals(PATCH_INTG_BRIDGE) &&
326 p.isEnabled())
327 .map(Port::number).findFirst();
328 }
329
330 private void initNode(OpenstackNode node) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700331 NodeState state = node.state();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700332 state.process(this, node);
333 log.debug("Processing node: {} state: {}", node.hostname(), state);
334 }
335
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700336 private void setNodeState(OpenstackNode node, NodeState newState) {
337 log.debug("Changed {} state: {}", node.hostname(), newState);
338 nodeStore.put(node.hostname(), OpenstackNode.getUpdatedNode(node, newState));
339 }
340
341 private NodeState nodeState(OpenstackNode node) {
342 if (!deviceService.isAvailable(node.intBridge())) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700343 return INIT;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900344 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700345 if (node.type().equals(NodeType.GATEWAY) &&
346 !deviceService.isAvailable(node.routerBridge().get())) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700347 return INIT;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900348 }
349
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700350 if (!isIfaceCreated(node.intBridge(), DEFAULT_TUNNEL)) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700351 return DEVICE_CREATED;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900352 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700353 if (node.type().equals(NodeType.GATEWAY) && (
354 !isIfaceCreated(node.routerBridge().get(), PATCH_ROUT_BRIDGE) ||
355 !isIfaceCreated(node.intBridge(), PATCH_INTG_BRIDGE))) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700356 return DEVICE_CREATED;
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700357 }
358
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700359 return COMPLETE;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900360 }
361
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700362 private boolean isIfaceCreated(DeviceId deviceId, String ifaceName) {
363 return deviceService.getPorts(deviceId).stream()
364 .filter(p -> p.annotations().value(PORT_NAME).contains(ifaceName) &&
365 p.isEnabled())
366 .findAny()
367 .isPresent();
368 }
369
370 private void createBridge(OpenstackNode node, String bridgeName, String dpid) {
371 Device device = deviceService.getDevice(node.ovsdbId());
372 if (device == null || !device.is(BridgeConfig.class)) {
373 log.error("Failed to create integration bridge on {}", node.ovsdbId());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900374 return;
375 }
376
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700377 List<ControllerInfo> controllers = clusterService.getNodes().stream()
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700378 .map(controller -> new ControllerInfo(controller.ip(), DEFAULT_OFPORT, "tcp"))
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700379 .collect(Collectors.toList());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900380
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700381 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700382 .name(bridgeName)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700383 .failMode(BridgeDescription.FailMode.SECURE)
384 .datapathId(dpid)
385 .disableInBand()
386 .controllers(controllers)
387 .build();
388
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700389 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
390 bridgeConfig.addBridge(bridgeDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900391 }
392
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900393 private void createTunnelInterface(OpenstackNode node) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700394 Device device = deviceService.getDevice(node.ovsdbId());
395 if (device == null || !device.is(InterfaceConfig.class)) {
396 log.error("Failed to create tunnel interface on {}", node.ovsdbId());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900397 return;
398 }
399
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700400 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
401 .deviceId(INTEGRATION_BRIDGE)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700402 .ifaceName(DEFAULT_TUNNEL)
403 .type(VXLAN)
404 .remote(TunnelEndPoints.flowTunnelEndpoint())
405 .key(TunnelKeys.flowTunnelKey())
406 .build();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700407
408 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
409 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900410 }
411
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700412 private void createPatchInterface(OpenstackNode node) {
413 Device device = deviceService.getDevice(node.ovsdbId());
414 if (device == null || !device.is(InterfaceConfig.class)) {
415 log.error("Failed to create patch interfaces on {}", node.hostname());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900416 return;
417 }
418
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700419 PatchDescription patchIntg = DefaultPatchDescription.builder()
420 .deviceId(INTEGRATION_BRIDGE)
421 .ifaceName(PATCH_INTG_BRIDGE)
422 .peer(PATCH_ROUT_BRIDGE)
423 .build();
424
425 PatchDescription patchRout = DefaultPatchDescription.builder()
426 .deviceId(ROUTER_BRIDGE)
427 .ifaceName(PATCH_ROUT_BRIDGE)
428 .peer(PATCH_INTG_BRIDGE)
429 .build();
430
431 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
432 ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
433 ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
434 }
435
436 private boolean isOvsdbConnected(OpenstackNode node) {
437 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
438 OvsdbClientService client = controller.getOvsdbClient(ovsdb);
439 return deviceService.isAvailable(node.ovsdbId()) &&
440 client != null &&
441 client.isConnected();
442 }
443
444 private void connectOvsdb(OpenstackNode node) {
445 controller.connect(node.managementIp(), TpPort.tpPort(ovsdbPort));
446 }
447
448 private Set<String> systemIfaces(OpenstackNode node) {
449 Set<String> ifaces = Sets.newHashSet(DEFAULT_TUNNEL);
450 if (node.type().equals(NodeType.GATEWAY)) {
451 ifaces.add(PATCH_INTG_BRIDGE);
452 ifaces.add(PATCH_ROUT_BRIDGE);
453 }
454 return ifaces;
455 }
456
457 private OpenstackNode nodeByDeviceId(DeviceId deviceId) {
458 OpenstackNode node = nodes().stream()
459 .filter(n -> n.intBridge().equals(deviceId))
460 .findFirst().orElseGet(() -> nodes().stream()
461 .filter(n -> n.routerBridge().isPresent())
462 .filter(n -> n.routerBridge().get().equals(deviceId))
463 .findFirst().orElse(null));
464
465 return node;
466 }
467
468 private class OvsdbHandler implements ConnectionHandler<Device> {
469
470 @Override
471 public void connected(Device device) {
472 OpenstackNode node = nodes().stream()
473 .filter(n -> n.ovsdbId().equals(device.id()))
474 .findFirst()
475 .orElse(null);
476 if (node != null) {
477 setNodeState(node, nodeState(node));
478 } else {
479 log.debug("{} is detected on unregistered node, ignore it.", device.id());
480 }
481 }
482
483 @Override
484 public void disconnected(Device device) {
485 log.debug("Device {} is disconnected", device.id());
486 }
487 }
488
489 private class BridgeHandler implements ConnectionHandler<Device> {
490
491 @Override
492 public void connected(Device device) {
493 OpenstackNode node = nodeByDeviceId(device.id());
494 if (node != null) {
495 setNodeState(node, nodeState(node));
496 } else {
497 log.debug("{} is detected on unregistered node, ignore it.", device.id());
498 }
499 }
500
501 @Override
502 public void disconnected(Device device) {
503 OpenstackNode node = nodeByDeviceId(device.id());
504 if (node != null) {
505 log.warn("Device {} is disconnected", device.id());
506 setNodeState(node, NodeState.INCOMPLETE);
507 }
508 }
509
510 /**
511 * Handles port added situation.
512 * If the added port is tunnel or data plane interface, proceed to the remaining
513 * node initialization. Otherwise, do nothing.
514 *
515 * @param port port
516 */
517 public void portAdded(Port port) {
518 OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
519 String portName = port.annotations().value(PORT_NAME);
520 if (node == null) {
521 log.debug("{} is added to unregistered node, ignore it.", portName);
522 return;
523 }
524
525 log.info("Port {} is added to {}", portName, node.hostname());
526 if (systemIfaces(node).contains(portName)) {
527 setNodeState(node, nodeState(node));
528 }
529 }
530
531 /**
532 * Handles port removed situation.
533 * If the removed port is tunnel or data plane interface, proceed to the remaining
534 * node initialization.Others, do nothing.
535 *
536 * @param port port
537 */
538 public void portRemoved(Port port) {
539 OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
540 String portName = port.annotations().value(PORT_NAME);
541
542 if (node == null) {
543 return;
544 }
545
546 log.info("Port {} is removed from {}", portName, node.hostname());
547 if (systemIfaces(node).contains(portName)) {
548 setNodeState(node, NodeState.INCOMPLETE);
549 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900550 }
551 }
552
553 private class InternalDeviceListener implements DeviceListener {
554
555 @Override
556 public void event(DeviceEvent event) {
Daniel Parkad21c572016-03-09 10:18:24 +0900557
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700558 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
559 if (!Objects.equals(localNodeId, leaderNodeId)) {
560 // do not allow to proceed without leadership
Daniel Parkad21c572016-03-09 10:18:24 +0900561 return;
562 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900563
564 Device device = event.subject();
565 ConnectionHandler<Device> handler =
566 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
567
568 switch (event.type()) {
569 case PORT_ADDED:
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700570 eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900571 break;
572 case PORT_UPDATED:
573 if (!event.port().isEnabled()) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700574 eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900575 }
576 break;
577 case DEVICE_ADDED:
578 case DEVICE_AVAILABILITY_CHANGED:
579 if (deviceService.isAvailable(device.id())) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700580 eventExecutor.execute(() -> handler.connected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900581 } else {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700582 eventExecutor.execute(() -> handler.disconnected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900583 }
584 break;
585 default:
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900586 break;
587 }
588 }
589 }
590
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900591 private void readConfiguration() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700592 OpenstackNodeConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900593 if (config == null) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700594 log.debug("No configuration found");
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900595 return;
596 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700597 config.openstackNodes().forEach(this::addOrUpdateNode);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900598 }
599
600 private class InternalConfigListener implements NetworkConfigListener {
601
602 @Override
603 public void event(NetworkConfigEvent event) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700604 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
605 if (!Objects.equals(localNodeId, leaderNodeId)) {
606 // do not allow to proceed without leadership
607 return;
608 }
609
610 if (!event.configClass().equals(CONFIG_CLASS)) {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900611 return;
612 }
613
614 switch (event.type()) {
615 case CONFIG_ADDED:
616 case CONFIG_UPDATED:
617 eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
618 break;
619 default:
620 break;
621 }
622 }
623 }
624
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700625 private class InternalMapListener implements MapEventListener<String, OpenstackNode> {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900626
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700627 @Override
628 public void event(MapEvent<String, OpenstackNode> event) {
629 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
630 if (!Objects.equals(localNodeId, leaderNodeId)) {
631 // do not allow to proceed without leadership
632 return;
633 }
634
635 OpenstackNode oldNode;
636 OpenstackNode newNode;
637
638 switch (event.type()) {
639 case UPDATE:
640 oldNode = event.oldValue().value();
641 newNode = event.newValue().value();
642
643 log.debug("Reloaded {}", newNode.hostname());
644 if (!newNode.equals(oldNode)) {
645 log.debug("New node: {}", newNode);
646 }
647 // performs init procedure even if the node is not changed
648 // for robustness since it's no harm to run init procedure
649 // multiple times
650 eventExecutor.execute(() -> initNode(newNode));
651 break;
652 case INSERT:
653 newNode = event.newValue().value();
654 log.info("Added {}", newNode.hostname());
655 eventExecutor.execute(() -> initNode(newNode));
656 break;
657 case REMOVE:
658 oldNode = event.oldValue().value();
659 log.info("Removed {}", oldNode.hostname());
660 break;
661 default:
662 break;
663 }
664 }
665 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900666}