blob: e7492bf28186d5b44440485e4a4004b0800464a5 [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;
49import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090050import org.onosproject.openstacknode.api.OpenstackNode;
51import org.onosproject.openstacknode.api.OpenstackNodeEvent;
52import org.onosproject.openstacknode.api.OpenstackNodeListener;
53import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090054import org.openstack4j.model.network.Network;
daniel park796c2eb2018-03-22 17:01:51 +090055import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090056import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
58
59import java.util.Set;
60import java.util.concurrent.ExecutorService;
61import java.util.concurrent.Executors;
62import java.util.stream.Collectors;
63
64import static org.onlab.util.Tools.groupedThreads;
65import static org.onosproject.net.AnnotationKeys.PORT_NAME;
daniel park796c2eb2018-03-22 17:01:51 +090066import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Daniel Parkc4d06402018-05-28 15:57:37 +090067import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_MAP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090068import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_CREATE_TIME;
69import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
70import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Jian Li51b844c2018-05-31 10:59:03 +090071import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Hyunsun Moon44aac662017-02-18 02:07:01 +090072
73@Service
74@Component(immediate = true)
75public final class OpenstackSwitchingHostProvider extends AbstractProvider implements HostProvider {
76
77 private final Logger log = LoggerFactory.getLogger(getClass());
78
79 private static final String PORT_NAME_PREFIX_VM = "tap";
Daniel Parkc4d06402018-05-28 15:57:37 +090080 private static final String PORT_NAME_PREFIX_CAVIUM = "enp";
Hyunsun Moon44aac662017-02-18 02:07:01 +090081 private static final String ERR_ADD_HOST = "Failed to add host: ";
daniel park5fe30792017-08-22 15:42:54 +090082 private static final String ANNOTATION_SEGMENT_ID = "segId";
Jian Li9d676fb2018-03-02 15:25:36 +090083 private static final String SONA_HOST_SCHEME = "sona";
Hyunsun Moon44aac662017-02-18 02:07:01 +090084
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090086 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090087
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090089 protected DeviceService deviceService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090090
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090092 protected HostProviderRegistry hostProviderRegistry;
Hyunsun Moon44aac662017-02-18 02:07:01 +090093
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090095 protected HostService hostService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090096
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +090098 protected MastershipService mastershipService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090099
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +0900101 protected OpenstackNetworkService osNetworkService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li9d676fb2018-03-02 15:25:36 +0900104 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900105
106 private final ExecutorService deviceEventExecutor =
107 Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event"));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900108 private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
109 private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
110
111 private HostProviderService hostProvider;
112
113 /**
114 * Creates OpenStack switching host provider.
115 */
116 public OpenstackSwitchingHostProvider() {
Jian Li9d676fb2018-03-02 15:25:36 +0900117 super(new ProviderId(SONA_HOST_SCHEME, OPENSTACK_NETWORKING_APP_ID));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900118 }
119
120 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800121 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900122 coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
123 deviceService.addListener(internalDeviceListener);
124 osNodeService.addListener(internalNodeListener);
125 hostProvider = hostProviderRegistry.register(this);
126
127 log.info("Started");
128 }
129
130 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800131 void deactivate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900132 hostProviderRegistry.unregister(this);
133 osNodeService.removeListener(internalNodeListener);
134 deviceService.removeListener(internalDeviceListener);
135
136 deviceEventExecutor.shutdown();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900137
138 log.info("Stopped");
139 }
140
141 @Override
142 public void triggerProbe(Host host) {
143 // no probe is required
144 }
145
Jian Li9d676fb2018-03-02 15:25:36 +0900146 /**
147 * Processes port addition event.
148 * Once a port addition event is detected, it tries to create a host instance
149 * with openstack augmented host information such as networkId, portId,
150 * createTime, segmentId and notifies to host provider.
151 *
152 * @param port port object used in ONOS
153 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900154 private void processPortAdded(Port port) {
155 // TODO check the node state is COMPLETE
156 org.openstack4j.model.network.Port osPort = osNetworkService.port(port);
157 if (osPort == null) {
158 log.warn(ERR_ADD_HOST + "OpenStack port for {} not found", port);
159 return;
160 }
161
162 Network osNet = osNetworkService.network(osPort.getNetworkId());
163 if (osNet == null) {
164 log.warn(ERR_ADD_HOST + "OpenStack network {} not found",
165 osPort.getNetworkId());
166 return;
167 }
168
169 if (osPort.getFixedIps().isEmpty()) {
170 log.warn(ERR_ADD_HOST + "no fixed IP for port {}", osPort.getId());
171 return;
172 }
173
174 MacAddress macAddr = MacAddress.valueOf(osPort.getMacAddress());
175 Set<IpAddress> fixedIps = osPort.getFixedIps().stream()
176 .map(ip -> IpAddress.valueOf(ip.getIpAddress()))
177 .collect(Collectors.toSet());
178 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
179
180 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
181 .set(ANNOTATION_NETWORK_ID, osPort.getNetworkId())
182 .set(ANNOTATION_PORT_ID, osPort.getId())
daniel park796c2eb2018-03-22 17:01:51 +0900183 .set(ANNOTATION_CREATE_TIME, String.valueOf(System.currentTimeMillis()));
184
185 if (osNet.getNetworkType() != NetworkType.FLAT) {
186 annotations.set(ANNOTATION_SEGMENT_ID, osNet.getProviderSegID());
187
188 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900189
190 HostDescription hostDesc = new DefaultHostDescription(
191 macAddr,
192 VlanId.NONE,
193 new HostLocation(connectPoint, System.currentTimeMillis()),
194 fixedIps,
195 annotations.build());
196
197 HostId hostId = HostId.hostId(macAddr);
198 hostProvider.hostDetected(hostId, hostDesc, false);
199 }
200
Jian Li9d676fb2018-03-02 15:25:36 +0900201 /**
202 * Processes port removal event.
203 * Once a port removal event is detected, it tries to look for a host
204 * instance through host provider by giving connect point information,
205 * and vanishes it.
206 *
207 * @param port port object used in ONOS
208 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900209 private void processPortRemoved(Port port) {
210 ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
Jian Li9d676fb2018-03-02 15:25:36 +0900211 hostService.getConnectedHosts(connectPoint)
212 .forEach(host -> hostProvider.hostVanished(host.id()));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900213 }
214
Jian Li9d676fb2018-03-02 15:25:36 +0900215 /**
216 * An internal device listener which listens the port events generated from
217 * OVS integration bridge.
218 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900219 private class InternalDeviceListener implements DeviceListener {
220
221 @Override
222 public boolean isRelevant(DeviceEvent event) {
223 Device device = event.subject();
224 if (!mastershipService.isLocalMaster(device.id())) {
225 // do not allow to proceed without mastership
226 return false;
227 }
228 Port port = event.port();
229 if (port == null) {
230 return false;
231 }
232 String portName = port.annotations().value(PORT_NAME);
Jian Li9d676fb2018-03-02 15:25:36 +0900233
234 return !Strings.isNullOrEmpty(portName) &&
Daniel Parkc4d06402018-05-28 15:57:37 +0900235 (portName.startsWith(PORT_NAME_PREFIX_VM) || isDirectPort(portName));
236 }
237
238 private boolean isDirectPort(String portName) {
239 return PORT_NAME_PREFIX_MAP.values().stream().filter(p -> portName.startsWith(p)).findAny().isPresent();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900240 }
241
242 @Override
243 public void event(DeviceEvent event) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900244 log.info("Device event occurred with type {}", event.type());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900245 switch (event.type()) {
246 case PORT_UPDATED:
247 if (!event.port().isEnabled()) {
Jian Li9d676fb2018-03-02 15:25:36 +0900248 portRemovedHelper(deviceEventExecutor, event);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900249 } else if (event.port().isEnabled()) {
Jian Li9d676fb2018-03-02 15:25:36 +0900250 portAddedHelper(deviceEventExecutor, event);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900251 }
252 break;
253 case PORT_ADDED:
Jian Li9d676fb2018-03-02 15:25:36 +0900254 portAddedHelper(deviceEventExecutor, event);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900255 break;
Hyunsun Moonb7a9cd22017-02-24 11:12:53 +0900256 case PORT_REMOVED:
Jian Li9d676fb2018-03-02 15:25:36 +0900257 portRemovedHelper(deviceEventExecutor, event);
Ray Milkeyd6a67c32018-02-02 10:30:35 -0800258 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900259 default:
260 break;
261 }
262 }
263 }
264
Jian Li9d676fb2018-03-02 15:25:36 +0900265 /**
266 * A helper method which logs the port addition event and performs port
267 * addition action.
268 *
269 * @param executor device executor service
270 * @param event device event
271 */
272 private void portAddedHelper(ExecutorService executor, DeviceEvent event) {
273 executor.execute(() -> {
274 log.debug("Instance port {} is detected from {}",
275 event.port().annotations().value(PORT_NAME),
276 event.subject().id());
277 processPortAdded(event.port());
278 });
279 }
280
281 /**
282 * A helper method which logs the port removal event and performs port
283 * removal action.
284 *
285 * @param executor device executor service
286 * @param event device event
287 */
288 private void portRemovedHelper(ExecutorService executor, DeviceEvent event) {
289 executor.execute(() -> {
290 log.debug("Instance port {} is removed from {}",
291 event.port().annotations().value(PORT_NAME),
292 event.subject().id());
293 processPortRemoved(event.port());
294 });
295 }
296
Hyunsun Moon44aac662017-02-18 02:07:01 +0900297 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
298
299 @Override
Jian Li9d676fb2018-03-02 15:25:36 +0900300 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Li51b844c2018-05-31 10:59:03 +0900301
302 if (event.subject().type() == CONTROLLER) {
303 return false;
304 }
Jian Li9d676fb2018-03-02 15:25:36 +0900305 // do not allow to proceed without mastership
306 Device device = deviceService.getDevice(event.subject().intgBridge());
Jian Li7ddea782018-03-30 11:17:42 +0900307 if (device == null) {
308 return false;
309 }
Jian Li9d676fb2018-03-02 15:25:36 +0900310 return mastershipService.isLocalMaster(device.id());
311 }
312
313 @Override
Hyunsun Moon44aac662017-02-18 02:07:01 +0900314 public void event(OpenstackNodeEvent event) {
315 OpenstackNode osNode = event.subject();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900316
317 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900318 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900319 deviceEventExecutor.execute(() -> {
320 log.info("COMPLETE node {} is detected", osNode.hostname());
321 processCompleteNode(event.subject());
322 });
323 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900324 case OPENSTACK_NODE_INCOMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900325 log.warn("{} is changed to INCOMPLETE state", osNode);
326 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900327 case OPENSTACK_NODE_CREATED:
328 case OPENSTACK_NODE_UPDATED:
329 case OPENSTACK_NODE_REMOVED:
Jian Li9d676fb2018-03-02 15:25:36 +0900330 // not reacts to the events other than complete and incomplete states
331 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900332 default:
333 break;
334 }
335 }
336
337 private void processCompleteNode(OpenstackNode osNode) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900338 deviceService.getPorts(osNode.intgBridge()).stream()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900339 .filter(port -> port.annotations().value(PORT_NAME)
340 .startsWith(PORT_NAME_PREFIX_VM) &&
341 port.isEnabled())
342 .forEach(port -> {
343 log.debug("Instance port {} is detected from {}",
344 port.annotations().value(PORT_NAME),
345 osNode.hostname());
346 processPortAdded(port);
347 });
348
Daniel Parkc4d06402018-05-28 15:57:37 +0900349 PORT_NAME_PREFIX_MAP.values().forEach(portNamePrefix -> {
350 deviceService.getPorts(osNode.intgBridge()).stream()
351 .filter(port -> port.annotations().value(PORT_NAME)
352 .startsWith(portNamePrefix) &&
353 port.isEnabled())
354 .forEach(port -> {
355 log.debug("Instance port {} is detected from {}",
356 port.annotations().value(portNamePrefix),
357 osNode.hostname());
358 processPortAdded(port);
359 });
360 });
361
Hyunsun Moon44aac662017-02-18 02:07:01 +0900362 Tools.stream(hostService.getHosts())
363 .filter(host -> deviceService.getPort(
364 host.location().deviceId(),
365 host.location().port()) == null)
366 .forEach(host -> {
367 log.info("Remove stale host {}", host.id());
368 hostProvider.hostVanished(host.id());
369 });
370 }
371 }
372}