blob: 8263f6335e9ee4664ea294f5136bc4508da15e6c [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 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070070@Component(immediate = true, service = { InstancePortService.class, InstancePortAdminService.class })
Jian Liecae4382018-06-28 17:41:12 +090071public class InstancePortManager
72 extends ListenerRegistry<InstancePortEvent, InstancePortListener>
73 implements InstancePortService, InstancePortAdminService {
74
75 protected final Logger log = getLogger(getClass());
76
77 private static final String MSG_INSTANCE_PORT = "Instance port %s %s";
78 private static final String MSG_CREATED = "created";
79 private static final String MSG_UPDATED = "updated";
80 private static final String MSG_REMOVED = "removed";
81
82 private static final String ERR_NULL_INSTANCE_PORT = "Instance port cannot be null";
83 private static final String ERR_NULL_INSTANCE_PORT_ID = "Instance port ID cannot be null";
84 private static final String ERR_NULL_MAC_ADDRESS = "MAC address cannot be null";
85 private static final String ERR_NULL_IP_ADDRESS = "IP address cannot be null";
86 private static final String ERR_NULL_NETWORK_ID = "Network ID cannot be null";
Jian Li63430202018-08-30 16:24:09 +090087 private static final String ERR_NULL_DEVICE_ID = "Device ID cannot be null";
88 private static final String ERR_NULL_PORT_NUMBER = "Port number cannot be null";
Jian Liecae4382018-06-28 17:41:12 +090089
90 private static final String ERR_IN_USE = " still in use";
91
Ray Milkeyd84f89b2018-08-17 14:54:17 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liecae4382018-06-28 17:41:12 +090093 protected CoreService coreService;
94
Ray Milkeyd84f89b2018-08-17 14:54:17 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liecae4382018-06-28 17:41:12 +090096 protected InstancePortStore instancePortStore;
97
Ray Milkeyd84f89b2018-08-17 14:54:17 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li078cd202018-07-23 18:58:20 +090099 protected LeadershipService leadershipService;
100
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li078cd202018-07-23 18:58:20 +0900102 protected ClusterService clusterService;
103
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liecae4382018-06-28 17:41:12 +0900105 protected HostService hostService;
106
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lic38e9032018-08-09 17:08:38 +0900108 protected OpenstackRouterService routerService;
109
Jian Liecae4382018-06-28 17:41:12 +0900110 private final InstancePortStoreDelegate
111 delegate = new InternalInstancePortStoreDelegate();
112 private final InternalHostListener
113 hostListener = new InternalHostListener();
114
Jian Li078cd202018-07-23 18:58:20 +0900115 private ApplicationId appId;
116 private NodeId localNodeId;
117
Jian Liecae4382018-06-28 17:41:12 +0900118 @Activate
119 protected void activate() {
Jian Li078cd202018-07-23 18:58:20 +0900120 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
121 localNodeId = clusterService.getLocalNode().id();
Jian Liecae4382018-06-28 17:41:12 +0900122 instancePortStore.setDelegate(delegate);
123 hostService.addListener(hostListener);
Jian Li078cd202018-07-23 18:58:20 +0900124 leadershipService.runForLeadership(appId.name());
125
Jian Liecae4382018-06-28 17:41:12 +0900126 log.info("Started");
127 }
128
129 @Deactivate
130 protected void deactivate() {
131 hostService.removeListener(hostListener);
132 instancePortStore.unsetDelegate(delegate);
Jian Li078cd202018-07-23 18:58:20 +0900133 leadershipService.withdraw(appId.name());
134
Jian Liecae4382018-06-28 17:41:12 +0900135 log.info("Stopped");
136 }
137
138 @Override
139 public void createInstancePort(InstancePort instancePort) {
140 checkNotNull(instancePort, ERR_NULL_INSTANCE_PORT);
141 checkArgument(!Strings.isNullOrEmpty(instancePort.portId()),
142 ERR_NULL_INSTANCE_PORT_ID);
143
144 instancePortStore.createInstancePort(instancePort);
145 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(),
146 MSG_CREATED));
147 }
148
149 @Override
150 public void updateInstancePort(InstancePort instancePort) {
151 checkNotNull(instancePort, ERR_NULL_INSTANCE_PORT);
152 checkArgument(!Strings.isNullOrEmpty(instancePort.portId()),
153 ERR_NULL_INSTANCE_PORT_ID);
154
Jian Lida03ce92018-07-24 21:41:53 +0900155 // in case OpenStack removes the port prior to OVS, we will not update
156 // the instance port as it does not exist in the store
157 if (instancePortStore.instancePort(instancePort.portId()) == null) {
158 log.warn("Unable to update instance port {}, as it does not exist", instancePort.portId());
159 return;
160 }
161
Jian Liecae4382018-06-28 17:41:12 +0900162 instancePortStore.updateInstancePort(instancePort);
163 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(), MSG_UPDATED));
164 }
165
166 @Override
167 public void removeInstancePort(String portId) {
168 checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_INSTANCE_PORT_ID);
169
170 synchronized (this) {
171 if (isInstancePortInUse(portId)) {
172 final String error =
173 String.format(MSG_INSTANCE_PORT, portId, ERR_IN_USE);
174 throw new IllegalStateException(error);
175 }
176 InstancePort instancePort = instancePortStore.removeInstancePort(portId);
177 if (instancePort != null) {
178 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(), MSG_REMOVED));
179 }
180 }
181 }
182
183 @Override
184 public void clear() {
185 instancePortStore.clear();
186 }
187
188 @Override
189 public InstancePort instancePort(MacAddress macAddress) {
190 checkNotNull(macAddress, ERR_NULL_MAC_ADDRESS);
191
192 return instancePortStore.instancePorts().stream()
193 .filter(port -> port.macAddress().equals(macAddress))
194 .findFirst().orElse(null);
195 }
196
197 @Override
198 public InstancePort instancePort(IpAddress ipAddress, String osNetId) {
199 checkNotNull(ipAddress, ERR_NULL_IP_ADDRESS);
200 checkNotNull(osNetId, ERR_NULL_NETWORK_ID);
201
202 return instancePortStore.instancePorts().stream()
203 .filter(port -> port.networkId().equals(osNetId))
204 .filter(port -> port.ipAddress().equals(ipAddress))
205 .findFirst().orElse(null);
206 }
207
208 @Override
209 public InstancePort instancePort(String portId) {
210 checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_INSTANCE_PORT_ID);
211
212 return instancePortStore.instancePort(portId);
213 }
214
215 @Override
Jian Li63430202018-08-30 16:24:09 +0900216 public InstancePort instancePort(DeviceId deviceId, PortNumber portNumber) {
217 checkNotNull(deviceId, ERR_NULL_DEVICE_ID);
218 checkNotNull(portNumber, ERR_NULL_PORT_NUMBER);
219
220 return instancePortStore.instancePorts().stream()
221 .filter(port -> port.deviceId().equals(deviceId))
222 .filter(port -> port.portNumber().equals(portNumber))
223 .findFirst().orElse(null);
224 }
225
226 @Override
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900227 public Set<InstancePort> instancePort(DeviceId deviceId) {
228 Set<InstancePort> ports = instancePortStore.instancePorts().stream()
229 .filter(port -> port.deviceId().equals(deviceId))
230 .collect(Collectors.toSet());
231
232 return ImmutableSet.copyOf(ports);
233 }
234
235 @Override
Jian Liecae4382018-06-28 17:41:12 +0900236 public Set<InstancePort> instancePorts() {
237 Set<InstancePort> ports = instancePortStore.instancePorts();
238
239 return ImmutableSet.copyOf(ports);
240 }
241
242 @Override
243 public Set<InstancePort> instancePorts(String osNetId) {
244 checkNotNull(osNetId, ERR_NULL_NETWORK_ID);
245
246 Set<InstancePort> ports = instancePortStore.instancePorts().stream()
247 .filter(port -> port.networkId().equals(osNetId))
248 .collect(Collectors.toSet());
249
250 return ImmutableSet.copyOf(ports);
251 }
252
Jian Lic38e9032018-08-09 17:08:38 +0900253 @Override
254 public IpAddress floatingIp(String osPortId) {
255 checkNotNull(osPortId, ERR_NULL_INSTANCE_PORT_ID);
256
257 return routerService.floatingIps().stream()
258 .filter(fip -> osPortId.equals(fip.getPortId()))
259 .filter(fip -> fip.getFloatingIpAddress() != null)
260 .map(fip -> IpAddress.valueOf(fip.getFloatingIpAddress()))
261 .findFirst().orElse(null);
262 }
263
Jian Liecae4382018-06-28 17:41:12 +0900264 private boolean isInstancePortInUse(String portId) {
265 // TODO add checking logic
266 return false;
267 }
268
269 private class InternalInstancePortStoreDelegate implements InstancePortStoreDelegate {
270
271 @Override
272 public void notify(InstancePortEvent event) {
273 if (event != null) {
274 log.trace("send instance port event {}", event);
275 process(event);
276 }
277 }
278 }
279
280 /**
281 * An internal listener that listens host event generated by HostLocationTracker
282 * in DistributedHostStore. The role of this listener is to convert host event
283 * to instance port event and post to the subscribers that have interested on
284 * this type of event.
285 */
286 private class InternalHostListener implements HostListener {
287
288 @Override
289 public boolean isRelevant(HostEvent event) {
290 Host host = event.subject();
291 if (!isValidHost(host)) {
292 log.debug("Invalid host detected, ignore it {}", host);
293 return false;
294 }
Jian Li078cd202018-07-23 18:58:20 +0900295
296 // do not allow to proceed without leadership
297 NodeId leader = leadershipService.getLeader(appId.name());
298 return Objects.equals(localNodeId, leader);
Jian Liecae4382018-06-28 17:41:12 +0900299 }
300
301 @Override
302 public void event(HostEvent event) {
303 InstancePort instPort = DefaultInstancePort.from(event.subject(), ACTIVE);
304
305 switch (event.type()) {
306 case HOST_UPDATED:
307 updateInstancePort(instPort);
308 break;
309 case HOST_ADDED:
Jian Liec5c32b2018-07-13 14:28:58 +0900310 InstancePort existingPort = instancePort(instPort.portId());
311 if (existingPort == null) {
312 // first time to add instance
313 createInstancePort(instPort);
314 } else {
Jian Li46b74002018-07-15 18:39:08 +0900315 if (existingPort.state() == INACTIVE) {
Jian Liee8214a2018-07-21 20:07:28 +0900316
317 if (instPort.deviceId().equals(existingPort.deviceId())) {
318
319 // VM RESTART case
320 // if the ID of switch where VM is attached to is
321 // identical, we can assume that the VM was
322 // restarted in the same location;
323 // note that the switch port number where VM is
324 // attached can be varied per each restart
325 updateInstancePort(instPort);
326 } else {
327
328 // VM COLD MIGRATION case
329 // if the ID of switch where VM is attached to is
330 // varied, we can assume that the VM was migrated
331 // to a new location
332 updateInstancePort(instPort.updateState(MIGRATING));
333 InstancePort updated = instPort.updateState(MIGRATED);
334 updateInstancePort(updated.updatePrevLocation(
335 existingPort.deviceId(), existingPort.portNumber()));
336 }
Jian Liec5c32b2018-07-13 14:28:58 +0900337 }
338 }
Jian Liecae4382018-06-28 17:41:12 +0900339 break;
340 case HOST_REMOVED:
Jian Li0488c732018-09-14 20:53:07 +0900341
342 // in case the instance port cannot be found in the store,
343 // this indicates that the instance port was removed due to
344 // the removal of openstack port; in some cases, openstack
345 // port removal message arrives before ovs port removal message
346 if (instancePortStore.instancePort(instPort.portId()) == null) {
347 log.debug("instance port was removed before ovs port removal");
348 break;
349 }
350
Jian Liec5c32b2018-07-13 14:28:58 +0900351 // we will remove instance port from persistent store,
Jian Liee8214a2018-07-21 20:07:28 +0900352 // only if we receive port removal signal from neutron.
Jian Liec5c32b2018-07-13 14:28:58 +0900353 // by default, we update the instance port state to INACTIVE
354 // to indicate the instance is terminated
355 updateInstancePort(instPort.updateState(INACTIVE));
Jian Liecae4382018-06-28 17:41:12 +0900356 break;
357 case HOST_MOVED:
Jian Liec5c32b2018-07-13 14:28:58 +0900358 Host oldHost = event.prevSubject();
359 Host currHost = event.subject();
360
361 // in the middle of VM migration
362 if (oldHost.locations().size() < currHost.locations().size()) {
363 updateInstancePort(instPort.updateState(MIGRATING));
364 }
365
366 // finish of VM migration
367 if (oldHost.locations().size() > currHost.locations().size()) {
368 Set<HostLocation> diff =
369 Sets.difference(oldHost.locations(), currHost.locations());
370 HostLocation location = diff.stream().findFirst().orElse(null);
371
372 if (location != null) {
373 InstancePort updated = instPort.updateState(MIGRATED);
Jian Li46b74002018-07-15 18:39:08 +0900374 updateInstancePort(updated.updatePrevLocation(
Jian Liec5c32b2018-07-13 14:28:58 +0900375 location.deviceId(), location.port()));
376 }
377 }
Jian Liecae4382018-06-28 17:41:12 +0900378 break;
379 default:
380 break;
381 }
382 }
383
384 private boolean isValidHost(Host host) {
385 return !host.ipAddresses().isEmpty() &&
386 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
387 host.annotations().value(ANNOTATION_PORT_ID) != null;
388 }
389 }
390}