blob: a5bcaf553ff0c2e0d8cafa8d3a101c2827eb37f7 [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) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700340 if (!isOvsdbConnected(node) || !deviceService.isAvailable(node.intBridge())) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700341 return INIT;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900342 }
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700343
344 // TODO use device service when we use single ONOS cluster for both openstackNode and vRouter
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700345 if (node.type().equals(NodeType.GATEWAY) &&
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700346 !isBridgeCreated(node.ovsdbId(), ROUTER_BRIDGE)) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700347 return INIT;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900348 }
349
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700350 if (!isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700351 return DEVICE_CREATED;
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700352 }
353
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700354 if (node.type().equals(NodeType.GATEWAY) && (
355 !isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE) ||
356 !isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) ||
357 !isIfaceCreated(node.ovsdbId(), node.uplink().get()))) {
358 return DEVICE_CREATED;
359 }
Hyunsun Moon05d9b262016-07-03 18:38:44 -0700360 return COMPLETE;
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900361 }
362
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700363 private boolean isIfaceCreated(DeviceId deviceId, String ifaceName) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700364 Device device = deviceService.getDevice(deviceId);
365 if (device == null || !device.is(BridgeConfig.class)) {
366 return false;
367 }
368
369 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
370 return bridgeConfig.getPorts().stream()
371 .filter(port -> port.annotations().value(PORT_NAME).equals(ifaceName))
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700372 .findAny()
373 .isPresent();
374 }
375
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700376 private boolean isBridgeCreated(DeviceId deviceId, String bridgeName) {
377 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.getBridges().stream()
384 .filter(bridge -> bridge.name().equals(bridgeName))
385 .findAny()
386 .isPresent();
387 }
388
389 private void createBridge(OpenstackNode node, String bridgeName, DeviceId deviceId) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700390 Device device = deviceService.getDevice(node.ovsdbId());
391 if (device == null || !device.is(BridgeConfig.class)) {
392 log.error("Failed to create integration bridge on {}", node.ovsdbId());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900393 return;
394 }
395
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700396 // TODO fix this when we use single ONOS cluster for both openstackNode and vRouter
397 Set<IpAddress> controllerIps;
398 if (bridgeName.equals(ROUTER_BRIDGE)) {
399 controllerIps = Sets.newHashSet(node.routerController().get());
400 } else {
401 controllerIps = clusterService.getNodes().stream()
402 .map(ControllerNode::ip)
403 .collect(Collectors.toSet());
404 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900405
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700406 List<ControllerInfo> controllers = controllerIps.stream()
407 .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
408 .collect(Collectors.toList());
409
410 String dpid = deviceId.toString().substring(DPID_BEGIN);
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700411 BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700412 .name(bridgeName)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700413 .failMode(BridgeDescription.FailMode.SECURE)
414 .datapathId(dpid)
415 .disableInBand()
416 .controllers(controllers)
417 .build();
418
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700419 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700420 bridgeConfig.addBridge(bridgeDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900421 }
422
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900423 private void createTunnelInterface(OpenstackNode node) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700424 if (isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
425 return;
426 }
427
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700428 Device device = deviceService.getDevice(node.ovsdbId());
429 if (device == null || !device.is(InterfaceConfig.class)) {
430 log.error("Failed to create tunnel interface on {}", node.ovsdbId());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900431 return;
432 }
433
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700434 TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
435 .deviceId(INTEGRATION_BRIDGE)
Hyunsun Moondd14e8e2016-06-09 16:17:32 -0700436 .ifaceName(DEFAULT_TUNNEL)
437 .type(VXLAN)
438 .remote(TunnelEndPoints.flowTunnelEndpoint())
439 .key(TunnelKeys.flowTunnelKey())
440 .build();
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700441
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700442 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700443 ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900444 }
445
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700446 private void createPatchInterface(OpenstackNode node) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700447 checkArgument(node.type().equals(NodeType.GATEWAY));
448 if (isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) &&
449 isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE)) {
450 return;
451 }
452
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700453 Device device = deviceService.getDevice(node.ovsdbId());
454 if (device == null || !device.is(InterfaceConfig.class)) {
455 log.error("Failed to create patch interfaces on {}", node.hostname());
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900456 return;
457 }
458
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700459 PatchDescription patchIntg = DefaultPatchDescription.builder()
460 .deviceId(INTEGRATION_BRIDGE)
461 .ifaceName(PATCH_INTG_BRIDGE)
462 .peer(PATCH_ROUT_BRIDGE)
463 .build();
464
465 PatchDescription patchRout = DefaultPatchDescription.builder()
466 .deviceId(ROUTER_BRIDGE)
467 .ifaceName(PATCH_ROUT_BRIDGE)
468 .peer(PATCH_INTG_BRIDGE)
469 .build();
470
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700471 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700472 ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
473 ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
474 }
475
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700476 private void addUplink(OpenstackNode node) {
477 checkArgument(node.type().equals(NodeType.GATEWAY));
478 if (isIfaceCreated(node.ovsdbId(), node.uplink().get())) {
479 return;
480 }
481
482 Device device = deviceService.getDevice(node.ovsdbId());
483 if (device == null || !device.is(BridgeConfig.class)) {
484 log.error("Failed to add port {} on {}", node.uplink().get(), node.ovsdbId());
485 return;
486 }
487
488 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
489 bridgeConfig.addPort(BridgeName.bridgeName(ROUTER_BRIDGE),
490 node.uplink().get());
491 }
492
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700493 private boolean isOvsdbConnected(OpenstackNode node) {
494 OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700495 OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700496 return deviceService.isAvailable(node.ovsdbId()) &&
497 client != null &&
498 client.isConnected();
499 }
500
501 private void connectOvsdb(OpenstackNode node) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700502 ovsdbController.connect(node.managementIp(), TpPort.tpPort(ovsdbPort));
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700503 }
504
505 private Set<String> systemIfaces(OpenstackNode node) {
506 Set<String> ifaces = Sets.newHashSet(DEFAULT_TUNNEL);
507 if (node.type().equals(NodeType.GATEWAY)) {
508 ifaces.add(PATCH_INTG_BRIDGE);
509 ifaces.add(PATCH_ROUT_BRIDGE);
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700510 ifaces.add(node.uplink().get());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700511 }
512 return ifaces;
513 }
514
515 private OpenstackNode nodeByDeviceId(DeviceId deviceId) {
516 OpenstackNode node = nodes().stream()
517 .filter(n -> n.intBridge().equals(deviceId))
518 .findFirst().orElseGet(() -> nodes().stream()
519 .filter(n -> n.routerBridge().isPresent())
520 .filter(n -> n.routerBridge().get().equals(deviceId))
521 .findFirst().orElse(null));
522
523 return node;
524 }
525
526 private class OvsdbHandler implements ConnectionHandler<Device> {
527
528 @Override
529 public void connected(Device device) {
530 OpenstackNode node = nodes().stream()
531 .filter(n -> n.ovsdbId().equals(device.id()))
532 .findFirst()
533 .orElse(null);
534 if (node != null) {
535 setNodeState(node, nodeState(node));
536 } else {
537 log.debug("{} is detected on unregistered node, ignore it.", device.id());
538 }
539 }
540
541 @Override
542 public void disconnected(Device device) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700543 OpenstackNode node = nodeByDeviceId(device.id());
544 if (node != null) {
545 log.warn("Device {} is disconnected", device.id());
546 setNodeState(node, NodeState.INCOMPLETE);
547 }
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700548 }
549 }
550
551 private class BridgeHandler implements ConnectionHandler<Device> {
552
553 @Override
554 public void connected(Device device) {
555 OpenstackNode node = nodeByDeviceId(device.id());
556 if (node != null) {
557 setNodeState(node, nodeState(node));
558 } else {
559 log.debug("{} is detected on unregistered node, ignore it.", device.id());
560 }
561 }
562
563 @Override
564 public void disconnected(Device device) {
565 OpenstackNode node = nodeByDeviceId(device.id());
566 if (node != null) {
567 log.warn("Device {} is disconnected", device.id());
568 setNodeState(node, NodeState.INCOMPLETE);
569 }
570 }
571
572 /**
573 * Handles port added situation.
574 * If the added port is tunnel or data plane interface, proceed to the remaining
575 * node initialization. Otherwise, do nothing.
576 *
577 * @param port port
578 */
579 public void portAdded(Port port) {
580 OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
581 String portName = port.annotations().value(PORT_NAME);
582 if (node == null) {
583 log.debug("{} is added to unregistered node, ignore it.", portName);
584 return;
585 }
586
587 log.info("Port {} is added to {}", portName, node.hostname());
588 if (systemIfaces(node).contains(portName)) {
589 setNodeState(node, nodeState(node));
590 }
591 }
592
593 /**
594 * Handles port removed situation.
595 * If the removed port is tunnel or data plane interface, proceed to the remaining
596 * node initialization.Others, do nothing.
597 *
598 * @param port port
599 */
600 public void portRemoved(Port port) {
601 OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
602 String portName = port.annotations().value(PORT_NAME);
603
604 if (node == null) {
605 return;
606 }
607
608 log.info("Port {} is removed from {}", portName, node.hostname());
609 if (systemIfaces(node).contains(portName)) {
610 setNodeState(node, NodeState.INCOMPLETE);
611 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900612 }
613 }
614
615 private class InternalDeviceListener implements DeviceListener {
616
617 @Override
618 public void event(DeviceEvent event) {
Daniel Parkad21c572016-03-09 10:18:24 +0900619
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700620 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
621 if (!Objects.equals(localNodeId, leaderNodeId)) {
622 // do not allow to proceed without leadership
Daniel Parkad21c572016-03-09 10:18:24 +0900623 return;
624 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900625
626 Device device = event.subject();
627 ConnectionHandler<Device> handler =
628 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
629
630 switch (event.type()) {
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700631 // TODO implement OVSDB port event so that we can handle updates on the OVSDB
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900632 case PORT_ADDED:
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700633 eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900634 break;
635 case PORT_UPDATED:
636 if (!event.port().isEnabled()) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700637 eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900638 }
639 break;
640 case DEVICE_ADDED:
641 case DEVICE_AVAILABILITY_CHANGED:
642 if (deviceService.isAvailable(device.id())) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700643 eventExecutor.execute(() -> handler.connected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900644 } else {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700645 eventExecutor.execute(() -> handler.disconnected(device));
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900646 }
647 break;
648 default:
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900649 break;
650 }
651 }
652 }
653
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900654 private void readConfiguration() {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700655 OpenstackNodeConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900656 if (config == null) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700657 log.debug("No configuration found");
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900658 return;
659 }
sangho24556ec2016-08-25 10:41:14 +0900660
661 Map<String, OpenstackNode> prevNodeMap = new HashMap(nodeStore.asJavaMap());
662 config.openstackNodes().forEach(node -> {
663 prevNodeMap.remove(node.hostname());
664 addOrUpdateNode(node);
665 });
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700666 prevNodeMap.values().forEach(this::deleteNode);
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900667 }
668
669 private class InternalConfigListener implements NetworkConfigListener {
670
671 @Override
672 public void event(NetworkConfigEvent event) {
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700673 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
674 if (!Objects.equals(localNodeId, leaderNodeId)) {
675 // do not allow to proceed without leadership
676 return;
677 }
678
679 if (!event.configClass().equals(CONFIG_CLASS)) {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900680 return;
681 }
682
683 switch (event.type()) {
684 case CONFIG_ADDED:
685 case CONFIG_UPDATED:
686 eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
687 break;
688 default:
689 break;
690 }
691 }
692 }
693
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700694 private class InternalMapListener implements MapEventListener<String, OpenstackNode> {
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900695
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700696 @Override
697 public void event(MapEvent<String, OpenstackNode> event) {
698 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
699 if (!Objects.equals(localNodeId, leaderNodeId)) {
700 // do not allow to proceed without leadership
701 return;
702 }
703
704 OpenstackNode oldNode;
705 OpenstackNode newNode;
706
707 switch (event.type()) {
708 case UPDATE:
709 oldNode = event.oldValue().value();
710 newNode = event.newValue().value();
711
Hyunsun Moon052c71f2016-07-11 18:56:18 -0700712 log.info("Reloaded {}", newNode.hostname());
Hyunsun Moon34bbe172016-06-28 19:18:40 -0700713 if (!newNode.equals(oldNode)) {
714 log.debug("New node: {}", newNode);
715 }
716 // performs init procedure even if the node is not changed
717 // for robustness since it's no harm to run init procedure
718 // multiple times
719 eventExecutor.execute(() -> initNode(newNode));
720 break;
721 case INSERT:
722 newNode = event.newValue().value();
723 log.info("Added {}", newNode.hostname());
724 eventExecutor.execute(() -> initNode(newNode));
725 break;
726 case REMOVE:
727 oldNode = event.oldValue().value();
728 log.info("Removed {}", oldNode.hostname());
729 break;
730 default:
731 break;
732 }
733 }
734 }
Daniel Parka7d6e9f2016-01-18 17:54:14 +0900735}