blob: 6094e72522fe77c602141f4aadf1bd0cd36d81e4 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Hyunsun Moon44aac662017-02-18 02:07:01 +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.openstacknetworking.impl;
17
18import com.google.common.base.Strings;
Jian Li24ec59f2018-05-23 19:01:25 +090019import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
Hyunsun Moon44aac662017-02-18 02:07:01 +090021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.MacAddress;
29import org.onlab.packet.VlanId;
30import org.onlab.util.Tools;
31import org.onosproject.core.CoreService;
32import org.onosproject.mastership.MastershipService;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.DefaultAnnotations;
Jian Li24ec59f2018-05-23 19:01:25 +090035import org.onosproject.net.DefaultHost;
Hyunsun Moon44aac662017-02-18 02:07:01 +090036import org.onosproject.net.Device;
Jian Li24ec59f2018-05-23 19:01:25 +090037import org.onosproject.net.DeviceId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090038import org.onosproject.net.Host;
39import org.onosproject.net.HostId;
40import org.onosproject.net.HostLocation;
41import org.onosproject.net.Port;
42import org.onosproject.net.device.DeviceEvent;
43import org.onosproject.net.device.DeviceListener;
44import org.onosproject.net.device.DeviceService;
45import org.onosproject.net.host.DefaultHostDescription;
46import org.onosproject.net.host.HostDescription;
47import org.onosproject.net.host.HostProvider;
48import org.onosproject.net.host.HostProviderRegistry;
49import org.onosproject.net.host.HostProviderService;
50import org.onosproject.net.host.HostService;
51import org.onosproject.net.provider.AbstractProvider;
52import org.onosproject.net.provider.ProviderId;
Jian Li24ec59f2018-05-23 19:01:25 +090053import org.onosproject.openstacknetworking.api.InstancePort;
54import org.onosproject.openstacknetworking.api.InstancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090055import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090056import org.onosproject.openstacknode.api.OpenstackNode;
57import org.onosproject.openstacknode.api.OpenstackNodeEvent;
58import org.onosproject.openstacknode.api.OpenstackNodeListener;
59import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090060import org.openstack4j.model.network.Network;
daniel park796c2eb2018-03-22 17:01:51 +090061import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090062import org.slf4j.Logger;
63import org.slf4j.LoggerFactory;
64
Jian Li24ec59f2018-05-23 19:01:25 +090065import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090066import java.util.Set;
67import java.util.concurrent.ExecutorService;
68import java.util.concurrent.Executors;
69import java.util.stream.Collectors;
70
71import static org.onlab.util.Tools.groupedThreads;
72import static org.onosproject.net.AnnotationKeys.PORT_NAME;
daniel park796c2eb2018-03-22 17:01:51 +090073import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Ray Milkey9dc57392018-06-08 08:52:31 -070074import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Hyunsun Moon44aac662017-02-18 02:07:01 +090075import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_CREATE_TIME;
76import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
77import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Jian Li51b844c2018-05-31 10:59:03 +090078import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon44aac662017-02-18 02:07:01 +090079
80@Service
81@Component(immediate = true)
82public final class OpenstackSwitchingHostProvider extends AbstractProvider implements HostProvider {
83
84 private final Logger log = LoggerFactory.getLogger(getClass());
85
86 private static final String PORT_NAME_PREFIX_VM = "tap";
87 private static final String ERR_ADD_HOST = "Failed to add host: ";
daniel park5fe30792017-08-22 15:42:54 +090088 private static final String ANNOTATION_SEGMENT_ID = "segId";
Jian Li9d676fb2018-03-02 15:25:36 +090089 private static final String SONA_HOST_SCHEME = "sona";
Hyunsun Moon44aac662017-02-18 02:07:01 +090090
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090092 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090093
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090095 protected DeviceService deviceService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090096
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090098 protected HostProviderRegistry hostProviderRegistry;
Hyunsun Moon44aac662017-02-18 02:07:01 +090099
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +0900101 protected HostService hostService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +0900104 protected MastershipService mastershipService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +0900107 protected OpenstackNetworkService osNetworkService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +0900110 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900111
Jian Li24ec59f2018-05-23 19:01:25 +0900112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected InstancePortService instancePortService;
114
Hyunsun Moon44aac662017-02-18 02:07:01 +0900115 private final ExecutorService deviceEventExecutor =
116 Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event"));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900117 private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
118 private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
119
120 private HostProviderService hostProvider;
121
Jian Li24ec59f2018-05-23 19:01:25 +0900122 private Map<HostId, Device> hostDeviceMap = Maps.newConcurrentMap();
123 private Set<Host> migratingHosts = Sets.newConcurrentHashSet();
124
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125 /**
126 * Creates OpenStack switching host provider.
127 */
128 public OpenstackSwitchingHostProvider() {
Jian Li9d676fb2018-03-02 15:25:36 +0900129 super(new ProviderId(SONA_HOST_SCHEME, OPENSTACK_NETWORKING_APP_ID));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900130 }
131
132 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800133 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900134 coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
135 deviceService.addListener(internalDeviceListener);
136 osNodeService.addListener(internalNodeListener);
137 hostProvider = hostProviderRegistry.register(this);
138
139 log.info("Started");
140 }
141
142 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800143 void deactivate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900144 hostProviderRegistry.unregister(this);
145 osNodeService.removeListener(internalNodeListener);
146 deviceService.removeListener(internalDeviceListener);
147
148 deviceEventExecutor.shutdown();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900149
150 log.info("Stopped");
151 }
152
153 @Override
154 public void triggerProbe(Host host) {
155 // no probe is required
156 }
157
Jian Li9d676fb2018-03-02 15:25:36 +0900158 /**
159 * Processes port addition event.
160 * Once a port addition event is detected, it tries to create a host instance
161 * with openstack augmented host information such as networkId, portId,
162 * createTime, segmentId and notifies to host provider.
163 *
164 * @param port port object used in ONOS
165 */
Jian Li24ec59f2018-05-23 19:01:25 +0900166 private void processPortAdded(Port port, Device device) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900167 // TODO check the node state is COMPLETE
168 org.openstack4j.model.network.Port osPort = osNetworkService.port(port);
169 if (osPort == null) {
170 log.warn(ERR_ADD_HOST + "OpenStack port for {} not found", port);
171 return;
172 }
173
174 Network osNet = osNetworkService.network(osPort.getNetworkId());
175 if (osNet == null) {
176 log.warn(ERR_ADD_HOST + "OpenStack network {} not found",
177 osPort.getNetworkId());
178 return;
179 }
180
181 if (osPort.getFixedIps().isEmpty()) {
182 log.warn(ERR_ADD_HOST + "no fixed IP for port {}", osPort.getId());
183 return;
184 }
185
186 MacAddress macAddr = MacAddress.valueOf(osPort.getMacAddress());
187 Set<IpAddress> fixedIps = osPort.getFixedIps().stream()
188 .map(ip -> IpAddress.valueOf(ip.getIpAddress()))
189 .collect(Collectors.toSet());
190 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
Jian Li24ec59f2018-05-23 19:01:25 +0900191 HostId oldHostId = HostId.hostId(macAddr);
192
193 // In VM migration case, a duplicated host port (port created in at new
194 // compute node) will be detected at OVS; in this case, we will store
195 // the old host instance into migration list, and overwrite old host
196 // with new host instance issue host creation event to ONOS core
197 Device oldDevice = hostDeviceMap.get(oldHostId);
198
199 if (device != null && oldDevice != null && !oldDevice.equals(device)) {
200 Host host = hostService.getHost(oldHostId);
201 if (host != null) {
202 migratingHosts.add(host);
203 }
204 }
205
206 if (device != null) {
207 hostDeviceMap.put(oldHostId, device);
208 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900209
210 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
211 .set(ANNOTATION_NETWORK_ID, osPort.getNetworkId())
212 .set(ANNOTATION_PORT_ID, osPort.getId())
daniel park796c2eb2018-03-22 17:01:51 +0900213 .set(ANNOTATION_CREATE_TIME, String.valueOf(System.currentTimeMillis()));
214
215 if (osNet.getNetworkType() != NetworkType.FLAT) {
216 annotations.set(ANNOTATION_SEGMENT_ID, osNet.getProviderSegID());
217
218 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900219
Jian Li24ec59f2018-05-23 19:01:25 +0900220 long currentTime = System.currentTimeMillis();
221
Hyunsun Moon44aac662017-02-18 02:07:01 +0900222 HostDescription hostDesc = new DefaultHostDescription(
223 macAddr,
224 VlanId.NONE,
Jian Li24ec59f2018-05-23 19:01:25 +0900225 new HostLocation(connectPoint, currentTime),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900226 fixedIps,
227 annotations.build());
228
229 HostId hostId = HostId.hostId(macAddr);
230 hostProvider.hostDetected(hostId, hostDesc, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900231
232 if (device != null && oldDevice != null && !oldDevice.equals(device)) {
233 Host oldHost = hostService.getHost(oldHostId);
234 Host newHost = new DefaultHost(oldHost.providerId(), hostId, macAddr,
235 VlanId.NONE, new HostLocation(connectPoint, currentTime),
236 fixedIps, annotations.build());
237 instancePortService.migrationPortAdded(HostBasedInstancePort.of(newHost));
238 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900239 }
240
Jian Li9d676fb2018-03-02 15:25:36 +0900241 /**
242 * Processes port removal event.
243 * Once a port removal event is detected, it tries to look for a host
244 * instance through host provider by giving connect point information,
245 * and vanishes it.
246 *
Jian Li24ec59f2018-05-23 19:01:25 +0900247 * @param event device event
Jian Li9d676fb2018-03-02 15:25:36 +0900248 */
Jian Li24ec59f2018-05-23 19:01:25 +0900249 private void processPortRemoved(DeviceEvent event) {
250 Port port = event.port();
251 DeviceId deviceId = event.subject().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900252 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
Jian Li24ec59f2018-05-23 19:01:25 +0900253
254 Set<Host> hostsToBeRemoved = hostService.getConnectedHosts(connectPoint);
255
256 if (hostsToBeRemoved.size() == 0) {
257
258 for (Host host : migratingHosts) {
259 if (host.location() == null) {
260 continue;
261 }
262 String hostLocation = host.location().toString();
263 StringBuilder deviceIdWithPort = new StringBuilder();
264 deviceIdWithPort.append(deviceId.toString());
265 deviceIdWithPort.append("/");
266 deviceIdWithPort.append(port.number().toString());
267
268 if (hostLocation.equals(deviceIdWithPort.toString())) {
269 InstancePort instPort = HostBasedInstancePort.of(host);
270 instancePortService.migrationPortRemoved(instPort);
271 migratingHosts.remove(host);
272 }
273 }
274
275 } else {
276 hostsToBeRemoved.forEach(host -> hostProvider.hostVanished(host.id()));
277 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900278 }
279
Jian Li9d676fb2018-03-02 15:25:36 +0900280 /**
281 * An internal device listener which listens the port events generated from
282 * OVS integration bridge.
283 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900284 private class InternalDeviceListener implements DeviceListener {
285
286 @Override
287 public boolean isRelevant(DeviceEvent event) {
288 Device device = event.subject();
289 if (!mastershipService.isLocalMaster(device.id())) {
290 // do not allow to proceed without mastership
291 return false;
292 }
293 Port port = event.port();
294 if (port == null) {
295 return false;
296 }
297 String portName = port.annotations().value(PORT_NAME);
Jian Li9d676fb2018-03-02 15:25:36 +0900298
299 return !Strings.isNullOrEmpty(portName) &&
Daniel Parkc4d06402018-05-28 15:57:37 +0900300 (portName.startsWith(PORT_NAME_PREFIX_VM) || isDirectPort(portName));
301 }
302
303 private boolean isDirectPort(String portName) {
Ray Milkey9dc57392018-06-08 08:52:31 -0700304 return portNamePrefixMap().values().stream().filter(p -> portName.startsWith(p)).findAny().isPresent();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900305 }
306
307 @Override
308 public void event(DeviceEvent event) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900309 log.info("Device event occurred with type {}", event.type());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900310 switch (event.type()) {
311 case PORT_UPDATED:
312 if (!event.port().isEnabled()) {
Jian Li9d676fb2018-03-02 15:25:36 +0900313 portRemovedHelper(deviceEventExecutor, event);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900314 } else if (event.port().isEnabled()) {
Jian Li9d676fb2018-03-02 15:25:36 +0900315 portAddedHelper(deviceEventExecutor, event);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900316 }
317 break;
318 case PORT_ADDED:
Jian Li9d676fb2018-03-02 15:25:36 +0900319 portAddedHelper(deviceEventExecutor, event);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900320 break;
Hyunsun Moonb7a9cd22017-02-24 11:12:53 +0900321 case PORT_REMOVED:
Jian Li9d676fb2018-03-02 15:25:36 +0900322 portRemovedHelper(deviceEventExecutor, event);
Ray Milkeyd6a67c32018-02-02 10:30:35 -0800323 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900324 default:
325 break;
326 }
327 }
328 }
329
Jian Li9d676fb2018-03-02 15:25:36 +0900330 /**
331 * A helper method which logs the port addition event and performs port
332 * addition action.
333 *
334 * @param executor device executor service
335 * @param event device event
336 */
337 private void portAddedHelper(ExecutorService executor, DeviceEvent event) {
338 executor.execute(() -> {
339 log.debug("Instance port {} is detected from {}",
340 event.port().annotations().value(PORT_NAME),
341 event.subject().id());
Jian Li24ec59f2018-05-23 19:01:25 +0900342 processPortAdded(event.port(), event.subject());
Jian Li9d676fb2018-03-02 15:25:36 +0900343 });
344 }
345
346 /**
347 * A helper method which logs the port removal event and performs port
348 * removal action.
349 *
350 * @param executor device executor service
351 * @param event device event
352 */
353 private void portRemovedHelper(ExecutorService executor, DeviceEvent event) {
354 executor.execute(() -> {
355 log.debug("Instance port {} is removed from {}",
356 event.port().annotations().value(PORT_NAME),
357 event.subject().id());
Jian Li24ec59f2018-05-23 19:01:25 +0900358 processPortRemoved(event);
Jian Li9d676fb2018-03-02 15:25:36 +0900359 });
360 }
361
Hyunsun Moon44aac662017-02-18 02:07:01 +0900362 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
363
364 @Override
Jian Li9d676fb2018-03-02 15:25:36 +0900365 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Li51b844c2018-05-31 10:59:03 +0900366
367 if (event.subject().type() == CONTROLLER) {
368 return false;
369 }
Jian Li9d676fb2018-03-02 15:25:36 +0900370 // do not allow to proceed without mastership
371 Device device = deviceService.getDevice(event.subject().intgBridge());
Jian Li7ddea782018-03-30 11:17:42 +0900372 if (device == null) {
373 return false;
374 }
Jian Li9d676fb2018-03-02 15:25:36 +0900375 return mastershipService.isLocalMaster(device.id());
376 }
377
378 @Override
Hyunsun Moon44aac662017-02-18 02:07:01 +0900379 public void event(OpenstackNodeEvent event) {
380 OpenstackNode osNode = event.subject();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900381
382 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900383 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900384 deviceEventExecutor.execute(() -> {
385 log.info("COMPLETE node {} is detected", osNode.hostname());
386 processCompleteNode(event.subject());
387 });
388 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900389 case OPENSTACK_NODE_INCOMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900390 log.warn("{} is changed to INCOMPLETE state", osNode);
391 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900392 case OPENSTACK_NODE_CREATED:
393 case OPENSTACK_NODE_UPDATED:
394 case OPENSTACK_NODE_REMOVED:
Jian Li9d676fb2018-03-02 15:25:36 +0900395 // not reacts to the events other than complete and incomplete states
396 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900397 default:
398 break;
399 }
400 }
401
402 private void processCompleteNode(OpenstackNode osNode) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900403 deviceService.getPorts(osNode.intgBridge()).stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900404 .filter(port -> port.annotations().value(PORT_NAME)
405 .startsWith(PORT_NAME_PREFIX_VM) &&
406 port.isEnabled())
407 .forEach(port -> {
408 log.debug("Instance port {} is detected from {}",
409 port.annotations().value(PORT_NAME),
410 osNode.hostname());
Jian Li24ec59f2018-05-23 19:01:25 +0900411 processPortAdded(port,
412 deviceService.getDevice(osNode.intgBridge()));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900413 });
414
Ray Milkey9dc57392018-06-08 08:52:31 -0700415 portNamePrefixMap().values().forEach(portNamePrefix -> {
Daniel Parkc4d06402018-05-28 15:57:37 +0900416 deviceService.getPorts(osNode.intgBridge()).stream()
417 .filter(port -> port.annotations().value(PORT_NAME)
418 .startsWith(portNamePrefix) &&
419 port.isEnabled())
420 .forEach(port -> {
421 log.debug("Instance port {} is detected from {}",
422 port.annotations().value(portNamePrefix),
423 osNode.hostname());
Jian Li24ec59f2018-05-23 19:01:25 +0900424 processPortAdded(port,
425 deviceService.getDevice(osNode.intgBridge()));
Daniel Parkc4d06402018-05-28 15:57:37 +0900426 });
427 });
428
Hyunsun Moon44aac662017-02-18 02:07:01 +0900429 Tools.stream(hostService.getHosts())
430 .filter(host -> deviceService.getPort(
431 host.location().deviceId(),
432 host.location().port()) == null)
433 .forEach(host -> {
434 log.info("Remove stale host {}", host.id());
435 hostProvider.hostVanished(host.id());
436 });
437 }
438 }
439}