blob: 2383e6228749204f416d072befdfc91b33bf5ec4 [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;
29import org.onosproject.core.CoreService;
30import org.onosproject.event.ListenerRegistry;
31import org.onosproject.net.Host;
Jian Liec5c32b2018-07-13 14:28:58 +090032import org.onosproject.net.HostLocation;
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;
44import org.slf4j.Logger;
45
46import java.util.Set;
47import java.util.stream.Collectors;
48
49import static com.google.common.base.Preconditions.checkArgument;
50import static com.google.common.base.Preconditions.checkNotNull;
Jian Liec5c32b2018-07-13 14:28:58 +090051import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_NETWORK_ID;
52import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_PORT_ID;
Jian Liecae4382018-06-28 17:41:12 +090053import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Jian Liec5c32b2018-07-13 14:28:58 +090054import static org.onosproject.openstacknetworking.api.InstancePort.State.INACTIVE;
55import static org.onosproject.openstacknetworking.api.InstancePort.State.MIGRATED;
56import static org.onosproject.openstacknetworking.api.InstancePort.State.MIGRATING;
Jian Liecae4382018-06-28 17:41:12 +090057import static org.slf4j.LoggerFactory.getLogger;
58
59/**
60 * Provides implementation of administering and interfacing instance ports.
61 * It also provides instance port events for the hosts mapped to OpenStack VM interface.
62 */
63@Service
Jian Liec5c32b2018-07-13 14:28:58 +090064@Component(immediate = true)
Jian Liecae4382018-06-28 17:41:12 +090065public class InstancePortManager
66 extends ListenerRegistry<InstancePortEvent, InstancePortListener>
67 implements InstancePortService, InstancePortAdminService {
68
69 protected final Logger log = getLogger(getClass());
70
71 private static final String MSG_INSTANCE_PORT = "Instance port %s %s";
72 private static final String MSG_CREATED = "created";
73 private static final String MSG_UPDATED = "updated";
74 private static final String MSG_REMOVED = "removed";
75
76 private static final String ERR_NULL_INSTANCE_PORT = "Instance port cannot be null";
77 private static final String ERR_NULL_INSTANCE_PORT_ID = "Instance port ID cannot be null";
78 private static final String ERR_NULL_MAC_ADDRESS = "MAC address cannot be null";
79 private static final String ERR_NULL_IP_ADDRESS = "IP address cannot be null";
80 private static final String ERR_NULL_NETWORK_ID = "Network ID cannot be null";
81
82 private static final String ERR_IN_USE = " still in use";
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected CoreService coreService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected InstancePortStore instancePortStore;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected HostService hostService;
92
93 private final InstancePortStoreDelegate
94 delegate = new InternalInstancePortStoreDelegate();
95 private final InternalHostListener
96 hostListener = new InternalHostListener();
97
98 @Activate
99 protected void activate() {
100 coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
101 instancePortStore.setDelegate(delegate);
102 hostService.addListener(hostListener);
103 log.info("Started");
104 }
105
106 @Deactivate
107 protected void deactivate() {
108 hostService.removeListener(hostListener);
109 instancePortStore.unsetDelegate(delegate);
110 log.info("Stopped");
111 }
112
113 @Override
114 public void createInstancePort(InstancePort instancePort) {
115 checkNotNull(instancePort, ERR_NULL_INSTANCE_PORT);
116 checkArgument(!Strings.isNullOrEmpty(instancePort.portId()),
117 ERR_NULL_INSTANCE_PORT_ID);
118
119 instancePortStore.createInstancePort(instancePort);
120 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(),
121 MSG_CREATED));
122 }
123
124 @Override
125 public void updateInstancePort(InstancePort instancePort) {
126 checkNotNull(instancePort, ERR_NULL_INSTANCE_PORT);
127 checkArgument(!Strings.isNullOrEmpty(instancePort.portId()),
128 ERR_NULL_INSTANCE_PORT_ID);
129
130 instancePortStore.updateInstancePort(instancePort);
131 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(), MSG_UPDATED));
132 }
133
134 @Override
135 public void removeInstancePort(String portId) {
136 checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_INSTANCE_PORT_ID);
137
138 synchronized (this) {
139 if (isInstancePortInUse(portId)) {
140 final String error =
141 String.format(MSG_INSTANCE_PORT, portId, ERR_IN_USE);
142 throw new IllegalStateException(error);
143 }
144 InstancePort instancePort = instancePortStore.removeInstancePort(portId);
145 if (instancePort != null) {
146 log.info(String.format(MSG_INSTANCE_PORT, instancePort.portId(), MSG_REMOVED));
147 }
148 }
149 }
150
151 @Override
152 public void clear() {
153 instancePortStore.clear();
154 }
155
156 @Override
157 public InstancePort instancePort(MacAddress macAddress) {
158 checkNotNull(macAddress, ERR_NULL_MAC_ADDRESS);
159
160 return instancePortStore.instancePorts().stream()
161 .filter(port -> port.macAddress().equals(macAddress))
162 .findFirst().orElse(null);
163 }
164
165 @Override
166 public InstancePort instancePort(IpAddress ipAddress, String osNetId) {
167 checkNotNull(ipAddress, ERR_NULL_IP_ADDRESS);
168 checkNotNull(osNetId, ERR_NULL_NETWORK_ID);
169
170 return instancePortStore.instancePorts().stream()
171 .filter(port -> port.networkId().equals(osNetId))
172 .filter(port -> port.ipAddress().equals(ipAddress))
173 .findFirst().orElse(null);
174 }
175
176 @Override
177 public InstancePort instancePort(String portId) {
178 checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_INSTANCE_PORT_ID);
179
180 return instancePortStore.instancePort(portId);
181 }
182
183 @Override
184 public Set<InstancePort> instancePorts() {
185 Set<InstancePort> ports = instancePortStore.instancePorts();
186
187 return ImmutableSet.copyOf(ports);
188 }
189
190 @Override
191 public Set<InstancePort> instancePorts(String osNetId) {
192 checkNotNull(osNetId, ERR_NULL_NETWORK_ID);
193
194 Set<InstancePort> ports = instancePortStore.instancePorts().stream()
195 .filter(port -> port.networkId().equals(osNetId))
196 .collect(Collectors.toSet());
197
198 return ImmutableSet.copyOf(ports);
199 }
200
Jian Liecae4382018-06-28 17:41:12 +0900201 private boolean isInstancePortInUse(String portId) {
202 // TODO add checking logic
203 return false;
204 }
205
206 private class InternalInstancePortStoreDelegate implements InstancePortStoreDelegate {
207
208 @Override
209 public void notify(InstancePortEvent event) {
210 if (event != null) {
211 log.trace("send instance port event {}", event);
212 process(event);
213 }
214 }
215 }
216
217 /**
218 * An internal listener that listens host event generated by HostLocationTracker
219 * in DistributedHostStore. The role of this listener is to convert host event
220 * to instance port event and post to the subscribers that have interested on
221 * this type of event.
222 */
223 private class InternalHostListener implements HostListener {
224
225 @Override
226 public boolean isRelevant(HostEvent event) {
227 Host host = event.subject();
228 if (!isValidHost(host)) {
229 log.debug("Invalid host detected, ignore it {}", host);
230 return false;
231 }
232 return true;
233 }
234
235 @Override
236 public void event(HostEvent event) {
237 InstancePort instPort = DefaultInstancePort.from(event.subject(), ACTIVE);
238
239 switch (event.type()) {
240 case HOST_UPDATED:
241 updateInstancePort(instPort);
242 break;
243 case HOST_ADDED:
Jian Liec5c32b2018-07-13 14:28:58 +0900244 InstancePort existingPort = instancePort(instPort.portId());
245 if (existingPort == null) {
246 // first time to add instance
247 createInstancePort(instPort);
248 } else {
249 // the instance was restarted
Jian Li46b74002018-07-15 18:39:08 +0900250 if (existingPort.state() == INACTIVE) {
251 updateInstancePort(instPort);
Jian Liec5c32b2018-07-13 14:28:58 +0900252 }
253 }
Jian Liecae4382018-06-28 17:41:12 +0900254 break;
255 case HOST_REMOVED:
Jian Liec5c32b2018-07-13 14:28:58 +0900256 // we will remove instance port from persistent store,
257 // only if we receive port removal signal from neutron
258 // by default, we update the instance port state to INACTIVE
259 // to indicate the instance is terminated
260 updateInstancePort(instPort.updateState(INACTIVE));
Jian Liecae4382018-06-28 17:41:12 +0900261 break;
262 case HOST_MOVED:
Jian Liec5c32b2018-07-13 14:28:58 +0900263 Host oldHost = event.prevSubject();
264 Host currHost = event.subject();
265
266 // in the middle of VM migration
267 if (oldHost.locations().size() < currHost.locations().size()) {
268 updateInstancePort(instPort.updateState(MIGRATING));
269 }
270
271 // finish of VM migration
272 if (oldHost.locations().size() > currHost.locations().size()) {
273 Set<HostLocation> diff =
274 Sets.difference(oldHost.locations(), currHost.locations());
275 HostLocation location = diff.stream().findFirst().orElse(null);
276
277 if (location != null) {
278 InstancePort updated = instPort.updateState(MIGRATED);
Jian Li46b74002018-07-15 18:39:08 +0900279 updateInstancePort(updated.updatePrevLocation(
Jian Liec5c32b2018-07-13 14:28:58 +0900280 location.deviceId(), location.port()));
281 }
282 }
Jian Liecae4382018-06-28 17:41:12 +0900283 break;
284 default:
285 break;
286 }
287 }
288
289 private boolean isValidHost(Host host) {
290 return !host.ipAddresses().isEmpty() &&
291 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
292 host.annotations().value(ANNOTATION_PORT_ID) != null;
293 }
294 }
295}