blob: 3f204fbf4aedd18290ba94db25c789ac2a40dedc [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.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;
Jian Li078cd202018-07-23 18:58:20 +090029import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.core.ApplicationId;
Jian Liecae4382018-06-28 17:41:12 +090033import org.onosproject.core.CoreService;
34import org.onosproject.event.ListenerRegistry;
35import org.onosproject.net.Host;
Jian Liec5c32b2018-07-13 14:28:58 +090036import org.onosproject.net.HostLocation;
Jian Liecae4382018-06-28 17:41:12 +090037import org.onosproject.net.host.HostEvent;
38import org.onosproject.net.host.HostListener;
39import org.onosproject.net.host.HostService;
40import org.onosproject.openstacknetworking.api.Constants;
41import org.onosproject.openstacknetworking.api.InstancePort;
42import org.onosproject.openstacknetworking.api.InstancePortAdminService;
43import org.onosproject.openstacknetworking.api.InstancePortEvent;
44import org.onosproject.openstacknetworking.api.InstancePortListener;
45import org.onosproject.openstacknetworking.api.InstancePortService;
46import org.onosproject.openstacknetworking.api.InstancePortStore;
47import org.onosproject.openstacknetworking.api.InstancePortStoreDelegate;
48import org.slf4j.Logger;
49
Jian Li078cd202018-07-23 18:58:20 +090050import java.util.Objects;
Jian Liecae4382018-06-28 17:41:12 +090051import java.util.Set;
52import java.util.stream.Collectors;
53
54import static com.google.common.base.Preconditions.checkArgument;
55import static com.google.common.base.Preconditions.checkNotNull;
Jian Liec5c32b2018-07-13 14:28:58 +090056import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_NETWORK_ID;
57import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_PORT_ID;
Jian Liecae4382018-06-28 17:41:12 +090058import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Jian Liec5c32b2018-07-13 14:28:58 +090059import static org.onosproject.openstacknetworking.api.InstancePort.State.INACTIVE;
60import static org.onosproject.openstacknetworking.api.InstancePort.State.MIGRATED;
61import static org.onosproject.openstacknetworking.api.InstancePort.State.MIGRATING;
Jian Liecae4382018-06-28 17:41:12 +090062import static org.slf4j.LoggerFactory.getLogger;
63
64/**
65 * Provides implementation of administering and interfacing instance ports.
66 * It also provides instance port events for the hosts mapped to OpenStack VM interface.
67 */
68@Service
Jian Liec5c32b2018-07-13 14:28:58 +090069@Component(immediate = true)
Jian Liecae4382018-06-28 17:41:12 +090070public class InstancePortManager
71 extends ListenerRegistry<InstancePortEvent, InstancePortListener>
72 implements InstancePortService, InstancePortAdminService {
73
74 protected final Logger log = getLogger(getClass());
75
76 private static final String MSG_INSTANCE_PORT = "Instance port %s %s";
77 private static final String MSG_CREATED = "created";
78 private static final String MSG_UPDATED = "updated";
79 private static final String MSG_REMOVED = "removed";
80
81 private static final String ERR_NULL_INSTANCE_PORT = "Instance port cannot be null";
82 private static final String ERR_NULL_INSTANCE_PORT_ID = "Instance port ID cannot be null";
83 private static final String ERR_NULL_MAC_ADDRESS = "MAC address cannot be null";
84 private static final String ERR_NULL_IP_ADDRESS = "IP address cannot be null";
85 private static final String ERR_NULL_NETWORK_ID = "Network ID cannot be null";
86
87 private static final String ERR_IN_USE = " still in use";
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected CoreService coreService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected InstancePortStore instancePortStore;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li078cd202018-07-23 18:58:20 +090096 protected LeadershipService leadershipService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected ClusterService clusterService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liecae4382018-06-28 17:41:12 +0900102 protected HostService hostService;
103
104 private final InstancePortStoreDelegate
105 delegate = new InternalInstancePortStoreDelegate();
106 private final InternalHostListener
107 hostListener = new InternalHostListener();
108
Jian Li078cd202018-07-23 18:58:20 +0900109 private ApplicationId appId;
110 private NodeId localNodeId;
111
Jian Liecae4382018-06-28 17:41:12 +0900112 @Activate
113 protected void activate() {
Jian Li078cd202018-07-23 18:58:20 +0900114 appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
115 localNodeId = clusterService.getLocalNode().id();
Jian Liecae4382018-06-28 17:41:12 +0900116 instancePortStore.setDelegate(delegate);
117 hostService.addListener(hostListener);
Jian Li078cd202018-07-23 18:58:20 +0900118 leadershipService.runForLeadership(appId.name());
119
Jian Liecae4382018-06-28 17:41:12 +0900120 log.info("Started");
121 }
122
123 @Deactivate
124 protected void deactivate() {
125 hostService.removeListener(hostListener);
126 instancePortStore.unsetDelegate(delegate);
Jian Li078cd202018-07-23 18:58:20 +0900127 leadershipService.withdraw(appId.name());
128
Jian Liecae4382018-06-28 17:41:12 +0900129 log.info("Stopped");
130 }
131
132 @Override
133 public void createInstancePort(InstancePort instancePort) {
134 checkNotNull(instancePort, ERR_NULL_INSTANCE_PORT);
135 checkArgument(!Strings.isNullOrEmpty(instancePort.portId()),
136 ERR_NULL_INSTANCE_PORT_ID);
137
138 instancePortStore.createInstancePort(instancePort);
139 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(),
140 MSG_CREATED));
141 }
142
143 @Override
144 public void updateInstancePort(InstancePort instancePort) {
145 checkNotNull(instancePort, ERR_NULL_INSTANCE_PORT);
146 checkArgument(!Strings.isNullOrEmpty(instancePort.portId()),
147 ERR_NULL_INSTANCE_PORT_ID);
148
Jian Lida03ce92018-07-24 21:41:53 +0900149 // in case OpenStack removes the port prior to OVS, we will not update
150 // the instance port as it does not exist in the store
151 if (instancePortStore.instancePort(instancePort.portId()) == null) {
152 log.warn("Unable to update instance port {}, as it does not exist", instancePort.portId());
153 return;
154 }
155
Jian Liecae4382018-06-28 17:41:12 +0900156 instancePortStore.updateInstancePort(instancePort);
157 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(), MSG_UPDATED));
158 }
159
160 @Override
161 public void removeInstancePort(String portId) {
162 checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_INSTANCE_PORT_ID);
163
164 synchronized (this) {
165 if (isInstancePortInUse(portId)) {
166 final String error =
167 String.format(MSG_INSTANCE_PORT, portId, ERR_IN_USE);
168 throw new IllegalStateException(error);
169 }
170 InstancePort instancePort = instancePortStore.removeInstancePort(portId);
171 if (instancePort != null) {
172 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(), MSG_REMOVED));
173 }
174 }
175 }
176
177 @Override
178 public void clear() {
179 instancePortStore.clear();
180 }
181
182 @Override
183 public InstancePort instancePort(MacAddress macAddress) {
184 checkNotNull(macAddress, ERR_NULL_MAC_ADDRESS);
185
186 return instancePortStore.instancePorts().stream()
187 .filter(port -> port.macAddress().equals(macAddress))
188 .findFirst().orElse(null);
189 }
190
191 @Override
192 public InstancePort instancePort(IpAddress ipAddress, String osNetId) {
193 checkNotNull(ipAddress, ERR_NULL_IP_ADDRESS);
194 checkNotNull(osNetId, ERR_NULL_NETWORK_ID);
195
196 return instancePortStore.instancePorts().stream()
197 .filter(port -> port.networkId().equals(osNetId))
198 .filter(port -> port.ipAddress().equals(ipAddress))
199 .findFirst().orElse(null);
200 }
201
202 @Override
203 public InstancePort instancePort(String portId) {
204 checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_INSTANCE_PORT_ID);
205
206 return instancePortStore.instancePort(portId);
207 }
208
209 @Override
210 public Set<InstancePort> instancePorts() {
211 Set<InstancePort> ports = instancePortStore.instancePorts();
212
213 return ImmutableSet.copyOf(ports);
214 }
215
216 @Override
217 public Set<InstancePort> instancePorts(String osNetId) {
218 checkNotNull(osNetId, ERR_NULL_NETWORK_ID);
219
220 Set<InstancePort> ports = instancePortStore.instancePorts().stream()
221 .filter(port -> port.networkId().equals(osNetId))
222 .collect(Collectors.toSet());
223
224 return ImmutableSet.copyOf(ports);
225 }
226
Jian Liecae4382018-06-28 17:41:12 +0900227 private boolean isInstancePortInUse(String portId) {
228 // TODO add checking logic
229 return false;
230 }
231
232 private class InternalInstancePortStoreDelegate implements InstancePortStoreDelegate {
233
234 @Override
235 public void notify(InstancePortEvent event) {
236 if (event != null) {
237 log.trace("send instance port event {}", event);
238 process(event);
239 }
240 }
241 }
242
243 /**
244 * An internal listener that listens host event generated by HostLocationTracker
245 * in DistributedHostStore. The role of this listener is to convert host event
246 * to instance port event and post to the subscribers that have interested on
247 * this type of event.
248 */
249 private class InternalHostListener implements HostListener {
250
251 @Override
252 public boolean isRelevant(HostEvent event) {
253 Host host = event.subject();
254 if (!isValidHost(host)) {
255 log.debug("Invalid host detected, ignore it {}", host);
256 return false;
257 }
Jian Li078cd202018-07-23 18:58:20 +0900258
259 // do not allow to proceed without leadership
260 NodeId leader = leadershipService.getLeader(appId.name());
261 return Objects.equals(localNodeId, leader);
Jian Liecae4382018-06-28 17:41:12 +0900262 }
263
264 @Override
265 public void event(HostEvent event) {
266 InstancePort instPort = DefaultInstancePort.from(event.subject(), ACTIVE);
267
268 switch (event.type()) {
269 case HOST_UPDATED:
270 updateInstancePort(instPort);
271 break;
272 case HOST_ADDED:
Jian Liec5c32b2018-07-13 14:28:58 +0900273 InstancePort existingPort = instancePort(instPort.portId());
274 if (existingPort == null) {
275 // first time to add instance
276 createInstancePort(instPort);
277 } else {
Jian Li46b74002018-07-15 18:39:08 +0900278 if (existingPort.state() == INACTIVE) {
Jian Liee8214a2018-07-21 20:07:28 +0900279
280 if (instPort.deviceId().equals(existingPort.deviceId())) {
281
282 // VM RESTART case
283 // if the ID of switch where VM is attached to is
284 // identical, we can assume that the VM was
285 // restarted in the same location;
286 // note that the switch port number where VM is
287 // attached can be varied per each restart
288 updateInstancePort(instPort);
289 } else {
290
291 // VM COLD MIGRATION case
292 // if the ID of switch where VM is attached to is
293 // varied, we can assume that the VM was migrated
294 // to a new location
295 updateInstancePort(instPort.updateState(MIGRATING));
296 InstancePort updated = instPort.updateState(MIGRATED);
297 updateInstancePort(updated.updatePrevLocation(
298 existingPort.deviceId(), existingPort.portNumber()));
299 }
Jian Liec5c32b2018-07-13 14:28:58 +0900300 }
301 }
Jian Liecae4382018-06-28 17:41:12 +0900302 break;
303 case HOST_REMOVED:
Jian Liec5c32b2018-07-13 14:28:58 +0900304 // we will remove instance port from persistent store,
Jian Liee8214a2018-07-21 20:07:28 +0900305 // only if we receive port removal signal from neutron.
Jian Liec5c32b2018-07-13 14:28:58 +0900306 // by default, we update the instance port state to INACTIVE
307 // to indicate the instance is terminated
308 updateInstancePort(instPort.updateState(INACTIVE));
Jian Liecae4382018-06-28 17:41:12 +0900309 break;
310 case HOST_MOVED:
Jian Liec5c32b2018-07-13 14:28:58 +0900311 Host oldHost = event.prevSubject();
312 Host currHost = event.subject();
313
314 // in the middle of VM migration
315 if (oldHost.locations().size() < currHost.locations().size()) {
316 updateInstancePort(instPort.updateState(MIGRATING));
317 }
318
319 // finish of VM migration
320 if (oldHost.locations().size() > currHost.locations().size()) {
321 Set<HostLocation> diff =
322 Sets.difference(oldHost.locations(), currHost.locations());
323 HostLocation location = diff.stream().findFirst().orElse(null);
324
325 if (location != null) {
326 InstancePort updated = instPort.updateState(MIGRATED);
Jian Li46b74002018-07-15 18:39:08 +0900327 updateInstancePort(updated.updatePrevLocation(
Jian Liec5c32b2018-07-13 14:28:58 +0900328 location.deviceId(), location.port()));
329 }
330 }
Jian Liecae4382018-06-28 17:41:12 +0900331 break;
332 default:
333 break;
334 }
335 }
336
337 private boolean isValidHost(Host host) {
338 return !host.ipAddresses().isEmpty() &&
339 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
340 host.annotations().value(ANNOTATION_PORT_ID) != null;
341 }
342 }
343}