blob: 82413d4391762b4b6e8a854b880936db64ddd3a7 [file] [log] [blame]
Jian Liecae4382018-06-28 17:41:12 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
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 com.google.common.collect.ImmutableSet;
Jian Liec5c32b2018-07-13 14:28:58 +090020import com.google.common.collect.Sets;
Jian Liecae4382018-06-28 17:41:12 +090021import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
Jian Li078cd202018-07-23 18:58:20 +090023import org.onosproject.cluster.ClusterService;
24import org.onosproject.cluster.LeadershipService;
25import org.onosproject.cluster.NodeId;
26import org.onosproject.core.ApplicationId;
Jian Liecae4382018-06-28 17:41:12 +090027import org.onosproject.core.CoreService;
28import org.onosproject.event.ListenerRegistry;
Jian Li63430202018-08-30 16:24:09 +090029import org.onosproject.net.DeviceId;
Jian Liecae4382018-06-28 17:41:12 +090030import org.onosproject.net.Host;
Jian Liec5c32b2018-07-13 14:28:58 +090031import org.onosproject.net.HostLocation;
Jian Li63430202018-08-30 16:24:09 +090032import org.onosproject.net.PortNumber;
Jian Liecae4382018-06-28 17:41:12 +090033import org.onosproject.net.host.HostEvent;
34import org.onosproject.net.host.HostListener;
35import org.onosproject.net.host.HostService;
36import org.onosproject.openstacknetworking.api.Constants;
37import org.onosproject.openstacknetworking.api.InstancePort;
38import org.onosproject.openstacknetworking.api.InstancePortAdminService;
39import org.onosproject.openstacknetworking.api.InstancePortEvent;
40import org.onosproject.openstacknetworking.api.InstancePortListener;
41import org.onosproject.openstacknetworking.api.InstancePortService;
42import org.onosproject.openstacknetworking.api.InstancePortStore;
43import org.onosproject.openstacknetworking.api.InstancePortStoreDelegate;
Jian Lic38e9032018-08-09 17:08:38 +090044import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070045import org.osgi.service.component.annotations.Activate;
46import org.osgi.service.component.annotations.Component;
47import org.osgi.service.component.annotations.Deactivate;
48import org.osgi.service.component.annotations.Reference;
49import org.osgi.service.component.annotations.ReferenceCardinality;
Jian Liecae4382018-06-28 17:41:12 +090050import org.slf4j.Logger;
51
Jian Li078cd202018-07-23 18:58:20 +090052import java.util.Objects;
Jian Liecae4382018-06-28 17:41:12 +090053import java.util.Set;
54import java.util.stream.Collectors;
55
56import static com.google.common.base.Preconditions.checkArgument;
57import static com.google.common.base.Preconditions.checkNotNull;
Jian Liec5c32b2018-07-13 14:28:58 +090058import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_NETWORK_ID;
59import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_PORT_ID;
Jian Liecae4382018-06-28 17:41:12 +090060import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Jian Liec5c32b2018-07-13 14:28:58 +090061import static org.onosproject.openstacknetworking.api.InstancePort.State.INACTIVE;
62import static org.onosproject.openstacknetworking.api.InstancePort.State.MIGRATED;
63import static org.onosproject.openstacknetworking.api.InstancePort.State.MIGRATING;
Jian Liecae4382018-06-28 17:41:12 +090064import static org.slf4j.LoggerFactory.getLogger;
65
66/**
67 * Provides implementation of administering and interfacing instance ports.
68 * It also provides instance port events for the hosts mapped to OpenStack VM interface.
69 */
Jian Li5ecfd1a2018-12-10 11:41:03 +090070@Component(
71 immediate = true,
72 service = { InstancePortService.class, InstancePortAdminService.class }
73)
Jian Liecae4382018-06-28 17:41:12 +090074public class InstancePortManager
75 extends ListenerRegistry<InstancePortEvent, InstancePortListener>
76 implements InstancePortService, InstancePortAdminService {
77
78 protected final Logger log = getLogger(getClass());
79
80 private static final String MSG_INSTANCE_PORT = "Instance port %s %s";
81 private static final String MSG_CREATED = "created";
82 private static final String MSG_UPDATED = "updated";
83 private static final String MSG_REMOVED = "removed";
84
85 private static final String ERR_NULL_INSTANCE_PORT = "Instance port cannot be null";
86 private static final String ERR_NULL_INSTANCE_PORT_ID = "Instance port ID cannot be null";
87 private static final String ERR_NULL_MAC_ADDRESS = "MAC address cannot be null";
88 private static final String ERR_NULL_IP_ADDRESS = "IP address cannot be null";
89 private static final String ERR_NULL_NETWORK_ID = "Network ID cannot be null";
Jian Li63430202018-08-30 16:24:09 +090090 private static final String ERR_NULL_DEVICE_ID = "Device ID cannot be null";
91 private static final String ERR_NULL_PORT_NUMBER = "Port number cannot be null";
Jian Liecae4382018-06-28 17:41:12 +090092
93 private static final String ERR_IN_USE = " still in use";
94
Ray Milkeyd84f89b2018-08-17 14:54:17 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liecae4382018-06-28 17:41:12 +090096 protected CoreService coreService;
97
Ray Milkeyd84f89b2018-08-17 14:54:17 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liecae4382018-06-28 17:41:12 +090099 protected InstancePortStore instancePortStore;
100
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li078cd202018-07-23 18:58:20 +0900102 protected LeadershipService leadershipService;
103
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li078cd202018-07-23 18:58:20 +0900105 protected ClusterService clusterService;
106
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liecae4382018-06-28 17:41:12 +0900108 protected HostService hostService;
109
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lic38e9032018-08-09 17:08:38 +0900111 protected OpenstackRouterService routerService;
112
Jian Liecae4382018-06-28 17:41:12 +0900113 private final InstancePortStoreDelegate
114 delegate = new InternalInstancePortStoreDelegate();
115 private final InternalHostListener
116 hostListener = new InternalHostListener();
117
Jian Li078cd202018-07-23 18:58:20 +0900118 private ApplicationId appId;
119 private NodeId localNodeId;
120
Jian Liecae4382018-06-28 17:41:12 +0900121 @Activate
122 protected void activate() {
Jian Li078cd202018-07-23 18:58:20 +0900123 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
124 localNodeId = clusterService.getLocalNode().id();
Jian Liecae4382018-06-28 17:41:12 +0900125 instancePortStore.setDelegate(delegate);
126 hostService.addListener(hostListener);
Jian Li078cd202018-07-23 18:58:20 +0900127 leadershipService.runForLeadership(appId.name());
128
Jian Liecae4382018-06-28 17:41:12 +0900129 log.info("Started");
130 }
131
132 @Deactivate
133 protected void deactivate() {
134 hostService.removeListener(hostListener);
135 instancePortStore.unsetDelegate(delegate);
Jian Li078cd202018-07-23 18:58:20 +0900136 leadershipService.withdraw(appId.name());
137
Jian Liecae4382018-06-28 17:41:12 +0900138 log.info("Stopped");
139 }
140
141 @Override
142 public void createInstancePort(InstancePort instancePort) {
143 checkNotNull(instancePort, ERR_NULL_INSTANCE_PORT);
144 checkArgument(!Strings.isNullOrEmpty(instancePort.portId()),
145 ERR_NULL_INSTANCE_PORT_ID);
146
147 instancePortStore.createInstancePort(instancePort);
148 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(),
149 MSG_CREATED));
150 }
151
152 @Override
153 public void updateInstancePort(InstancePort instancePort) {
154 checkNotNull(instancePort, ERR_NULL_INSTANCE_PORT);
155 checkArgument(!Strings.isNullOrEmpty(instancePort.portId()),
156 ERR_NULL_INSTANCE_PORT_ID);
157
Jian Lida03ce92018-07-24 21:41:53 +0900158 // in case OpenStack removes the port prior to OVS, we will not update
159 // the instance port as it does not exist in the store
160 if (instancePortStore.instancePort(instancePort.portId()) == null) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900161 log.warn("Unable to update instance port {}, as it does not exist",
162 instancePort.portId());
Jian Lida03ce92018-07-24 21:41:53 +0900163 return;
164 }
165
Jian Liecae4382018-06-28 17:41:12 +0900166 instancePortStore.updateInstancePort(instancePort);
167 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(), MSG_UPDATED));
168 }
169
170 @Override
171 public void removeInstancePort(String portId) {
172 checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_INSTANCE_PORT_ID);
173
174 synchronized (this) {
175 if (isInstancePortInUse(portId)) {
176 final String error =
177 String.format(MSG_INSTANCE_PORT, portId, ERR_IN_USE);
178 throw new IllegalStateException(error);
179 }
180 InstancePort instancePort = instancePortStore.removeInstancePort(portId);
181 if (instancePort != null) {
182 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(), MSG_REMOVED));
183 }
184 }
185 }
186
187 @Override
188 public void clear() {
189 instancePortStore.clear();
190 }
191
192 @Override
193 public InstancePort instancePort(MacAddress macAddress) {
194 checkNotNull(macAddress, ERR_NULL_MAC_ADDRESS);
195
196 return instancePortStore.instancePorts().stream()
197 .filter(port -> port.macAddress().equals(macAddress))
198 .findFirst().orElse(null);
199 }
200
201 @Override
202 public InstancePort instancePort(IpAddress ipAddress, String osNetId) {
203 checkNotNull(ipAddress, ERR_NULL_IP_ADDRESS);
204 checkNotNull(osNetId, ERR_NULL_NETWORK_ID);
205
206 return instancePortStore.instancePorts().stream()
207 .filter(port -> port.networkId().equals(osNetId))
208 .filter(port -> port.ipAddress().equals(ipAddress))
209 .findFirst().orElse(null);
210 }
211
212 @Override
213 public InstancePort instancePort(String portId) {
214 checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_INSTANCE_PORT_ID);
215
216 return instancePortStore.instancePort(portId);
217 }
218
219 @Override
Jian Li63430202018-08-30 16:24:09 +0900220 public InstancePort instancePort(DeviceId deviceId, PortNumber portNumber) {
221 checkNotNull(deviceId, ERR_NULL_DEVICE_ID);
222 checkNotNull(portNumber, ERR_NULL_PORT_NUMBER);
223
224 return instancePortStore.instancePorts().stream()
225 .filter(port -> port.deviceId().equals(deviceId))
226 .filter(port -> port.portNumber().equals(portNumber))
227 .findFirst().orElse(null);
228 }
229
230 @Override
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900231 public Set<InstancePort> instancePort(DeviceId deviceId) {
232 Set<InstancePort> ports = instancePortStore.instancePorts().stream()
233 .filter(port -> port.deviceId().equals(deviceId))
234 .collect(Collectors.toSet());
235
236 return ImmutableSet.copyOf(ports);
237 }
238
239 @Override
Jian Liecae4382018-06-28 17:41:12 +0900240 public Set<InstancePort> instancePorts() {
241 Set<InstancePort> ports = instancePortStore.instancePorts();
242
243 return ImmutableSet.copyOf(ports);
244 }
245
246 @Override
247 public Set<InstancePort> instancePorts(String osNetId) {
248 checkNotNull(osNetId, ERR_NULL_NETWORK_ID);
249
250 Set<InstancePort> ports = instancePortStore.instancePorts().stream()
251 .filter(port -> port.networkId().equals(osNetId))
252 .collect(Collectors.toSet());
253
254 return ImmutableSet.copyOf(ports);
255 }
256
Jian Lic38e9032018-08-09 17:08:38 +0900257 @Override
258 public IpAddress floatingIp(String osPortId) {
259 checkNotNull(osPortId, ERR_NULL_INSTANCE_PORT_ID);
260
261 return routerService.floatingIps().stream()
262 .filter(fip -> osPortId.equals(fip.getPortId()))
263 .filter(fip -> fip.getFloatingIpAddress() != null)
264 .map(fip -> IpAddress.valueOf(fip.getFloatingIpAddress()))
265 .findFirst().orElse(null);
266 }
267
Jian Liecae4382018-06-28 17:41:12 +0900268 private boolean isInstancePortInUse(String portId) {
269 // TODO add checking logic
270 return false;
271 }
272
273 private class InternalInstancePortStoreDelegate implements InstancePortStoreDelegate {
274
275 @Override
276 public void notify(InstancePortEvent event) {
277 if (event != null) {
278 log.trace("send instance port event {}", event);
279 process(event);
280 }
281 }
282 }
283
284 /**
285 * An internal listener that listens host event generated by HostLocationTracker
286 * in DistributedHostStore. The role of this listener is to convert host event
287 * to instance port event and post to the subscribers that have interested on
288 * this type of event.
289 */
290 private class InternalHostListener implements HostListener {
291
292 @Override
293 public boolean isRelevant(HostEvent event) {
294 Host host = event.subject();
295 if (!isValidHost(host)) {
296 log.debug("Invalid host detected, ignore it {}", host);
297 return false;
298 }
Jian Li078cd202018-07-23 18:58:20 +0900299
300 // do not allow to proceed without leadership
301 NodeId leader = leadershipService.getLeader(appId.name());
302 return Objects.equals(localNodeId, leader);
Jian Liecae4382018-06-28 17:41:12 +0900303 }
304
305 @Override
306 public void event(HostEvent event) {
307 InstancePort instPort = DefaultInstancePort.from(event.subject(), ACTIVE);
308
309 switch (event.type()) {
310 case HOST_UPDATED:
311 updateInstancePort(instPort);
312 break;
313 case HOST_ADDED:
Jian Li6a47fd02018-11-27 21:51:03 +0900314 processHostAddition(instPort);
Jian Liecae4382018-06-28 17:41:12 +0900315 break;
316 case HOST_REMOVED:
Jian Li6a47fd02018-11-27 21:51:03 +0900317 processHostRemoval(instPort);
Jian Liecae4382018-06-28 17:41:12 +0900318 break;
319 case HOST_MOVED:
Jian Li6a47fd02018-11-27 21:51:03 +0900320 processHostMove(event, instPort);
Jian Liecae4382018-06-28 17:41:12 +0900321 break;
322 default:
323 break;
324 }
325 }
326
Jian Li6a47fd02018-11-27 21:51:03 +0900327 private void processHostAddition(InstancePort instPort) {
328 InstancePort existingPort = instancePort(instPort.portId());
329 if (existingPort == null) {
330 // first time to add instance
331 createInstancePort(instPort);
332 } else {
333 if (existingPort.state() == INACTIVE) {
334
335 if (instPort.deviceId().equals(existingPort.deviceId())) {
336 // VM RESTART case
337 // if the ID of switch where VM is attached to is
338 // identical, we can assume that the VM was
339 // restarted in the same location;
340 // note that the switch port number where VM is
341 // attached can be varied per each restart
342 updateInstancePort(instPort);
343 } else {
344 // VM COLD MIGRATION case
345 // if the ID of switch where VM is attached to is
346 // varied, we can assume that the VM was migrated
347 // to a new location
348 updateInstancePort(instPort.updateState(MIGRATING));
349 InstancePort updated = instPort.updateState(MIGRATED);
350 updateInstancePort(updated.updatePrevLocation(
351 existingPort.deviceId(), existingPort.portNumber()));
352 }
353 }
354 }
355 }
356
357 private void processHostRemoval(InstancePort instPort) {
358 /* in case the instance port cannot be found in the store,
359 this indicates that the instance port was removed due to
360 the removal of openstack port; in some cases, openstack
361 port removal message arrives before ovs port removal message */
362 if (instancePortStore.instancePort(instPort.portId()) == null) {
363 log.debug("instance port was removed before ovs port removal");
364 return;
365 }
366
367 /* we will remove instance port from persistent store,
368 only if we receive port removal signal from neutron.
369 by default, we update the instance port state to INACTIVE
370 to indicate the instance is terminated */
371 updateInstancePort(instPort.updateState(INACTIVE));
372 }
373
374 private void processHostMove(HostEvent event, InstancePort instPort) {
375 Host oldHost = event.prevSubject();
376 Host currHost = event.subject();
377
378 // in the middle of VM migration
379 if (oldHost.locations().size() < currHost.locations().size()) {
380 updateInstancePort(instPort.updateState(MIGRATING));
381 }
382
383 // finish of VM migration
384 if (oldHost.locations().size() > currHost.locations().size()) {
385 Set<HostLocation> diff =
386 Sets.difference(oldHost.locations(), currHost.locations());
387 HostLocation location = diff.stream().findFirst().orElse(null);
388
389 if (location != null) {
390 InstancePort updated = instPort.updateState(MIGRATED);
391 updateInstancePort(updated.updatePrevLocation(
392 location.deviceId(), location.port()));
393 }
394 }
395 }
396
Jian Liecae4382018-06-28 17:41:12 +0900397 private boolean isValidHost(Host host) {
398 return !host.ipAddresses().isEmpty() &&
399 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
400 host.annotations().value(ANNOTATION_PORT_ID) != null;
401 }
402 }
403}