blob: b2af7b3cea9941b3c8c379fa6f0c29d4ba7974b3 [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;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.packet.IpAddress;
26import org.onlab.packet.MacAddress;
27import org.onlab.packet.VlanId;
28import org.onlab.util.Tools;
29import org.onosproject.core.CoreService;
30import org.onosproject.mastership.MastershipService;
31import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.DefaultAnnotations;
33import org.onosproject.net.Device;
34import org.onosproject.net.Host;
35import org.onosproject.net.HostId;
36import org.onosproject.net.HostLocation;
37import org.onosproject.net.Port;
38import org.onosproject.net.device.DeviceEvent;
39import org.onosproject.net.device.DeviceListener;
40import org.onosproject.net.device.DeviceService;
41import org.onosproject.net.host.DefaultHostDescription;
42import org.onosproject.net.host.HostDescription;
43import org.onosproject.net.host.HostProvider;
44import org.onosproject.net.host.HostProviderRegistry;
45import org.onosproject.net.host.HostProviderService;
46import org.onosproject.net.host.HostService;
47import org.onosproject.net.provider.AbstractProvider;
48import org.onosproject.net.provider.ProviderId;
Jian Liec5c32b2018-07-13 14:28:58 +090049import org.onosproject.openstacknetworking.api.InstancePortAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090050import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090051import org.onosproject.openstacknode.api.OpenstackNode;
52import org.onosproject.openstacknode.api.OpenstackNodeEvent;
53import org.onosproject.openstacknode.api.OpenstackNodeListener;
54import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090055import org.openstack4j.model.network.Network;
daniel park796c2eb2018-03-22 17:01:51 +090056import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090057import org.slf4j.Logger;
58import org.slf4j.LoggerFactory;
59
Jian Liec5c32b2018-07-13 14:28:58 +090060import java.util.Optional;
Hyunsun Moon44aac662017-02-18 02:07:01 +090061import java.util.Set;
62import java.util.concurrent.ExecutorService;
63import java.util.concurrent.Executors;
64import java.util.stream.Collectors;
65
66import static org.onlab.util.Tools.groupedThreads;
67import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Jian Liec5c32b2018-07-13 14:28:58 +090068import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_CREATE_TIME;
69import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_NETWORK_ID;
70import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_PORT_ID;
71import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_SEGMENT_ID;
daniel park796c2eb2018-03-22 17:01:51 +090072import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Ray Milkey9dc57392018-06-08 08:52:31 -070073import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
Jian Li51b844c2018-05-31 10:59:03 +090074import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon44aac662017-02-18 02:07:01 +090075
76@Service
77@Component(immediate = true)
78public final class OpenstackSwitchingHostProvider extends AbstractProvider implements HostProvider {
79
80 private final Logger log = LoggerFactory.getLogger(getClass());
81
82 private static final String PORT_NAME_PREFIX_VM = "tap";
83 private static final String ERR_ADD_HOST = "Failed to add host: ";
Jian Li9d676fb2018-03-02 15:25:36 +090084 private static final String SONA_HOST_SCHEME = "sona";
Hyunsun Moon44aac662017-02-18 02:07:01 +090085
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090087 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090088
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090090 protected DeviceService deviceService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090091
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090093 protected HostProviderRegistry hostProviderRegistry;
Hyunsun Moon44aac662017-02-18 02:07:01 +090094
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090096 protected HostService hostService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090097
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090099 protected MastershipService mastershipService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +0900102 protected OpenstackNetworkService osNetworkService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +0900105 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900106
Jian Li24ec59f2018-05-23 19:01:25 +0900107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liec5c32b2018-07-13 14:28:58 +0900108 protected InstancePortAdminService instancePortAdminService;
Jian Li24ec59f2018-05-23 19:01:25 +0900109
Hyunsun Moon44aac662017-02-18 02:07:01 +0900110 private final ExecutorService deviceEventExecutor =
111 Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event"));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900112 private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
113 private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
114
115 private HostProviderService hostProvider;
116
117 /**
118 * Creates OpenStack switching host provider.
119 */
120 public OpenstackSwitchingHostProvider() {
Jian Li9d676fb2018-03-02 15:25:36 +0900121 super(new ProviderId(SONA_HOST_SCHEME, OPENSTACK_NETWORKING_APP_ID));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900122 }
123
124 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800125 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900126 coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
127 deviceService.addListener(internalDeviceListener);
128 osNodeService.addListener(internalNodeListener);
129 hostProvider = hostProviderRegistry.register(this);
130
131 log.info("Started");
132 }
133
134 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800135 void deactivate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900136 hostProviderRegistry.unregister(this);
137 osNodeService.removeListener(internalNodeListener);
138 deviceService.removeListener(internalDeviceListener);
139
140 deviceEventExecutor.shutdown();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900141
142 log.info("Stopped");
143 }
144
145 @Override
146 public void triggerProbe(Host host) {
147 // no probe is required
148 }
149
Jian Li9d676fb2018-03-02 15:25:36 +0900150 /**
151 * Processes port addition event.
152 * Once a port addition event is detected, it tries to create a host instance
153 * with openstack augmented host information such as networkId, portId,
154 * createTime, segmentId and notifies to host provider.
155 *
156 * @param port port object used in ONOS
157 */
Jian Li24ec59f2018-05-23 19:01:25 +0900158 private void processPortAdded(Port port, Device device) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900159 // TODO check the node state is COMPLETE
160 org.openstack4j.model.network.Port osPort = osNetworkService.port(port);
161 if (osPort == null) {
162 log.warn(ERR_ADD_HOST + "OpenStack port for {} not found", port);
163 return;
164 }
165
166 Network osNet = osNetworkService.network(osPort.getNetworkId());
167 if (osNet == null) {
168 log.warn(ERR_ADD_HOST + "OpenStack network {} not found",
169 osPort.getNetworkId());
170 return;
171 }
172
173 if (osPort.getFixedIps().isEmpty()) {
174 log.warn(ERR_ADD_HOST + "no fixed IP for port {}", osPort.getId());
175 return;
176 }
177
Jian Liec5c32b2018-07-13 14:28:58 +0900178 MacAddress mac = MacAddress.valueOf(osPort.getMacAddress());
179 HostId hostId = HostId.hostId(mac);
180
181 // typically one openstack port should only be bound to one fix IP address;
182 // however, openstack4j binds multiple fixed IPs to one port, this might
183 // be a defect of openstack4j implementation
184
185 // TODO: we need to find a way to bind multiple ports from multiple
186 // openstack networks into one host sooner or later
Hyunsun Moon44aac662017-02-18 02:07:01 +0900187 Set<IpAddress> fixedIps = osPort.getFixedIps().stream()
Jian Liec5c32b2018-07-13 14:28:58 +0900188 .map(ip -> IpAddress.valueOf(ip.getIpAddress()))
189 .collect(Collectors.toSet());
190
191 // connect point is the combination of switch ID with port number where
192 // the host is attached to
Hyunsun Moon44aac662017-02-18 02:07:01 +0900193 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
Jian Li24ec59f2018-05-23 19:01:25 +0900194
Jian Liec5c32b2018-07-13 14:28:58 +0900195 long createTime = System.currentTimeMillis();
Jian Li24ec59f2018-05-23 19:01:25 +0900196
Jian Liec5c32b2018-07-13 14:28:58 +0900197 // we check whether the host already attached to some locations
198 Host host = hostService.getHost(hostId);
199 if (host != null) {
200 Set<HostLocation> locations = host.locations().stream()
201 .filter(l -> l.deviceId().equals(connectPoint.deviceId()))
202 .filter(l -> l.port().equals(connectPoint.port()))
203 .collect(Collectors.toSet());
204 if (locations.size() == 0) {
205 hostProvider.addLocationToHost(hostId,
206 new HostLocation(connectPoint, createTime));
Jian Li24ec59f2018-05-23 19:01:25 +0900207 }
Jian Liec5c32b2018-07-13 14:28:58 +0900208 } else {
Jian Li24ec59f2018-05-23 19:01:25 +0900209
Jian Liec5c32b2018-07-13 14:28:58 +0900210 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
211 .set(ANNOTATION_NETWORK_ID, osPort.getNetworkId())
212 .set(ANNOTATION_PORT_ID, osPort.getId())
213 .set(ANNOTATION_CREATE_TIME, String.valueOf(createTime));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900214
Jian Liec5c32b2018-07-13 14:28:58 +0900215 // FLAT does not require segment ID
216 if (osNet.getNetworkType() != NetworkType.FLAT) {
217 annotations.set(ANNOTATION_SEGMENT_ID, osNet.getProviderSegID());
218 }
daniel park796c2eb2018-03-22 17:01:51 +0900219
Jian Liec5c32b2018-07-13 14:28:58 +0900220 HostDescription hostDesc = new DefaultHostDescription(
221 mac,
222 VlanId.NONE,
223 new HostLocation(connectPoint, createTime),
224 fixedIps,
225 annotations.build());
226 hostProvider.hostDetected(hostId, hostDesc, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900227 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900228 }
229
Jian Li9d676fb2018-03-02 15:25:36 +0900230 /**
231 * Processes port removal event.
232 * Once a port removal event is detected, it tries to look for a host
233 * instance through host provider by giving connect point information,
234 * and vanishes it.
235 *
Jian Li24ec59f2018-05-23 19:01:25 +0900236 * @param event device event
Jian Li9d676fb2018-03-02 15:25:36 +0900237 */
Jian Li24ec59f2018-05-23 19:01:25 +0900238 private void processPortRemoved(DeviceEvent event) {
239 Port port = event.port();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900240 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
Jian Li24ec59f2018-05-23 19:01:25 +0900241
Jian Liec5c32b2018-07-13 14:28:58 +0900242 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
Jian Li24ec59f2018-05-23 19:01:25 +0900243
Jian Liec5c32b2018-07-13 14:28:58 +0900244 hosts.forEach(h -> {
245 Optional<HostLocation> hostLocation = h.locations().stream()
246 .filter(l -> l.deviceId().equals(port.element().id()))
247 .filter(l -> l.port().equals(port.number())).findAny();
Jian Li24ec59f2018-05-23 19:01:25 +0900248
Jian Liec5c32b2018-07-13 14:28:58 +0900249 // if the host contains only one filtered location, we remove the host
250 if (h.locations().size() == 1) {
251 hostProvider.hostVanished(h.id());
Jian Li24ec59f2018-05-23 19:01:25 +0900252 }
253
Jian Liec5c32b2018-07-13 14:28:58 +0900254 // if the host contains multiple locations, we simply remove the
255 // host location
256 if (h.locations().size() > 1 && hostLocation.isPresent()) {
257 hostProvider.removeLocationFromHost(h.id(), hostLocation.get());
258 }
259 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900260 }
261
Jian Li9d676fb2018-03-02 15:25:36 +0900262 /**
263 * An internal device listener which listens the port events generated from
264 * OVS integration bridge.
265 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900266 private class InternalDeviceListener implements DeviceListener {
267
268 @Override
269 public boolean isRelevant(DeviceEvent event) {
270 Device device = event.subject();
271 if (!mastershipService.isLocalMaster(device.id())) {
272 // do not allow to proceed without mastership
273 return false;
274 }
275 Port port = event.port();
276 if (port == null) {
277 return false;
278 }
279 String portName = port.annotations().value(PORT_NAME);
Jian Li9d676fb2018-03-02 15:25:36 +0900280
281 return !Strings.isNullOrEmpty(portName) &&
Daniel Parkc4d06402018-05-28 15:57:37 +0900282 (portName.startsWith(PORT_NAME_PREFIX_VM) || isDirectPort(portName));
283 }
284
285 private boolean isDirectPort(String portName) {
Ray Milkey9dc57392018-06-08 08:52:31 -0700286 return portNamePrefixMap().values().stream().filter(p -> portName.startsWith(p)).findAny().isPresent();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900287 }
288
289 @Override
290 public void event(DeviceEvent event) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900291 log.info("Device event occurred with type {}", event.type());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900292 switch (event.type()) {
293 case PORT_UPDATED:
294 if (!event.port().isEnabled()) {
Jian Li9d676fb2018-03-02 15:25:36 +0900295 portRemovedHelper(deviceEventExecutor, event);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900296 } else if (event.port().isEnabled()) {
Jian Li9d676fb2018-03-02 15:25:36 +0900297 portAddedHelper(deviceEventExecutor, event);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900298 }
299 break;
300 case PORT_ADDED:
Jian Li9d676fb2018-03-02 15:25:36 +0900301 portAddedHelper(deviceEventExecutor, event);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900302 break;
Hyunsun Moonb7a9cd22017-02-24 11:12:53 +0900303 case PORT_REMOVED:
Jian Li9d676fb2018-03-02 15:25:36 +0900304 portRemovedHelper(deviceEventExecutor, event);
Ray Milkeyd6a67c32018-02-02 10:30:35 -0800305 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900306 default:
307 break;
308 }
309 }
310 }
311
Jian Li9d676fb2018-03-02 15:25:36 +0900312 /**
313 * A helper method which logs the port addition event and performs port
314 * addition action.
315 *
316 * @param executor device executor service
317 * @param event device event
318 */
319 private void portAddedHelper(ExecutorService executor, DeviceEvent event) {
320 executor.execute(() -> {
321 log.debug("Instance port {} is detected from {}",
322 event.port().annotations().value(PORT_NAME),
323 event.subject().id());
Jian Li24ec59f2018-05-23 19:01:25 +0900324 processPortAdded(event.port(), event.subject());
Jian Li9d676fb2018-03-02 15:25:36 +0900325 });
326 }
327
328 /**
329 * A helper method which logs the port removal event and performs port
330 * removal action.
331 *
332 * @param executor device executor service
333 * @param event device event
334 */
335 private void portRemovedHelper(ExecutorService executor, DeviceEvent event) {
336 executor.execute(() -> {
337 log.debug("Instance port {} is removed from {}",
338 event.port().annotations().value(PORT_NAME),
339 event.subject().id());
Jian Li24ec59f2018-05-23 19:01:25 +0900340 processPortRemoved(event);
Jian Li9d676fb2018-03-02 15:25:36 +0900341 });
342 }
343
Hyunsun Moon44aac662017-02-18 02:07:01 +0900344 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
345
346 @Override
Jian Li9d676fb2018-03-02 15:25:36 +0900347 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Li51b844c2018-05-31 10:59:03 +0900348
349 if (event.subject().type() == CONTROLLER) {
350 return false;
351 }
Jian Li9d676fb2018-03-02 15:25:36 +0900352 // do not allow to proceed without mastership
353 Device device = deviceService.getDevice(event.subject().intgBridge());
Jian Li7ddea782018-03-30 11:17:42 +0900354 if (device == null) {
355 return false;
356 }
Jian Li9d676fb2018-03-02 15:25:36 +0900357 return mastershipService.isLocalMaster(device.id());
358 }
359
360 @Override
Hyunsun Moon44aac662017-02-18 02:07:01 +0900361 public void event(OpenstackNodeEvent event) {
362 OpenstackNode osNode = event.subject();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900363
364 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900365 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900366 deviceEventExecutor.execute(() -> {
367 log.info("COMPLETE node {} is detected", osNode.hostname());
368 processCompleteNode(event.subject());
369 });
370 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900371 case OPENSTACK_NODE_INCOMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900372 log.warn("{} is changed to INCOMPLETE state", osNode);
373 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900374 case OPENSTACK_NODE_CREATED:
375 case OPENSTACK_NODE_UPDATED:
376 case OPENSTACK_NODE_REMOVED:
Jian Li9d676fb2018-03-02 15:25:36 +0900377 // not reacts to the events other than complete and incomplete states
378 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900379 default:
380 break;
381 }
382 }
383
384 private void processCompleteNode(OpenstackNode osNode) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900385 deviceService.getPorts(osNode.intgBridge()).stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900386 .filter(port -> port.annotations().value(PORT_NAME)
387 .startsWith(PORT_NAME_PREFIX_VM) &&
388 port.isEnabled())
389 .forEach(port -> {
390 log.debug("Instance port {} is detected from {}",
391 port.annotations().value(PORT_NAME),
392 osNode.hostname());
Jian Li24ec59f2018-05-23 19:01:25 +0900393 processPortAdded(port,
394 deviceService.getDevice(osNode.intgBridge()));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900395 });
396
Ray Milkey9dc57392018-06-08 08:52:31 -0700397 portNamePrefixMap().values().forEach(portNamePrefix -> {
Daniel Parkc4d06402018-05-28 15:57:37 +0900398 deviceService.getPorts(osNode.intgBridge()).stream()
399 .filter(port -> port.annotations().value(PORT_NAME)
400 .startsWith(portNamePrefix) &&
401 port.isEnabled())
402 .forEach(port -> {
403 log.debug("Instance port {} is detected from {}",
404 port.annotations().value(portNamePrefix),
405 osNode.hostname());
Jian Li24ec59f2018-05-23 19:01:25 +0900406 processPortAdded(port,
407 deviceService.getDevice(osNode.intgBridge()));
Daniel Parkc4d06402018-05-28 15:57:37 +0900408 });
409 });
410
Hyunsun Moon44aac662017-02-18 02:07:01 +0900411 Tools.stream(hostService.getHosts())
412 .filter(host -> deviceService.getPort(
413 host.location().deviceId(),
414 host.location().port()) == null)
415 .forEach(host -> {
416 log.info("Remove stale host {}", host.id());
417 hostProvider.hostVanished(host.id());
418 });
419 }
420 }
421}