blob: d645b8fa93cc6fdbb807150a60a7a8007565c25d [file] [log] [blame]
Hyunsun Mooneaf75e62016-09-27 16:40:23 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
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.opencord.cordvtn.impl;
17
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
23import org.apache.felix.scr.annotations.Service;
24import org.onlab.util.Tools;
25import org.onosproject.event.ListenerRegistry;
26import org.onosproject.net.Host;
27import org.onosproject.net.HostId;
28import org.onosproject.net.host.HostService;
29import org.opencord.cordvtn.api.CordVtnAdminService;
30import org.opencord.cordvtn.api.CordVtnService;
31import org.opencord.cordvtn.api.CordVtnStore;
32import org.opencord.cordvtn.api.CordVtnStoreDelegate;
33import org.opencord.cordvtn.api.Instance;
34import org.opencord.cordvtn.api.NetworkId;
35import org.opencord.cordvtn.api.PortId;
36import org.opencord.cordvtn.api.ServiceNetwork;
37import org.opencord.cordvtn.api.ServicePort;
38import org.opencord.cordvtn.api.SubnetId;
39import org.opencord.cordvtn.api.VtnNetwork;
40import org.opencord.cordvtn.api.VtnNetworkEvent;
41import org.opencord.cordvtn.api.VtnNetworkListener;
42import org.opencord.cordvtn.api.VtnPort;
43import org.openstack4j.model.network.Network;
44import org.openstack4j.model.network.Port;
45import org.openstack4j.model.network.Subnet;
46import org.slf4j.Logger;
47
48import java.util.Objects;
49import java.util.Optional;
50import java.util.Set;
51import java.util.stream.Collectors;
52
53import static com.google.common.base.Preconditions.checkNotNull;
54import static org.opencord.cordvtn.api.Instance.NETWORK_ID;
55import static org.slf4j.LoggerFactory.getLogger;
56
57/**
58 * Provides implementation of administering and interfacing VTN networks.
59 */
60@Component(immediate = true)
61@Service
62public class CordVtnManager extends ListenerRegistry<VtnNetworkEvent, VtnNetworkListener>
63 implements CordVtnAdminService, CordVtnService {
64
65 protected final Logger log = getLogger(getClass());
66
67 private static final String MSG_SERVICE_NET = "VTN network %s %s";
68 private static final String MSG_SERVICE_PORT = "VTN port %s %s";
69 private static final String MSG_NET = "Network %s %s";
70 private static final String MSG_PORT = "Port %s %s";
71 private static final String MSG_SUBNET = "Subnet %s %s";
72
73 private static final String CREATED = "created";
74 private static final String UPDATED = "updated";
75 private static final String REMOVED = "removed";
76
77 private static final String ERR_NULL_SERVICE_PORT = "Service port cannot be null";
78 private static final String ERR_NULL_SERVICE_NET = "Service network cannot be null";
79 private static final String ERR_NULL_PORT = "Port cannot be null";
80 private static final String ERR_NULL_NET = "Network cannot be null";
81 private static final String ERR_NULL_SUBNET = "Subnet cannot be null";
82 private static final String ERR_NULL_PORT_ID = "Port ID cannot be null";
83 private static final String ERR_NULL_NET_ID = "Network ID cannot be null";
84 private static final String ERR_NULL_SUBNET_ID = "Subnet ID cannot be null";
85
86 private static final String ERR_SYNC = "VTN store is out of sync: ";
87 private static final String ERR_NOT_FOUND = " does not exist";
88 private static final String ERR_IN_USE_INSTANCE = "There are instances still in use on the network %s";
89 private static final String ERR_IN_USE_NETWORK = "There are subscribers still in use on the network %s";
90 private static final String ERR_IN_USE_PORT = "There are ports still in use on the network %s";
91 private static final String ERR_SUBNET_DUPLICATE = "Subnet already exists for network %s";
92
93 private static final String PORT = "port ";
94 private static final String NETWORK = "network ";
95 private static final String SUBNET = "subnet for ";
96 private static final String PROVIDER = "provider ";
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected HostService hostService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected CordVtnStore store;
103
104 private CordVtnStoreDelegate delegate = new InternalCordVtnStoreDelegate();
105
106 @Activate
107 protected void activate() {
108 store.setDelegate(delegate);
109 log.info("Started");
110 }
111
112 @Deactivate
113 protected void deactivate() {
114 store.unsetDelegate(delegate);
115 log.info("Stopped");
116 }
117
118 @Override
119 public void createVtnNetwork(ServiceNetwork serviceNet) {
120 checkNotNull(serviceNet, ERR_NULL_SERVICE_NET);
121 synchronized (this) {
122 Network network = store.getNetwork(serviceNet.id());
123 if (network == null) {
124 final String error = ERR_SYNC + NETWORK + serviceNet.id() + ERR_NOT_FOUND;
125 throw new IllegalStateException(error);
126 }
127
128 Subnet subnet = getSubnet(serviceNet.id());
129 if (subnet == null) {
130 final String error = ERR_SYNC + SUBNET + serviceNet.id() + ERR_NOT_FOUND;
131 throw new IllegalStateException(error);
132 }
133
134 // TODO check VTN network instead of network
135 serviceNet.providers().stream().forEach(provider -> {
136 if (store.getNetwork(provider.id()) == null) {
137 final String error = ERR_SYNC + PROVIDER + provider.id() + ERR_NOT_FOUND;
138 throw new IllegalStateException(error);
139 }
140 });
141
142 store.createVtnNetwork(VtnNetwork.of(network, subnet, serviceNet));
143 log.info(String.format(MSG_SERVICE_NET, CREATED, serviceNet.id()));
144 }
145 }
146
147 @Override
148 public void updateVtnNetwork(ServiceNetwork serviceNet) {
149 checkNotNull(serviceNet, ERR_NULL_SERVICE_NET);
150 synchronized (this) {
151 VtnNetwork existing = store.getVtnNetwork(serviceNet.id());
152 if (existing == null) {
153 final String error = ERR_SYNC + NETWORK + serviceNet.id() + ERR_NOT_FOUND;
154 throw new IllegalStateException(error);
155 }
156 // only providers update is allowed
157 VtnNetwork updated = VtnNetwork.builder(existing)
158 .providers(serviceNet.providers())
159 .build();
160 store.updateVtnNetwork(updated);
161 log.info(String.format(MSG_SERVICE_NET, UPDATED, serviceNet.id()));
162 }
163 }
164
165 @Override
166 public void removeVtnNetwork(NetworkId netId) {
167 checkNotNull(netId, ERR_NULL_NET_ID);
168 // TODO check if the network still exists?
169 store.removeVtnNetwork(netId);
170 log.info(String.format(MSG_SERVICE_NET, REMOVED, netId));
171 }
172
173 @Override
174 public void createVtnPort(ServicePort servicePort) {
175 checkNotNull(servicePort, ERR_NULL_SERVICE_PORT);
176 synchronized (this) {
177 Port port = store.getPort(servicePort.id());
178 if (port == null) {
179 final String error = ERR_SYNC + PORT + servicePort.id() + ERR_NOT_FOUND;
180 throw new IllegalStateException(error);
181 }
182 store.createVtnPort(VtnPort.of(port, servicePort));
183 log.info(String.format(MSG_SERVICE_PORT, CREATED, servicePort.id()));
184 }
185 }
186
187 @Override
188 public void updateVtnPort(ServicePort servicePort) {
189 checkNotNull(servicePort, ERR_NULL_SERVICE_PORT);
190 synchronized (this) {
191 VtnPort vtnPort = store.getVtnPort(servicePort.id());
192 if (vtnPort == null) {
193 final String error = ERR_SYNC + PORT + servicePort.id() + ERR_NOT_FOUND;
194 throw new IllegalStateException(error);
195 }
196 store.updateVtnPort(VtnPort.of(vtnPort, servicePort));
197 log.info(String.format(MSG_SERVICE_PORT, UPDATED, servicePort.id()));
198 }
199 }
200
201 @Override
202 public void removeVtnPort(PortId portId) {
203 checkNotNull(portId, ERR_NULL_PORT_ID);
204 store.removeVtnPort(portId);
205 log.info(String.format(MSG_SERVICE_PORT, REMOVED, portId));
206 }
207
208 @Override
209 public void createNetwork(Network network) {
210 checkNotNull(network, ERR_NULL_NET);
211 store.createNetwork(network);
212 log.info(String.format(MSG_NET, CREATED, network.getId()));
213 }
214
215 @Override
216 public void updateNetwork(Network network) {
217 checkNotNull(network, ERR_NULL_NET);
218 store.updateNetwork(network);
219 log.info(String.format(MSG_NET, UPDATED, network.getId()));
220 }
221
222 @Override
223 public void removeNetwork(NetworkId netId) {
224 checkNotNull(netId, ERR_NULL_NET_ID);
225 // FIXME Neutron removes network anyway even if there's an exception here
226 store.removeNetwork(netId);
227 log.info(String.format(MSG_NET, REMOVED, netId));
228 }
229
230 @Override
231 public void createPort(Port port) {
232 checkNotNull(port, ERR_NULL_PORT);
233 synchronized (this) {
234 if (store.getNetwork(NetworkId.of(port.getNetworkId())) == null) {
235 final String error = ERR_SYNC + port.getNetworkId() + ERR_NOT_FOUND;
236 throw new IllegalStateException(error);
237 }
238 store.createPort(port);
239 log.info(String.format(MSG_PORT, CREATED, port.getId()));
240 }
241 }
242
243 @Override
244 public void updatePort(Port port) {
245 checkNotNull(port, ERR_NULL_PORT);
246 synchronized (this) {
247 if (store.getNetwork(NetworkId.of(port.getNetworkId())) == null) {
248 final String error = ERR_SYNC + port.getNetworkId() + ERR_NOT_FOUND;
249 throw new IllegalStateException(error);
250 }
251 store.updatePort(port);
252 log.info(String.format(MSG_PORT, UPDATED, port.getId()));
253 }
254 }
255
256 @Override
257 public void removePort(PortId portId) {
258 checkNotNull(portId, ERR_NULL_PORT_ID);
259 synchronized (this) {
260 if (getInstance(portId) != null) {
261 final String error = String.format(ERR_IN_USE_PORT, portId);
262 throw new IllegalStateException(error);
263 }
264 removeVtnPort(portId);
265 store.removePort(portId);
266 log.info(String.format(MSG_PORT, REMOVED, portId));
267 }
268 }
269
270 @Override
271 public void createSubnet(Subnet subnet) {
272 checkNotNull(subnet, ERR_NULL_SUBNET);
273 synchronized (this) {
274 if (store.getNetwork(NetworkId.of(subnet.getNetworkId())) == null) {
275 final String error = ERR_SYNC + subnet.getNetworkId() + ERR_NOT_FOUND;
276 throw new IllegalStateException(error);
277 }
278
279 if (getSubnet(NetworkId.of(subnet.getNetworkId())) != null) {
280 // CORD does not allow multiple subnets for a network
281 final String error = String.format(ERR_SUBNET_DUPLICATE, subnet.getNetworkId());
282 throw new IllegalStateException(error);
283 }
284 store.createSubnet(subnet);
285 log.info(String.format(MSG_SUBNET, CREATED, subnet.getId()));
286 }
287 }
288
289 @Override
290 public void updateSubnet(Subnet subnet) {
291 checkNotNull(subnet, ERR_NULL_SUBNET);
292 synchronized (this) {
293 if (store.getNetwork(NetworkId.of(subnet.getNetworkId())) == null) {
294 final String error = ERR_SYNC + subnet.getNetworkId() + ERR_NOT_FOUND;
295 throw new IllegalStateException(error);
296 }
297 store.updateSubnet(subnet);
298 log.info(String.format(MSG_SUBNET, UPDATED, subnet.getId()));
299 }
300 }
301
302 @Override
303 public void removeSubnet(SubnetId subnetId) {
304 checkNotNull(subnetId, ERR_NULL_SUBNET_ID);
305 // FIXME Neutron removes network anyway even if there's an exception here
306 synchronized (this) {
307 removeVtnNetwork(NetworkId.of(store.getSubnet(subnetId).getNetworkId()));
308 store.removeSubnet(subnetId);
309 log.info(String.format(MSG_SUBNET, REMOVED, subnetId));
310 }
311 }
312
313 @Override
314 public VtnNetwork getVtnNetwork(NetworkId netId) {
315 checkNotNull(netId, ERR_NULL_NET_ID);
316 return store.getVtnNetwork(netId);
317 }
318
319 @Override
320 public VtnNetwork getVtnNetworkOrDefault(NetworkId netId) {
321 checkNotNull(netId, ERR_NULL_NET_ID);
322
323 // return default VTN network if the network and subnet exist
324 VtnNetwork vtnNet = store.getVtnNetwork(netId);
325 return vtnNet == null ? getDefaultVtnNetwork(netId) : vtnNet;
326 }
327
328 @Override
329 public Set<VtnNetwork> getVtnNetworks() {
330 return store.getVtnNetworks();
331 }
332
333 @Override
334 public VtnPort getVtnPort(PortId portId) {
335 checkNotNull(portId, ERR_NULL_PORT_ID);
336 return store.getVtnPort(portId);
337 }
338
339 @Override
340 public VtnPort getVtnPortOrDefault(PortId portId) {
341 checkNotNull(portId, ERR_NULL_PORT_ID);
342
343 // return default VTN port if the port exists
344 VtnPort vtnPort = store.getVtnPort(portId);
345 return vtnPort == null ? getDefaultPort(portId) : vtnPort;
346 }
347
348 @Override
349 public VtnPort getVtnPort(String portName) {
350 Optional<Port> port = store.getPorts()
351 .stream()
352 .filter(p -> p.getId().contains(portName.substring(3)))
353 .findFirst();
354 if (!port.isPresent()) {
355 return null;
356 }
357 return getVtnPortOrDefault(PortId.of(port.get().getId()));
358 }
359
360 @Override
361 public Set<VtnPort> getVtnPorts() {
362 return store.getVtnPorts();
363 }
364
365 @Override
366 public Network getNetwork(NetworkId netId) {
367 checkNotNull(netId, ERR_NULL_NET_ID);
368 return store.getNetwork(netId);
369 }
370
371 @Override
372 public Set<Network> getNetworks() {
373 return store.getNetworks();
374 }
375
376 @Override
377 public Port getPort(PortId portId) {
378 checkNotNull(portId, ERR_NULL_PORT_ID);
379 return store.getPort(portId);
380 }
381
382 @Override
383 public Set<Port> getPorts() {
384 return store.getPorts();
385 }
386
387 @Override
388 public Subnet getSubnet(SubnetId subnetId) {
389 checkNotNull(subnetId, ERR_NULL_SUBNET_ID);
390 return store.getSubnet(subnetId);
391 }
392
393 @Override
394 public Set<Subnet> getSubnets() {
395 return store.getSubnets();
396 }
397
398 @Override
399 public Instance getInstance(PortId portId) {
400 VtnPort vtnPort = getVtnPortOrDefault(portId);
401 if (vtnPort == null) {
402 final String error = "Failed to build VTN port for " + portId.id();
403 throw new IllegalStateException(error);
404 }
405
406 Host host = hostService.getHost(HostId.hostId(vtnPort.mac()));
407 if (host == null) {
408 return null;
409 }
410 return Instance.of(host);
411 }
412
413 @Override
414 public Set<Instance> getInstances(NetworkId netId) {
415 return Tools.stream(hostService.getHosts())
416 .filter(host -> Objects.equals(
417 host.annotations().value(NETWORK_ID),
418 netId.id()))
419 .map(Instance::of)
420 .collect(Collectors.toSet());
421 }
422
423 private VtnNetwork getDefaultVtnNetwork(NetworkId netId) {
424 Network network = getNetwork(netId);
425 Subnet subnet = getSubnet(netId);
426 if (network == null || subnet == null) {
427 return null;
428 }
429 return VtnNetwork.of(network, subnet, null);
430 }
431
432 private VtnPort getDefaultPort(PortId portId) {
433 Port port = getPort(portId);
434 if (port == null) {
435 return null;
436 }
437 return VtnPort.of(port, null);
438 }
439
440 private Subnet getSubnet(NetworkId netId) {
441 // TODO fix networking-onos to send Network UPDATE when subnet created
442 Optional<Subnet> subnet = getSubnets().stream()
443 .filter(s -> Objects.equals(s.getNetworkId(), netId.id()))
444 .findFirst();
445 return subnet.orElse(null);
446 }
447
448 private class InternalCordVtnStoreDelegate implements CordVtnStoreDelegate {
449
450 @Override
451 public void notify(VtnNetworkEvent event) {
452 if (event != null) {
453 log.trace("send service network event {}", event);
454 process(event);
455 }
456 }
457 }
458}