blob: 09b4f5252fc93fc0707c97d5ab8ce7dc0cbbf9c4 [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 }
256 process(new OpenstackNodeEvent(DEVICE_CREATED, node));
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700257
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700258 createTunnelInterface(node);
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700259 if (node.type().equals(NodeType.GATEWAY)) {
260 createPatchInterface(node);
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700261 addUplink(node);
262 // TODO remove this when OVSDB provides port event
263 setNodeState(node, nodeState(node));
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700264 }
265 }
266
267 @Override
268 public void processCompleteState(OpenstackNode node) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700269 process(new OpenstackNodeEvent(COMPLETE, node));
270 log.info("Finished init {}", node.hostname());
271 }
272
273 @Override
274 public void processIncompleteState(OpenstackNode node) {
275 process(new OpenstackNodeEvent(INCOMPLETE, node));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900276 }
277
278 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700279 public List<OpenstackNode> nodes() {
280 return nodeStore.values().stream().map(Versioned::value).collect(Collectors.toList());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900281 }
282
283 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700284 public Set<OpenstackNode> completeNodes() {
285 return nodeStore.values().stream().map(Versioned::value)
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700286 .filter(node -> node.state().equals(COMPLETE))
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700287 .collect(Collectors.toSet());
288 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900289
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700290 @Override
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700291 public Optional<IpAddress> dataIp(DeviceId deviceId) {
292 OpenstackNode node = nodeByDeviceId(deviceId);
293 if (node == null) {
294 log.warn("Failed to get node for {}", deviceId);
295 return Optional.empty();
296 }
297 return Optional.of(node.dataIp());
298 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900299
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700300 @Override
301 public Optional<PortNumber> tunnelPort(DeviceId deviceId) {
302 return deviceService.getPorts(deviceId).stream()
303 .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL) &&
304 p.isEnabled())
305 .map(Port::number).findFirst();
306 }
307
308 @Override
309 public Optional<DeviceId> routerBridge(DeviceId intBridgeId) {
310 OpenstackNode node = nodeByDeviceId(intBridgeId);
311 if (node == null || node.type().equals(NodeType.COMPUTE)) {
312 log.warn("Failed to find router bridge connected to {}", intBridgeId);
313 return Optional.empty();
314 }
315 return node.routerBridge();
316 }
317
318 @Override
319 public Optional<PortNumber> externalPort(DeviceId intBridgeId) {
320 return deviceService.getPorts(intBridgeId).stream()
321 .filter(p -> p.annotations().value(PORT_NAME).equals(PATCH_INTG_BRIDGE) &&
322 p.isEnabled())
323 .map(Port::number).findFirst();
324 }
325
326 private void initNode(OpenstackNode node) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700327 NodeState state = node.state();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700328 state.process(this, node);
329 log.debug("Processing node: {} state: {}", node.hostname(), state);
330 }
331
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700332 private void setNodeState(OpenstackNode node, NodeState newState) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700333 if (node.state() != newState) {
334 log.debug("Changed {} state: {}", node.hostname(), newState);
335 nodeStore.put(node.hostname(), OpenstackNode.getUpdatedNode(node, newState));
336 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700337 }
338
339 private NodeState nodeState(OpenstackNode node) {
Frank Wang39b95772017-03-01 15:35:13 +0800340 if (!isOvsdbConnected(node) || !deviceService.isAvailable(node.intBridge()) ||
341 !isBridgeCreated(node.ovsdbId(), INTEGRATION_BRIDGE)) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700342 return INIT;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900343 }
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700344
345 // TODO use device service when we use single ONOS cluster for both openstackNode and vRouter
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700346 if (node.type().equals(NodeType.GATEWAY) &&
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700347 !isBridgeCreated(node.ovsdbId(), ROUTER_BRIDGE)) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700348 return INIT;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900349 }
350
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700351 if (!isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700352 return DEVICE_CREATED;
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700353 }
354
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700355 if (node.type().equals(NodeType.GATEWAY) && (
356 !isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE) ||
357 !isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) ||
358 !isIfaceCreated(node.ovsdbId(), node.uplink().get()))) {
359 return DEVICE_CREATED;
360 }
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700361 return COMPLETE;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900362 }
363
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700364 private boolean isIfaceCreated(DeviceId deviceId, String ifaceName) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700365 Device device = deviceService.getDevice(deviceId);
366 if (device == null || !device.is(BridgeConfig.class)) {
367 return false;
368 }
369
370 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
371 return bridgeConfig.getPorts().stream()
372 .filter(port -> port.annotations().value(PORT_NAME).equals(ifaceName))
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700373 .findAny()
374 .isPresent();
375 }
376
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700377 private boolean isBridgeCreated(DeviceId deviceId, String bridgeName) {
378 Device device = deviceService.getDevice(deviceId);
379 if (device == null || !device.is(BridgeConfig.class)) {
380 return false;
381 }
382
383 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
384 return bridgeConfig.getBridges().stream()
385 .filter(bridge -> bridge.name().equals(bridgeName))
386 .findAny()
387 .isPresent();
388 }
389
390 private void createBridge(OpenstackNode node, String bridgeName, DeviceId deviceId) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700391 Device device = deviceService.getDevice(node.ovsdbId());
392 if (device == null || !device.is(BridgeConfig.class)) {
393 log.error("Failed to create integration bridge on {}", node.ovsdbId());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900394 return;
395 }
396
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700397 // TODO fix this when we use single ONOS cluster for both openstackNode and vRouter
398 Set<IpAddress> controllerIps;
399 if (bridgeName.equals(ROUTER_BRIDGE)) {
400 controllerIps = Sets.newHashSet(node.routerController().get());
401 } else {
402 controllerIps = clusterService.getNodes().stream()
403 .map(ControllerNode::ip)
404 .collect(Collectors.toSet());
405 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900406
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700407 List<ControllerInfo> controllers = controllerIps.stream()
408 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
409 .collect(Collectors.toList());
410
411 String dpid = deviceId.toString().substring(DPID_BEGIN);
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700412 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700413 .name(bridgeName)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700414 .failMode(BridgeDescription.FailMode.SECURE)
415 .datapathId(dpid)
416 .disableInBand()
417 .controllers(controllers)
418 .build();
419
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700420 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700421 bridgeConfig.addBridge(bridgeDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900422 }
423
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900424 private void createTunnelInterface(OpenstackNode node) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700425 if (isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
426 return;
427 }
428
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700429 Device device = deviceService.getDevice(node.ovsdbId());
430 if (device == null || !device.is(InterfaceConfig.class)) {
431 log.error("Failed to create tunnel interface on {}", node.ovsdbId());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900432 return;
433 }
434
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700435 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
436 .deviceId(INTEGRATION_BRIDGE)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700437 .ifaceName(DEFAULT_TUNNEL)
438 .type(VXLAN)
439 .remote(TunnelEndPoints.flowTunnelEndpoint())
440 .key(TunnelKeys.flowTunnelKey())
441 .build();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700442
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700443 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700444 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900445 }
446
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700447 private void createPatchInterface(OpenstackNode node) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700448 checkArgument(node.type().equals(NodeType.GATEWAY));
449 if (isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) &&
450 isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE)) {
451 return;
452 }
453
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700454 Device device = deviceService.getDevice(node.ovsdbId());
455 if (device == null || !device.is(InterfaceConfig.class)) {
456 log.error("Failed to create patch interfaces on {}", node.hostname());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900457 return;
458 }
459
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700460 PatchDescription patchIntg = DefaultPatchDescription.builder()
461 .deviceId(INTEGRATION_BRIDGE)
462 .ifaceName(PATCH_INTG_BRIDGE)
463 .peer(PATCH_ROUT_BRIDGE)
464 .build();
465
466 PatchDescription patchRout = DefaultPatchDescription.builder()
467 .deviceId(ROUTER_BRIDGE)
468 .ifaceName(PATCH_ROUT_BRIDGE)
469 .peer(PATCH_INTG_BRIDGE)
470 .build();
471
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700472 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700473 ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
474 ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
475 }
476
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700477 private void addUplink(OpenstackNode node) {
478 checkArgument(node.type().equals(NodeType.GATEWAY));
479 if (isIfaceCreated(node.ovsdbId(), node.uplink().get())) {
480 return;
481 }
482
483 Device device = deviceService.getDevice(node.ovsdbId());
484 if (device == null || !device.is(BridgeConfig.class)) {
485 log.error("Failed to add port {} on {}", node.uplink().get(), node.ovsdbId());
486 return;
487 }
488
489 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
490 bridgeConfig.addPort(BridgeName.bridgeName(ROUTER_BRIDGE),
491 node.uplink().get());
492 }
493
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700494 private boolean isOvsdbConnected(OpenstackNode node) {
495 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700496 OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700497 return deviceService.isAvailable(node.ovsdbId()) &&
498 client != null &&
499 client.isConnected();
500 }
501
502 private void connectOvsdb(OpenstackNode node) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700503 ovsdbController.connect(node.managementIp(), TpPort.tpPort(ovsdbPort));
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700504 }
505
506 private Set<String> systemIfaces(OpenstackNode node) {
507 Set<String> ifaces = Sets.newHashSet(DEFAULT_TUNNEL);
508 if (node.type().equals(NodeType.GATEWAY)) {
509 ifaces.add(PATCH_INTG_BRIDGE);
510 ifaces.add(PATCH_ROUT_BRIDGE);
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700511 ifaces.add(node.uplink().get());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700512 }
513 return ifaces;
514 }
515
516 private OpenstackNode nodeByDeviceId(DeviceId deviceId) {
517 OpenstackNode node = nodes().stream()
518 .filter(n -> n.intBridge().equals(deviceId))
519 .findFirst().orElseGet(() -> nodes().stream()
520 .filter(n -> n.routerBridge().isPresent())
521 .filter(n -> n.routerBridge().get().equals(deviceId))
Frank Wang39b95772017-03-01 15:35:13 +0800522 .findFirst().orElseGet(() -> nodes().stream()
523 .filter(n -> n.ovsdbId().equals(deviceId))
524 .findFirst().orElse(null)));
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700525 return node;
526 }
527
528 private class OvsdbHandler implements ConnectionHandler<Device> {
529
530 @Override
531 public void connected(Device device) {
532 OpenstackNode node = nodes().stream()
533 .filter(n -> n.ovsdbId().equals(device.id()))
534 .findFirst()
535 .orElse(null);
536 if (node != null) {
537 setNodeState(node, nodeState(node));
538 } else {
539 log.debug("{} is detected on unregistered node, ignore it.", device.id());
540 }
541 }
542
543 @Override
544 public void disconnected(Device device) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700545 OpenstackNode node = nodeByDeviceId(device.id());
546 if (node != null) {
547 log.warn("Device {} is disconnected", device.id());
548 setNodeState(node, NodeState.INCOMPLETE);
549 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700550 }
551 }
552
553 private class BridgeHandler implements ConnectionHandler<Device> {
554
555 @Override
556 public void connected(Device device) {
557 OpenstackNode node = nodeByDeviceId(device.id());
558 if (node != null) {
559 setNodeState(node, nodeState(node));
560 } else {
561 log.debug("{} is detected on unregistered node, ignore it.", device.id());
562 }
563 }
564
565 @Override
566 public void disconnected(Device device) {
567 OpenstackNode node = nodeByDeviceId(device.id());
568 if (node != null) {
569 log.warn("Device {} is disconnected", device.id());
570 setNodeState(node, NodeState.INCOMPLETE);
571 }
572 }
573
574 /**
575 * Handles port added situation.
576 * If the added port is tunnel or data plane interface, proceed to the remaining
577 * node initialization. Otherwise, do nothing.
578 *
579 * @param port port
580 */
581 public void portAdded(Port port) {
582 OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
583 String portName = port.annotations().value(PORT_NAME);
584 if (node == null) {
585 log.debug("{} is added to unregistered node, ignore it.", portName);
586 return;
587 }
588
589 log.info("Port {} is added to {}", portName, node.hostname());
590 if (systemIfaces(node).contains(portName)) {
591 setNodeState(node, nodeState(node));
592 }
593 }
594
595 /**
596 * Handles port removed situation.
597 * If the removed port is tunnel or data plane interface, proceed to the remaining
598 * node initialization.Others, do nothing.
599 *
600 * @param port port
601 */
602 public void portRemoved(Port port) {
603 OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
604 String portName = port.annotations().value(PORT_NAME);
605
606 if (node == null) {
607 return;
608 }
609
610 log.info("Port {} is removed from {}", portName, node.hostname());
611 if (systemIfaces(node).contains(portName)) {
612 setNodeState(node, NodeState.INCOMPLETE);
613 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900614 }
615 }
616
617 private class InternalDeviceListener implements DeviceListener {
618
619 @Override
620 public void event(DeviceEvent event) {
Daniel Parkad21c572016-03-09 10:18:24 +0900621
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700622 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
623 if (!Objects.equals(localNodeId, leaderNodeId)) {
624 // do not allow to proceed without leadership
Daniel Parkad21c572016-03-09 10:18:24 +0900625 return;
626 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900627
628 Device device = event.subject();
629 ConnectionHandler<Device> handler =
630 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
631
632 switch (event.type()) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700633 // TODO implement OVSDB port event so that we can handle updates on the OVSDB
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900634 case PORT_ADDED:
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700635 eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900636 break;
637 case PORT_UPDATED:
638 if (!event.port().isEnabled()) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700639 eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900640 }
641 break;
642 case DEVICE_ADDED:
643 case DEVICE_AVAILABILITY_CHANGED:
644 if (deviceService.isAvailable(device.id())) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700645 eventExecutor.execute(() -> handler.connected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900646 } else {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700647 eventExecutor.execute(() -> handler.disconnected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900648 }
649 break;
650 default:
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900651 break;
652 }
653 }
654 }
655
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900656 private void readConfiguration() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700657 OpenstackNodeConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900658 if (config == null) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700659 log.debug("No configuration found");
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900660 return;
661 }
sangho24556ec2016-08-25 10:41:14 +0900662
663 Map<String, OpenstackNode> prevNodeMap = new HashMap(nodeStore.asJavaMap());
664 config.openstackNodes().forEach(node -> {
665 prevNodeMap.remove(node.hostname());
666 addOrUpdateNode(node);
667 });
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700668 prevNodeMap.values().forEach(this::deleteNode);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900669 }
670
671 private class InternalConfigListener implements NetworkConfigListener {
672
673 @Override
674 public void event(NetworkConfigEvent event) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700675 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
676 if (!Objects.equals(localNodeId, leaderNodeId)) {
677 // do not allow to proceed without leadership
678 return;
679 }
680
681 if (!event.configClass().equals(CONFIG_CLASS)) {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900682 return;
683 }
684
685 switch (event.type()) {
686 case CONFIG_ADDED:
687 case CONFIG_UPDATED:
688 eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
689 break;
690 default:
691 break;
692 }
693 }
694 }
695
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700696 private class InternalMapListener implements MapEventListener<String, OpenstackNode> {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900697
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700698 @Override
699 public void event(MapEvent<String, OpenstackNode> event) {
700 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
701 if (!Objects.equals(localNodeId, leaderNodeId)) {
702 // do not allow to proceed without leadership
703 return;
704 }
705
706 OpenstackNode oldNode;
707 OpenstackNode newNode;
708
709 switch (event.type()) {
710 case UPDATE:
711 oldNode = event.oldValue().value();
712 newNode = event.newValue().value();
713
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700714 log.info("Reloaded {}", newNode.hostname());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700715 if (!newNode.equals(oldNode)) {
716 log.debug("New node: {}", newNode);
717 }
718 // performs init procedure even if the node is not changed
719 // for robustness since it's no harm to run init procedure
720 // multiple times
721 eventExecutor.execute(() -> initNode(newNode));
722 break;
723 case INSERT:
724 newNode = event.newValue().value();
725 log.info("Added {}", newNode.hostname());
726 eventExecutor.execute(() -> initNode(newNode));
727 break;
728 case REMOVE:
729 oldNode = event.oldValue().value();
730 log.info("Removed {}", oldNode.hostname());
731 break;
732 default:
733 break;
734 }
735 }
736 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900737}