blob: 69b6fcb7114c3843ddf4b1485f2ecddccb8c3138 [file] [log] [blame]
Hyunsun Moon187bf532017-01-19 10:57:40 +09001/*
Brian O'Connor80dff972017-08-03 22:46:30 -07002 * Copyright 2017-present Open Networking Foundation
Hyunsun Moon187bf532017-01-19 10:57:40 +09003 *
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 com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Maps;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
Jonathan Hart86051062018-01-26 14:56:30 -080026import org.onlab.packet.IpAddress;
27import org.onlab.packet.IpPrefix;
28import org.onlab.packet.MacAddress;
Hyunsun Moon187bf532017-01-19 10:57:40 +090029import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
31import org.onosproject.event.ListenerRegistry;
32import org.onosproject.net.Host;
33import org.onosproject.net.HostId;
34import org.onosproject.net.config.ConfigFactory;
35import org.onosproject.net.config.NetworkConfigRegistry;
36import org.onosproject.net.config.basics.SubjectFactories;
37import org.onosproject.net.host.HostService;
38import org.opencord.cordvtn.api.Constants;
39import org.opencord.cordvtn.api.CordVtnConfig;
40import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
41import org.opencord.cordvtn.api.core.ServiceNetworkEvent;
42import org.opencord.cordvtn.api.core.ServiceNetworkListener;
43import org.opencord.cordvtn.api.core.ServiceNetworkService;
44import org.opencord.cordvtn.api.core.ServiceNetworkStore;
45import org.opencord.cordvtn.api.core.ServiceNetworkStoreDelegate;
46import org.opencord.cordvtn.api.net.NetworkId;
47import org.opencord.cordvtn.api.net.PortId;
Jonathan Hart86051062018-01-26 14:56:30 -080048import org.opencord.cordvtn.api.net.SegmentId;
Hyunsun Moon187bf532017-01-19 10:57:40 +090049import org.opencord.cordvtn.api.net.ServiceNetwork;
50import org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType;
51import org.opencord.cordvtn.api.net.ServicePort;
Jonathan Hart86051062018-01-26 14:56:30 -080052import org.opencord.cordvtn.rest.XosVtnNetworkingClient;
53import org.openstack4j.api.OSClient;
54import org.openstack4j.api.exceptions.AuthenticationException;
55import org.openstack4j.model.identity.Access;
56import org.openstack4j.openstack.OSFactory;
Hyunsun Moon187bf532017-01-19 10:57:40 +090057import org.slf4j.Logger;
58
59import java.util.Map;
60import java.util.Objects;
61import java.util.Set;
62import java.util.stream.Collectors;
63
64import static com.google.common.base.Preconditions.checkNotNull;
65import static org.slf4j.LoggerFactory.getLogger;
66
67/**
68 * Provides implementation of administering and interfacing service network and port.
69 */
70@Component(immediate = true)
71@Service
72public class ServiceNetworkManager extends ListenerRegistry<ServiceNetworkEvent, ServiceNetworkListener>
73 implements ServiceNetworkAdminService, ServiceNetworkService {
74
75 protected final Logger log = getLogger(getClass());
76
77 private static final String MSG_SERVICE_NET = "Service network %s %s";
78 private static final String MSG_SERVICE_PORT = "Service port %s %s";
79 private static final String MSG_PROVIDER_NET = "Provider network %s %s";
80 private static final String MSG_CREATED = "created";
81 private static final String MSG_UPDATED = "updated";
82 private static final String MSG_REMOVED = "removed";
83
84 private static final String ERR_NULL_SERVICE_NET = "Service network cannot be null";
85 private static final String ERR_NULL_SERVICE_NET_ID = "Service network ID cannot be null";
86 private static final String ERR_NULL_SERVICE_NET_TYPE = "Service network type cannot be null";
87 private static final String ERR_NULL_SERVICE_PORT = "Service port cannot be null";
88 private static final String ERR_NULL_SERVICE_PORT_ID = "Service port ID cannot be null";
Hyunsun Moon5510e342017-02-23 19:41:00 +090089 private static final String ERR_NULL_SERVICE_PORT_NAME = "Service port name cannot be null";
Hyunsun Moon187bf532017-01-19 10:57:40 +090090 private static final String ERR_NULL_SERVICE_PORT_NET_ID = "Service port network ID cannot be null";
91
92 private static final String ERR_NOT_FOUND = " does not exist";
93 private static final String ERR_IN_USE = " still in use";
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected NetworkConfigRegistry configRegistry;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected CoreService coreService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected HostService hostService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected ServiceNetworkStore snetStore;
106
Jonathan Hart86051062018-01-26 14:56:30 -0800107 private ApplicationId appId;
108
Hyunsun Moon187bf532017-01-19 10:57:40 +0900109 // TODO add cordvtn config service and move this
110 private static final Class<CordVtnConfig> CONFIG_CLASS = CordVtnConfig.class;
111 private final ConfigFactory configFactory =
112 new ConfigFactory<ApplicationId, CordVtnConfig>(
113 SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "cordvtn") {
114 @Override
115 public CordVtnConfig createConfig() {
116 return new CordVtnConfig();
117 }
118 };
119
120 private final ServiceNetworkStoreDelegate delegate = new InternalServiceNetworkStoreDelegate();
121
122 @Activate
123 protected void activate() {
Jonathan Hart86051062018-01-26 14:56:30 -0800124 appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
Hyunsun Moon187bf532017-01-19 10:57:40 +0900125 configRegistry.registerConfigFactory(configFactory);
126 snetStore.setDelegate(delegate);
127 log.info("Started");
128 }
129
130 @Deactivate
131 protected void deactivate() {
132 configRegistry.unregisterConfigFactory(configFactory);
133 snetStore.unsetDelegate(delegate);
134 log.info("Stopped");
135 }
136
137 @Override
138 public void purgeStates() {
139 snetStore.clear();
140 }
141
142 @Override
Jonathan Hart86051062018-01-26 14:56:30 -0800143 public void syncXosState() {
144 CordVtnConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
145 if (config == null) {
146 return;
147 }
148 syncXosState(config.getXosEndpoint(), config.getXosUsername(), config.getXosPassword());
149 }
150
151 @Override
152 public void syncXosState(String endpoint, String user, String password) {
153 checkNotNull(endpoint);
154 checkNotNull(user);
155 checkNotNull(password);
156
157 XosVtnNetworkingClient client = XosVtnNetworkingClient.builder()
158 .endpoint(endpoint)
159 .user(user)
160 .password(password)
161 .build();
162
163 client.requestSync();
164 }
165
166 @Override
167 public void syncNeutronState() {
168 CordVtnConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
169 if (config == null) {
170 return;
171 }
172 syncNeutronState(config.getOpenstackEndpoint(), config.getOpenstackTenant(),
173 config.getOpenstackUser(), config.getOpenstackPassword());
174 }
175
176 @Override
177 public void syncNeutronState(String endpoint, String tenant, String user, String password) {
178 Access osAccess;
179 try {
180 osAccess = OSFactory.builder()
181 .endpoint(endpoint)
182 .tenantName(tenant)
183 .credentials(user, password)
184 .authenticate()
185 .getAccess();
186 } catch (AuthenticationException e) {
187 log.warn("Authentication failed");
188 return;
189 } catch (Exception e) {
190 log.warn("OpenStack service endpoint is unreachable");
191 return;
192 }
193
194 OSClient osClient = OSFactory.clientFromAccess(osAccess);
195 osClient.networking().network().list().forEach(osNet -> {
196 ServiceNetwork snet = DefaultServiceNetwork.builder()
197 .id(NetworkId.of(osNet.getId()))
198 .name(osNet.getName())
199 .type(ServiceNetwork.NetworkType.PRIVATE)
200 .segmentId(SegmentId.of(Long.valueOf(osNet.getProviderSegID())))
201 .build();
202 if (serviceNetwork(snet.id()) != null) {
203 updateServiceNetwork(snet);
204 } else {
205 createServiceNetwork(snet);
206 }
207 });
208
209 osClient.networking().subnet().list().forEach(osSubnet -> {
210 ServiceNetwork snet = DefaultServiceNetwork.builder()
211 .id(NetworkId.of(osSubnet.getNetworkId()))
212 .subnet(IpPrefix.valueOf(osSubnet.getCidr()))
213 .serviceIp(IpAddress.valueOf(osSubnet.getGateway()))
214 .build();
215 updateServiceNetwork(snet);
216 });
217
218 osClient.networking().port().list().forEach(osPort -> {
219 ServicePort.Builder sportBuilder = DefaultServicePort.builder()
220 .id(PortId.of(osPort.getId()))
221 .name("tap" + osPort.getId().substring(0, 11))
222 .networkId(NetworkId.of(osPort.getNetworkId()));
223
224 if (osPort.getMacAddress() != null) {
225 sportBuilder.mac(MacAddress.valueOf(osPort.getMacAddress()));
226 }
227 if (!osPort.getFixedIps().isEmpty()) {
228 sportBuilder.ip(IpAddress.valueOf(
229 osPort.getFixedIps().iterator().next().getIpAddress()));
230 }
231 ServicePort sport = sportBuilder.build();
232 if (servicePort(sport.id()) != null) {
233 updateServicePort(sport);
234 } else {
235 createServicePort(sport);
236 }
237 });
238 }
239
240 @Override
Hyunsun Moon187bf532017-01-19 10:57:40 +0900241 public ServiceNetwork serviceNetwork(NetworkId netId) {
242 checkNotNull(netId, ERR_NULL_SERVICE_NET_ID);
243 return snetStore.serviceNetwork(netId);
244 }
245
246 @Override
247 public Set<ServiceNetwork> serviceNetworks() {
248 return snetStore.serviceNetworks();
249 }
250
251 @Override
252 public void createServiceNetwork(ServiceNetwork snet) {
253 checkNotNull(snet, ERR_NULL_SERVICE_NET);
254 checkNotNull(snet.id(), ERR_NULL_SERVICE_NET_ID);
255 checkNotNull(snet.type(), ERR_NULL_SERVICE_NET_TYPE);
256 synchronized (this) {
257 snet.providers().keySet().forEach(provider -> {
258 if (snetStore.serviceNetwork(provider) == null) {
259 final String error = String.format(
260 MSG_PROVIDER_NET, provider.id(), ERR_NOT_FOUND);
261 throw new IllegalStateException(error);
262 }
263 });
264 snetStore.createServiceNetwork(snet);
265 log.info(String.format(MSG_SERVICE_NET, snet.name(), MSG_CREATED));
266 }
267 }
268
269 @Override
270 public void updateServiceNetwork(ServiceNetwork snet) {
271 checkNotNull(snet, ERR_NULL_SERVICE_NET);
272 checkNotNull(snet.id(), ERR_NULL_SERVICE_NET_ID);
273 synchronized (this) {
274 ServiceNetwork existing = snetStore.serviceNetwork(snet.id());
275 if (existing == null) {
276 final String error = String.format(
277 MSG_SERVICE_NET, snet.id(), ERR_NOT_FOUND);
278 throw new IllegalStateException(error);
279 }
280 // TODO do not allow service type update if the network in use
281 snetStore.updateServiceNetwork(DefaultServiceNetwork.builder(existing, snet).build());
282 log.info(String.format(MSG_SERVICE_NET, existing.name(), MSG_UPDATED));
283 }
284 }
285
286 @Override
287 public void removeServiceNetwork(NetworkId netId) {
288 checkNotNull(netId, ERR_NULL_SERVICE_NET_ID);
289 synchronized (this) {
290 if (isNetworkInUse(netId)) {
291 final String error = String.format(MSG_SERVICE_NET, netId, ERR_IN_USE);
292 throw new IllegalStateException(error);
293 }
294 // remove dependencies on this network first
295 serviceNetworks().stream().filter(n -> isProvider(n, netId)).forEach(n -> {
296 Map<NetworkId, DependencyType> newProviders = Maps.newHashMap(n.providers());
297 newProviders.remove(netId);
298 ServiceNetwork updated = DefaultServiceNetwork.builder(n)
299 .providers(newProviders)
300 .build();
301 snetStore.updateServiceNetwork(updated);
302 });
303 ServiceNetwork snet = snetStore.removeServiceNetwork(netId);
304 log.info(String.format(MSG_SERVICE_NET, snet.name(), MSG_REMOVED));
305 }
306 }
307
308 @Override
309 public ServicePort servicePort(PortId portId) {
310 checkNotNull(portId, ERR_NULL_SERVICE_PORT_ID);
311 return snetStore.servicePort(portId);
312 }
313
314 @Override
315 public Set<ServicePort> servicePorts() {
316 return snetStore.servicePorts();
317 }
318
319 @Override
320 public Set<ServicePort> servicePorts(NetworkId netId) {
321 Set<ServicePort> sports = snetStore.servicePorts().stream()
322 .filter(p -> Objects.equals(p.networkId(), netId))
323 .collect(Collectors.toSet());
324 return ImmutableSet.copyOf(sports);
325 }
326
327 @Override
328 public void createServicePort(ServicePort sport) {
329 checkNotNull(sport, ERR_NULL_SERVICE_PORT);
330 checkNotNull(sport.id(), ERR_NULL_SERVICE_PORT_ID);
Hyunsun Moon5510e342017-02-23 19:41:00 +0900331 checkNotNull(sport.id(), ERR_NULL_SERVICE_PORT_NAME);
Hyunsun Moon187bf532017-01-19 10:57:40 +0900332 checkNotNull(sport.networkId(), ERR_NULL_SERVICE_PORT_NET_ID);
333 synchronized (this) {
334 ServiceNetwork existing = snetStore.serviceNetwork(sport.networkId());
335 if (existing == null) {
336 final String error = String.format(
337 MSG_SERVICE_NET, sport.networkId(), ERR_NOT_FOUND);
338 throw new IllegalStateException(error);
339 }
340 snetStore.createServicePort(sport);
341 log.info(String.format(MSG_SERVICE_PORT, sport.id(), MSG_CREATED));
342 }
343 }
344
345 @Override
346 public void updateServicePort(ServicePort sport) {
347 checkNotNull(sport, ERR_NULL_SERVICE_PORT);
348 checkNotNull(sport.id(), ERR_NULL_SERVICE_PORT_ID);
349 synchronized (this) {
350 ServicePort existing = snetStore.servicePort(sport.id());
351 if (existing == null) {
352 final String error = String.format(
353 MSG_SERVICE_PORT, sport.id(), ERR_NOT_FOUND);
354 throw new IllegalStateException(error);
355 }
356 snetStore.updateServicePort(DefaultServicePort.builder(existing, sport).build());
357 log.info(String.format(MSG_SERVICE_PORT, sport.id(), MSG_UPDATED));
358 }
359 }
360
361 @Override
362 public void removeServicePort(PortId portId) {
363 checkNotNull(portId, ERR_NULL_SERVICE_PORT_ID);
364 synchronized (this) {
365 if (isPortInUse(portId)) {
366 final String error = String.format(MSG_SERVICE_PORT, portId, ERR_IN_USE);
367 throw new IllegalStateException(error);
368 }
369 snetStore.removeServicePort(portId);
370 log.info(String.format(MSG_SERVICE_PORT, portId, MSG_REMOVED));
371 }
372 }
373
374 /**
375 * Returns if the given target network is a provider of the given network.
376 *
377 * @param snet service network
378 * @param targetId target network
379 * @return true if the service network is a provider of the target network
380 */
381 private boolean isProvider(ServiceNetwork snet, NetworkId targetId) {
382 checkNotNull(snet);
383 return snet.providers().keySet().contains(targetId);
384 }
385
386 private boolean isNetworkInUse(NetworkId netId) {
387 // TODO use instance service to see if there's running instance for the network
388 return !servicePorts(netId).isEmpty();
389 }
390
391 private boolean isPortInUse(PortId portId) {
392 ServicePort sport = servicePort(portId);
393 if (sport == null) {
394 final String error = String.format(MSG_SERVICE_PORT, portId, ERR_NOT_FOUND);
395 throw new IllegalStateException(error);
396 }
397 // TODO use instance service to see if there's running instance for the port
398 Host host = hostService.getHost(HostId.hostId(sport.mac()));
399 return host != null;
400 }
401
402 private class InternalServiceNetworkStoreDelegate implements ServiceNetworkStoreDelegate {
403
404 @Override
405 public void notify(ServiceNetworkEvent event) {
406 if (event != null) {
407 log.trace("send service network event {}", event);
408 process(event);
409 }
410 }
411 }
412}