blob: 5276c586f6ef7b465bdae5831c011b2dca6dd82b [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;
Hyunsun Moon052c71f2016-07-11 18:56:18 -070033import org.onosproject.cluster.ControllerNode;
Daniel Parkad21c572016-03-09 10:18:24 +090034import org.onosproject.cluster.LeadershipService;
35import org.onosproject.cluster.NodeId;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090036import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
Hyunsun Moon05d9b262016-07-03 18:38:44 -070038import org.onosproject.event.ListenerRegistry;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090039import org.onosproject.net.Device;
40import org.onosproject.net.DeviceId;
41import org.onosproject.net.Port;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070042import org.onosproject.net.PortNumber;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090043import org.onosproject.net.behaviour.BridgeConfig;
Hyunsun Moon1251e192016-06-07 16:57:05 -070044import org.onosproject.net.behaviour.BridgeDescription;
Hyunsun Moon052c71f2016-07-11 18:56:18 -070045import org.onosproject.net.behaviour.BridgeName;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090046import org.onosproject.net.behaviour.ControllerInfo;
Hyunsun Moon1251e192016-06-07 16:57:05 -070047import org.onosproject.net.behaviour.DefaultBridgeDescription;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070048import org.onosproject.net.behaviour.DefaultPatchDescription;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090049import org.onosproject.net.behaviour.DefaultTunnelDescription;
Hyunsun Moondd14e8e2016-06-09 16:17:32 -070050import org.onosproject.net.behaviour.InterfaceConfig;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070051import org.onosproject.net.behaviour.PatchDescription;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090052import org.onosproject.net.behaviour.TunnelDescription;
Hyunsun Moondd14e8e2016-06-09 16:17:32 -070053import org.onosproject.net.behaviour.TunnelEndPoints;
54import org.onosproject.net.behaviour.TunnelKeys;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090055import org.onosproject.net.config.ConfigFactory;
56import org.onosproject.net.config.NetworkConfigEvent;
57import org.onosproject.net.config.NetworkConfigListener;
58import org.onosproject.net.config.NetworkConfigRegistry;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090059import org.onosproject.net.config.basics.SubjectFactories;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090060import org.onosproject.net.device.DeviceEvent;
61import org.onosproject.net.device.DeviceListener;
62import org.onosproject.net.device.DeviceService;
Hyunsun Moon05d9b262016-07-03 18:38:44 -070063import org.onosproject.openstacknode.OpenstackNodeEvent.NodeState;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090064import org.onosproject.ovsdb.controller.OvsdbClientService;
65import org.onosproject.ovsdb.controller.OvsdbController;
66import org.onosproject.ovsdb.controller.OvsdbNodeId;
67import org.onosproject.store.serializers.KryoNamespaces;
68import org.onosproject.store.service.ConsistentMap;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070069import org.onosproject.store.service.MapEvent;
70import org.onosproject.store.service.MapEventListener;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090071import org.onosproject.store.service.Serializer;
72import org.onosproject.store.service.StorageService;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070073import org.onosproject.store.service.Versioned;
74import org.osgi.service.component.ComponentContext;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090075import org.slf4j.Logger;
76
sangho24556ec2016-08-25 10:41:14 +090077import java.util.Dictionary;
78import java.util.HashMap;
79import java.util.List;
80import java.util.Map;
81import java.util.Objects;
82import java.util.Optional;
83import java.util.Set;
84import java.util.concurrent.ExecutorService;
85import java.util.stream.Collectors;
86
Hyunsun Moon052c71f2016-07-11 18:56:18 -070087import static com.google.common.base.Preconditions.checkArgument;
Daniel Parkad21c572016-03-09 10:18:24 +090088import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090089import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070090import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090091import static org.onosproject.net.Device.Type.SWITCH;
92import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
Hyunsun Moon34bbe172016-06-28 19:18:40 -070093import static org.onosproject.openstacknode.Constants.*;
Hyunsun Moon05d9b262016-07-03 18:38:44 -070094import static org.onosproject.openstacknode.OpenstackNodeEvent.NodeState.*;
Daniel Parka7d6e9f2016-01-18 17:54:14 +090095import static org.slf4j.LoggerFactory.getLogger;
96
Daniel Parka7d6e9f2016-01-18 17:54:14 +090097/**
98 * Initializes devices in compute/gateway nodes according to there type.
99 */
100@Component(immediate = true)
101@Service
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700102public final class OpenstackNodeManager extends ListenerRegistry<OpenstackNodeEvent, OpenstackNodeListener>
103 implements OpenstackNodeService {
104 private final Logger log = getLogger(getClass());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700105
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900106 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
107 .register(KryoNamespaces.API)
108 .register(OpenstackNode.class)
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700109 .register(NodeType.class)
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900110 .register(NodeState.class);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900111
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700112 private static final String OVSDB_PORT = "ovsdbPort";
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900113 private static final int DPID_BEGIN = 3;
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700114
115 private static final String APP_ID = "org.onosproject.openstacknode";
116 private static final Class<OpenstackNodeConfig> CONFIG_CLASS = OpenstackNodeConfig.class;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected CoreService coreService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected DeviceService deviceService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700125 protected OvsdbController ovsdbController;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected ClusterService clusterService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900131 protected StorageService storageService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700134 protected ComponentConfigService componentConfigService;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected NetworkConfigRegistry configRegistry;
138
Daniel Parkad21c572016-03-09 10:18:24 +0900139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected LeadershipService leadershipService;
141
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700142 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
143 label = "OVSDB server listen port")
144 private int ovsdbPort = DEFAULT_OVSDB_PORT;
145
146 private final ExecutorService eventExecutor =
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700147 newSingleThreadScheduledExecutor(groupedThreads("onos/openstack-node", "event-handler", log));
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700148
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900149 private final ConfigFactory configFactory =
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700150 new ConfigFactory<ApplicationId, OpenstackNodeConfig>(
151 SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "openstacknode") {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900152 @Override
153 public OpenstackNodeConfig createConfig() {
154 return new OpenstackNodeConfig();
155 }
156 };
157
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700158 private final NetworkConfigListener configListener = new InternalConfigListener();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900159 private final DeviceListener deviceListener = new InternalDeviceListener();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700160 private final MapEventListener<String, OpenstackNode> nodeStoreListener = new InternalMapListener();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900161
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700162 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
163 private final BridgeHandler bridgeHandler = new BridgeHandler();
164
165 private ConsistentMap<String, OpenstackNode> nodeStore;
sangho24556ec2016-08-25 10:41:14 +0900166
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900167 private ApplicationId appId;
Daniel Parkad21c572016-03-09 10:18:24 +0900168 private NodeId localNodeId;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900169
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900170 @Activate
171 protected void activate() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700172 appId = coreService.getAppId(APP_ID);
173
Daniel Parkad21c572016-03-09 10:18:24 +0900174 localNodeId = clusterService.getLocalNode().id();
175 leadershipService.runForLeadership(appId.name());
176
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700177 nodeStore = storageService.<String, OpenstackNode>consistentMapBuilder()
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900178 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700179 .withName("openstack-nodestore")
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900180 .withApplicationId(appId)
181 .build();
182
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700183 nodeStore.addListener(nodeStoreListener);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900184 deviceService.addListener(deviceListener);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700185
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900186 configRegistry.registerConfigFactory(configFactory);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700187 configRegistry.addListener(configListener);
188 componentConfigService.registerProperties(getClass());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900189
Hyunsun Moonef6bad22016-07-19 16:25:43 -0700190 readConfiguration();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900191 log.info("Started");
192 }
193
194 @Deactivate
195 protected void deactivate() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700196 configRegistry.removeListener(configListener);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900197 deviceService.removeListener(deviceListener);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700198 nodeStore.removeListener(nodeStoreListener);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900199
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700200 componentConfigService.unregisterProperties(getClass(), false);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900201 configRegistry.unregisterConfigFactory(configFactory);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700202
Daniel Parkad21c572016-03-09 10:18:24 +0900203 leadershipService.withdraw(appId.name());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700204 eventExecutor.shutdown();
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900205
206 log.info("Stopped");
207 }
208
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700209 @Modified
210 protected void modified(ComponentContext context) {
211 Dictionary<?, ?> properties = context.getProperties();
212 int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
213 if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
214 ovsdbPort = updatedOvsdbPort;
215 }
216
217 log.info("Modified");
218 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900219
220 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700221 public void addOrUpdateNode(OpenstackNode node) {
222 nodeStore.put(node.hostname(),
223 OpenstackNode.getUpdatedNode(node, nodeState(node)));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900224 }
225
226 @Override
227 public void deleteNode(OpenstackNode node) {
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 }
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700239 process(new OpenstackNodeEvent(INIT, node));
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700240
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700241 createBridge(node, INTEGRATION_BRIDGE, node.intBridge());
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700242 if (node.type().equals(NodeType.GATEWAY)) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700243 createBridge(node, ROUTER_BRIDGE, node.routerBridge().get());
244 // TODO remove this when OVSDB provides port event
245 setNodeState(node, nodeState(node));
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700246 }
247 }
248
249 @Override
250 public void processDeviceCreatedState(OpenstackNode node) {
251 // make sure there is OVSDB connection
252 if (!isOvsdbConnected(node)) {
253 connectOvsdb(node);
254 return;
255 }
daniel park917beb42017-03-16 18:07:15 +0900256
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700257 process(new OpenstackNodeEvent(DEVICE_CREATED, node));
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700258
daniel park917beb42017-03-16 18:07:15 +0900259 if (node.dataIp().isPresent()) {
260 createTunnelInterface(node);
261 }
262
263 if (node.vlanPort().isPresent()) {
264 addVlanPort(node);
265 }
266
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700267 if (node.type().equals(NodeType.GATEWAY)) {
268 createPatchInterface(node);
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700269 addUplink(node);
270 // TODO remove this when OVSDB provides port event
271 setNodeState(node, nodeState(node));
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700272 }
273 }
274
275 @Override
276 public void processCompleteState(OpenstackNode node) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700277 process(new OpenstackNodeEvent(COMPLETE, node));
278 log.info("Finished init {}", node.hostname());
279 }
280
281 @Override
282 public void processIncompleteState(OpenstackNode node) {
283 process(new OpenstackNodeEvent(INCOMPLETE, node));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900284 }
285
286 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700287 public List<OpenstackNode> nodes() {
288 return nodeStore.values().stream().map(Versioned::value).collect(Collectors.toList());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900289 }
290
291 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700292 public Set<OpenstackNode> completeNodes() {
293 return nodeStore.values().stream().map(Versioned::value)
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700294 .filter(node -> node.state().equals(COMPLETE))
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700295 .collect(Collectors.toSet());
296 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900297
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700298 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700299 public Optional<IpAddress> dataIp(DeviceId deviceId) {
300 OpenstackNode node = nodeByDeviceId(deviceId);
301 if (node == null) {
302 log.warn("Failed to get node for {}", deviceId);
303 return Optional.empty();
304 }
daniel park917beb42017-03-16 18:07:15 +0900305 return node.dataIp();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700306 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900307
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700308 @Override
309 public Optional<PortNumber> tunnelPort(DeviceId deviceId) {
310 return deviceService.getPorts(deviceId).stream()
311 .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL) &&
312 p.isEnabled())
313 .map(Port::number).findFirst();
314 }
315
316 @Override
317 public Optional<DeviceId> routerBridge(DeviceId intBridgeId) {
318 OpenstackNode node = nodeByDeviceId(intBridgeId);
319 if (node == null || node.type().equals(NodeType.COMPUTE)) {
320 log.warn("Failed to find router bridge connected to {}", intBridgeId);
321 return Optional.empty();
322 }
323 return node.routerBridge();
324 }
325
326 @Override
327 public Optional<PortNumber> externalPort(DeviceId intBridgeId) {
328 return deviceService.getPorts(intBridgeId).stream()
329 .filter(p -> p.annotations().value(PORT_NAME).equals(PATCH_INTG_BRIDGE) &&
330 p.isEnabled())
331 .map(Port::number).findFirst();
332 }
333
334 private void initNode(OpenstackNode node) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700335 NodeState state = node.state();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700336 state.process(this, node);
337 log.debug("Processing node: {} state: {}", node.hostname(), state);
338 }
339
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700340 private void setNodeState(OpenstackNode node, NodeState newState) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700341 if (node.state() != newState) {
342 log.debug("Changed {} state: {}", node.hostname(), newState);
343 nodeStore.put(node.hostname(), OpenstackNode.getUpdatedNode(node, newState));
344 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700345 }
346
347 private NodeState nodeState(OpenstackNode node) {
Frank Wang39b95772017-03-01 15:35:13 +0800348 if (!isOvsdbConnected(node) || !deviceService.isAvailable(node.intBridge()) ||
349 !isBridgeCreated(node.ovsdbId(), INTEGRATION_BRIDGE)) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700350 return INIT;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900351 }
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700352
353 // TODO use device service when we use single ONOS cluster for both openstackNode and vRouter
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700354 if (node.type().equals(NodeType.GATEWAY) &&
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700355 !isBridgeCreated(node.ovsdbId(), ROUTER_BRIDGE)) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700356 return INIT;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900357 }
358
daniel park917beb42017-03-16 18:07:15 +0900359 if (node.dataIp().isPresent() && !isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
360 return DEVICE_CREATED;
361 }
362
363 if (node.vlanPort().isPresent() && !isIfaceCreated(node.ovsdbId(), node.vlanPort().get())) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700364 return DEVICE_CREATED;
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700365 }
366
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700367 if (node.type().equals(NodeType.GATEWAY) && (
368 !isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE) ||
369 !isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) ||
370 !isIfaceCreated(node.ovsdbId(), node.uplink().get()))) {
371 return DEVICE_CREATED;
372 }
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700373 return COMPLETE;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900374 }
375
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700376 private boolean isIfaceCreated(DeviceId deviceId, String ifaceName) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700377 Device device = deviceService.getDevice(deviceId);
378 if (device == null || !device.is(BridgeConfig.class)) {
379 return false;
380 }
381
382 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
383 return bridgeConfig.getPorts().stream()
384 .filter(port -> port.annotations().value(PORT_NAME).equals(ifaceName))
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700385 .findAny()
386 .isPresent();
387 }
388
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700389 private boolean isBridgeCreated(DeviceId deviceId, String bridgeName) {
390 Device device = deviceService.getDevice(deviceId);
391 if (device == null || !device.is(BridgeConfig.class)) {
392 return false;
393 }
394
395 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
396 return bridgeConfig.getBridges().stream()
397 .filter(bridge -> bridge.name().equals(bridgeName))
398 .findAny()
399 .isPresent();
400 }
401
402 private void createBridge(OpenstackNode node, String bridgeName, DeviceId deviceId) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700403 Device device = deviceService.getDevice(node.ovsdbId());
404 if (device == null || !device.is(BridgeConfig.class)) {
405 log.error("Failed to create integration bridge on {}", node.ovsdbId());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900406 return;
407 }
408
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700409 // TODO fix this when we use single ONOS cluster for both openstackNode and vRouter
410 Set<IpAddress> controllerIps;
411 if (bridgeName.equals(ROUTER_BRIDGE)) {
412 controllerIps = Sets.newHashSet(node.routerController().get());
413 } else {
414 controllerIps = clusterService.getNodes().stream()
415 .map(ControllerNode::ip)
416 .collect(Collectors.toSet());
417 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900418
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700419 List<ControllerInfo> controllers = controllerIps.stream()
420 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
421 .collect(Collectors.toList());
422
423 String dpid = deviceId.toString().substring(DPID_BEGIN);
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700424 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700425 .name(bridgeName)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700426 .failMode(BridgeDescription.FailMode.SECURE)
427 .datapathId(dpid)
428 .disableInBand()
429 .controllers(controllers)
430 .build();
431
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700432 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700433 bridgeConfig.addBridge(bridgeDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900434 }
435
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900436 private void createTunnelInterface(OpenstackNode node) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700437 if (isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
438 return;
439 }
440
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700441 Device device = deviceService.getDevice(node.ovsdbId());
442 if (device == null || !device.is(InterfaceConfig.class)) {
443 log.error("Failed to create tunnel interface on {}", node.ovsdbId());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900444 return;
445 }
446
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700447 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
448 .deviceId(INTEGRATION_BRIDGE)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700449 .ifaceName(DEFAULT_TUNNEL)
450 .type(VXLAN)
451 .remote(TunnelEndPoints.flowTunnelEndpoint())
452 .key(TunnelKeys.flowTunnelKey())
453 .build();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700454
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700455 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700456 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900457 }
458
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700459 private void createPatchInterface(OpenstackNode node) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700460 checkArgument(node.type().equals(NodeType.GATEWAY));
461 if (isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) &&
462 isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE)) {
463 return;
464 }
465
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700466 Device device = deviceService.getDevice(node.ovsdbId());
467 if (device == null || !device.is(InterfaceConfig.class)) {
468 log.error("Failed to create patch interfaces on {}", node.hostname());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900469 return;
470 }
471
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700472 PatchDescription patchIntg = DefaultPatchDescription.builder()
473 .deviceId(INTEGRATION_BRIDGE)
474 .ifaceName(PATCH_INTG_BRIDGE)
475 .peer(PATCH_ROUT_BRIDGE)
476 .build();
477
478 PatchDescription patchRout = DefaultPatchDescription.builder()
479 .deviceId(ROUTER_BRIDGE)
480 .ifaceName(PATCH_ROUT_BRIDGE)
481 .peer(PATCH_INTG_BRIDGE)
482 .build();
483
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700484 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700485 ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
486 ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
487 }
488
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700489 private void addUplink(OpenstackNode node) {
490 checkArgument(node.type().equals(NodeType.GATEWAY));
491 if (isIfaceCreated(node.ovsdbId(), node.uplink().get())) {
492 return;
493 }
494
495 Device device = deviceService.getDevice(node.ovsdbId());
496 if (device == null || !device.is(BridgeConfig.class)) {
497 log.error("Failed to add port {} on {}", node.uplink().get(), node.ovsdbId());
498 return;
499 }
500
501 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
502 bridgeConfig.addPort(BridgeName.bridgeName(ROUTER_BRIDGE),
503 node.uplink().get());
504 }
505
daniel park917beb42017-03-16 18:07:15 +0900506 private void addVlanPort(OpenstackNode node) {
507 if (isIfaceCreated(node.ovsdbId(), node.vlanPort().get())) {
508 return;
509 }
510
511 Device device = deviceService.getDevice(node.ovsdbId());
512 if (device == null || !device.is(BridgeConfig.class)) {
513 log.error("Failed to add port {} on {}", node.vlanPort().get(), node.ovsdbId());
514 return;
515 }
516
517 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
518 bridgeConfig.addPort(BridgeName.bridgeName(INTEGRATION_BRIDGE),
519 node.vlanPort().get());
520 }
521
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700522 private boolean isOvsdbConnected(OpenstackNode node) {
523 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700524 OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700525 return deviceService.isAvailable(node.ovsdbId()) &&
526 client != null &&
527 client.isConnected();
528 }
529
530 private void connectOvsdb(OpenstackNode node) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700531 ovsdbController.connect(node.managementIp(), TpPort.tpPort(ovsdbPort));
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700532 }
533
534 private Set<String> systemIfaces(OpenstackNode node) {
daniel park917beb42017-03-16 18:07:15 +0900535 Set<String> ifaces = Sets.newHashSet();
536 node.dataIp().ifPresent(ip -> ifaces.add(DEFAULT_TUNNEL));
537 node.vlanPort().ifPresent(p -> ifaces.add(p));
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700538 if (node.type().equals(NodeType.GATEWAY)) {
539 ifaces.add(PATCH_INTG_BRIDGE);
540 ifaces.add(PATCH_ROUT_BRIDGE);
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700541 ifaces.add(node.uplink().get());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700542 }
543 return ifaces;
544 }
545
546 private OpenstackNode nodeByDeviceId(DeviceId deviceId) {
547 OpenstackNode node = nodes().stream()
548 .filter(n -> n.intBridge().equals(deviceId))
549 .findFirst().orElseGet(() -> nodes().stream()
550 .filter(n -> n.routerBridge().isPresent())
551 .filter(n -> n.routerBridge().get().equals(deviceId))
Frank Wang39b95772017-03-01 15:35:13 +0800552 .findFirst().orElseGet(() -> nodes().stream()
553 .filter(n -> n.ovsdbId().equals(deviceId))
554 .findFirst().orElse(null)));
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700555 return node;
556 }
557
558 private class OvsdbHandler implements ConnectionHandler<Device> {
559
560 @Override
561 public void connected(Device device) {
562 OpenstackNode node = nodes().stream()
563 .filter(n -> n.ovsdbId().equals(device.id()))
564 .findFirst()
565 .orElse(null);
566 if (node != null) {
567 setNodeState(node, nodeState(node));
568 } else {
569 log.debug("{} is detected on unregistered node, ignore it.", device.id());
570 }
571 }
572
573 @Override
574 public void disconnected(Device device) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700575 OpenstackNode node = nodeByDeviceId(device.id());
576 if (node != null) {
577 log.warn("Device {} is disconnected", device.id());
578 setNodeState(node, NodeState.INCOMPLETE);
579 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700580 }
581 }
582
583 private class BridgeHandler implements ConnectionHandler<Device> {
584
585 @Override
586 public void connected(Device device) {
587 OpenstackNode node = nodeByDeviceId(device.id());
588 if (node != null) {
589 setNodeState(node, nodeState(node));
590 } else {
591 log.debug("{} is detected on unregistered node, ignore it.", device.id());
592 }
593 }
594
595 @Override
596 public void disconnected(Device device) {
597 OpenstackNode node = nodeByDeviceId(device.id());
598 if (node != null) {
599 log.warn("Device {} is disconnected", device.id());
600 setNodeState(node, NodeState.INCOMPLETE);
601 }
602 }
603
604 /**
605 * Handles port added situation.
606 * If the added port is tunnel or data plane interface, proceed to the remaining
607 * node initialization. Otherwise, do nothing.
608 *
609 * @param port port
610 */
611 public void portAdded(Port port) {
612 OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
613 String portName = port.annotations().value(PORT_NAME);
614 if (node == null) {
615 log.debug("{} is added to unregistered node, ignore it.", portName);
616 return;
617 }
618
619 log.info("Port {} is added to {}", portName, node.hostname());
620 if (systemIfaces(node).contains(portName)) {
621 setNodeState(node, nodeState(node));
622 }
623 }
624
625 /**
626 * Handles port removed situation.
627 * If the removed port is tunnel or data plane interface, proceed to the remaining
628 * node initialization.Others, do nothing.
629 *
630 * @param port port
631 */
632 public void portRemoved(Port port) {
633 OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
634 String portName = port.annotations().value(PORT_NAME);
635
636 if (node == null) {
637 return;
638 }
639
640 log.info("Port {} is removed from {}", portName, node.hostname());
641 if (systemIfaces(node).contains(portName)) {
642 setNodeState(node, NodeState.INCOMPLETE);
643 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900644 }
645 }
646
647 private class InternalDeviceListener implements DeviceListener {
648
649 @Override
650 public void event(DeviceEvent event) {
Daniel Parkad21c572016-03-09 10:18:24 +0900651
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700652 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
653 if (!Objects.equals(localNodeId, leaderNodeId)) {
654 // do not allow to proceed without leadership
Daniel Parkad21c572016-03-09 10:18:24 +0900655 return;
656 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900657
658 Device device = event.subject();
659 ConnectionHandler<Device> handler =
660 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
661
662 switch (event.type()) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700663 // TODO implement OVSDB port event so that we can handle updates on the OVSDB
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900664 case PORT_ADDED:
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700665 eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900666 break;
667 case PORT_UPDATED:
668 if (!event.port().isEnabled()) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700669 eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900670 }
671 break;
672 case DEVICE_ADDED:
673 case DEVICE_AVAILABILITY_CHANGED:
674 if (deviceService.isAvailable(device.id())) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700675 eventExecutor.execute(() -> handler.connected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900676 } else {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700677 eventExecutor.execute(() -> handler.disconnected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900678 }
679 break;
680 default:
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900681 break;
682 }
683 }
684 }
685
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900686 private void readConfiguration() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700687 OpenstackNodeConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900688 if (config == null) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700689 log.debug("No configuration found");
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900690 return;
691 }
sangho24556ec2016-08-25 10:41:14 +0900692
693 Map<String, OpenstackNode> prevNodeMap = new HashMap(nodeStore.asJavaMap());
694 config.openstackNodes().forEach(node -> {
695 prevNodeMap.remove(node.hostname());
696 addOrUpdateNode(node);
697 });
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700698 prevNodeMap.values().forEach(this::deleteNode);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900699 }
700
701 private class InternalConfigListener implements NetworkConfigListener {
702
703 @Override
704 public void event(NetworkConfigEvent event) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700705 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
706 if (!Objects.equals(localNodeId, leaderNodeId)) {
707 // do not allow to proceed without leadership
708 return;
709 }
710
711 if (!event.configClass().equals(CONFIG_CLASS)) {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900712 return;
713 }
714
715 switch (event.type()) {
716 case CONFIG_ADDED:
717 case CONFIG_UPDATED:
718 eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
719 break;
720 default:
721 break;
722 }
723 }
724 }
725
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700726 private class InternalMapListener implements MapEventListener<String, OpenstackNode> {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900727
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700728 @Override
729 public void event(MapEvent<String, OpenstackNode> event) {
730 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
731 if (!Objects.equals(localNodeId, leaderNodeId)) {
732 // do not allow to proceed without leadership
733 return;
734 }
735
736 OpenstackNode oldNode;
737 OpenstackNode newNode;
738
739 switch (event.type()) {
740 case UPDATE:
741 oldNode = event.oldValue().value();
742 newNode = event.newValue().value();
743
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700744 log.info("Reloaded {}", newNode.hostname());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700745 if (!newNode.equals(oldNode)) {
746 log.debug("New node: {}", newNode);
747 }
748 // performs init procedure even if the node is not changed
749 // for robustness since it's no harm to run init procedure
750 // multiple times
751 eventExecutor.execute(() -> initNode(newNode));
752 break;
753 case INSERT:
754 newNode = event.newValue().value();
755 log.info("Added {}", newNode.hostname());
756 eventExecutor.execute(() -> initNode(newNode));
757 break;
758 case REMOVE:
759 oldNode = event.oldValue().value();
760 log.info("Removed {}", oldNode.hostname());
761 break;
762 default:
763 break;
764 }
765 }
766 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900767}