blob: bb5e067fe25ecbffebbafc0b8e004c7c4b0f69ee [file] [log] [blame]
Hyunsun Moone7e4bb32016-05-16 04:32:45 -07001/*
2 * Copyright 2015-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 */
alshabibb4d31712016-06-01 18:51:03 -070016package org.opencord.cordvtn.impl;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070017
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070018import com.google.common.collect.Sets;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.packet.Ethernet;
26import org.onlab.packet.Ip4Address;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.MacAddress;
29import org.onlab.packet.VlanId;
alshabibb4d31712016-06-01 18:51:03 -070030import org.opencord.cordvtn.api.CordVtnConfig;
31import org.opencord.cordvtn.api.CordVtnService;
32import org.opencord.cordvtn.api.Instance;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070033import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
35import org.onosproject.dhcp.DhcpService;
Hyunsun Moon86e25e82016-05-18 21:28:06 -070036import org.onosproject.dhcp.IpAssignment;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070037import org.onosproject.mastership.MastershipService;
38import org.onosproject.net.ConnectPoint;
39import org.onosproject.net.DefaultAnnotations;
40import org.onosproject.net.Host;
41import org.onosproject.net.HostId;
42import org.onosproject.net.HostLocation;
43import org.onosproject.net.Port;
44import org.onosproject.net.config.ConfigFactory;
45import org.onosproject.net.config.NetworkConfigEvent;
46import org.onosproject.net.config.NetworkConfigListener;
47import org.onosproject.net.config.NetworkConfigRegistry;
48import org.onosproject.net.config.basics.SubjectFactories;
49import org.onosproject.net.device.DeviceService;
50import org.onosproject.net.host.DefaultHostDescription;
51import org.onosproject.net.host.HostDescription;
52import org.onosproject.net.host.HostEvent;
53import org.onosproject.net.host.HostListener;
54import org.onosproject.net.host.HostProvider;
55import org.onosproject.net.host.HostProviderRegistry;
56import org.onosproject.net.host.HostProviderService;
57import org.onosproject.net.host.HostService;
58import org.onosproject.net.packet.PacketContext;
59import org.onosproject.net.packet.PacketProcessor;
60import org.onosproject.net.packet.PacketService;
61import org.onosproject.net.provider.AbstractProvider;
62import org.onosproject.net.provider.ProviderId;
63import org.onosproject.xosclient.api.OpenStackAccess;
64import org.onosproject.xosclient.api.VtnPort;
65import org.onosproject.xosclient.api.VtnPortApi;
66import org.onosproject.xosclient.api.VtnService;
67import org.onosproject.xosclient.api.VtnServiceApi;
68import org.onosproject.xosclient.api.VtnServiceId;
69import org.onosproject.xosclient.api.XosAccess;
70import org.onosproject.xosclient.api.XosClientService;
71import org.slf4j.Logger;
72
Hyunsun Moon86e25e82016-05-18 21:28:06 -070073import java.util.Date;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070074import java.util.Map;
75import java.util.Objects;
76import java.util.Set;
77import java.util.concurrent.ExecutorService;
78import java.util.stream.Collectors;
79import java.util.stream.StreamSupport;
80
81import static com.google.common.base.Preconditions.checkNotNull;
82import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
83import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon86e25e82016-05-18 21:28:06 -070084import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
Hyunsun Moon60a10672016-06-12 17:39:12 -070085import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.MANAGEMENT;
86import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.PRIVATE;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070087import static org.slf4j.LoggerFactory.getLogger;
88
89/**
90 * Adds or removes instances to network services.
91 */
92@Component(immediate = true)
93@Service(value = CordVtnInstanceManager.class)
94public class CordVtnInstanceManager extends AbstractProvider implements HostProvider {
95
96 protected final Logger log = getLogger(getClass());
97
98 private static final String XOS_ACCESS_ERROR = "XOS access is not configured";
99 private static final String OPENSTACK_ACCESS_ERROR = "OpenStack access is not configured";
100 private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
Hyunsun Moon86e25e82016-05-18 21:28:06 -0700101 private static final int DHCP_INFINITE_LEASE = -1;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected CoreService coreService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected NetworkConfigRegistry configRegistry;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected HostProviderRegistry hostProviderRegistry;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected DeviceService deviceService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected HostService hostService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected PacketService packetService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected DhcpService dhcpService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected MastershipService mastershipService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected XosClientService xosClient;
129
130 private final ConfigFactory configFactory =
131 new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
132 @Override
133 public CordVtnConfig createConfig() {
134 return new CordVtnConfig();
135 }
136 };
137
138 private final ExecutorService eventExecutor =
139 newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-instance", "event-handler"));
140 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
141 private final HostListener hostListener = new InternalHostListener();
142 private final NetworkConfigListener configListener = new InternalConfigListener();
143
144 private ApplicationId appId;
145 private HostProviderService hostProvider;
146 private CordVtnArpProxy arpProxy; // TODO make it a component service
147 private MacAddress privateGatewayMac = MacAddress.NONE;
148 private XosAccess xosAccess = null;
149 private OpenStackAccess osAccess = null;
150
151 /**
152 * Creates an cordvtn host location provider.
153 */
154 public CordVtnInstanceManager() {
155 super(new ProviderId("host", CordVtnService.CORDVTN_APP_ID));
156 }
157
158 @Activate
159 protected void activate() {
160 appId = coreService.registerApplication(CordVtnService.CORDVTN_APP_ID);
161
162 arpProxy = new CordVtnArpProxy(appId, packetService, hostService);
163 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
164 arpProxy.requestPacket();
165
166 hostService.addListener(hostListener);
167 hostProvider = hostProviderRegistry.register(this);
168
169 configRegistry.registerConfigFactory(configFactory);
170 configRegistry.addListener(configListener);
171
172 log.info("Started");
173 }
174
175 @Deactivate
176 protected void deactivate() {
177 hostProviderRegistry.unregister(this);
178 hostService.removeListener(hostListener);
179
180 packetService.removeProcessor(packetProcessor);
181
182 configRegistry.unregisterConfigFactory(configFactory);
183 configRegistry.removeListener(configListener);
184
185 eventExecutor.shutdown();
186 log.info("Stopped");
187 }
188
189 @Override
190 public void triggerProbe(Host host) {
191 /*
192 * Note: In CORD deployment, we assume that all hosts are configured.
193 * Therefore no probe is required.
194 */
195 }
196
197 /**
198 * Adds a service instance at a given connect point.
199 *
200 * @param connectPoint connect point of the instance
201 */
202 public void addInstance(ConnectPoint connectPoint) {
203 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
204 if (port == null) {
205 log.debug("No port found from {}", connectPoint);
206 return;
207 }
208
209 VtnPort vtnPort = getVtnPort(port.annotations().value("portName"));
210 if (vtnPort == null) {
211 return;
212 }
213
214 VtnService vtnService = getVtnService(vtnPort.serviceId());
215 if (vtnService == null) {
216 return;
217 }
218
219 // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
220 // existing instances.
221 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
alshabibb4d31712016-06-01 18:51:03 -0700222 .set(Instance.SERVICE_TYPE, vtnService.serviceType().toString())
223 .set(Instance.SERVICE_ID, vtnPort.serviceId().id())
224 .set(Instance.PORT_ID, vtnPort.id().id())
225 .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700226
227 HostDescription hostDesc = new DefaultHostDescription(
228 vtnPort.mac(),
229 VlanId.NONE,
230 new HostLocation(connectPoint, System.currentTimeMillis()),
231 Sets.newHashSet(vtnPort.ip()),
232 annotations.build());
233
234 HostId hostId = HostId.hostId(vtnPort.mac());
235 hostProvider.hostDetected(hostId, hostDesc, false);
236 }
237
238 /**
239 * Adds a service instance with given host ID and host description.
240 *
241 * @param hostId host id
242 * @param description host description
243 */
244 public void addInstance(HostId hostId, HostDescription description) {
245 hostProvider.hostDetected(hostId, description, false);
246 }
247
248 /**
249 * Removes a service instance from a given connect point.
250 *
251 * @param connectPoint connect point
252 */
253 public void removeInstance(ConnectPoint connectPoint) {
254 hostService.getConnectedHosts(connectPoint)
255 .stream()
256 .forEach(host -> hostProvider.hostVanished(host.id()));
257 }
258
259 /**
260 * Removes service instance with given host ID.
261 *
262 * @param hostId host id
263 */
264 public void removeInstance(HostId hostId) {
265 hostProvider.hostVanished(hostId);
266 }
267
268 private void instanceDetected(Instance instance) {
269 VtnService service = getVtnService(instance.serviceId());
270 if (service == null) {
271 return;
272 }
273
274 if (service.networkType().equals(PRIVATE)) {
275 arpProxy.addGateway(service.serviceIp(), privateGatewayMac);
276 arpProxy.sendGratuitousArpForGateway(service.serviceIp(), Sets.newHashSet(instance));
277 }
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700278 if (!instance.isNestedInstance()) {
279 registerDhcpLease(instance, service);
280 }
281 }
282
283 private void instanceRemoved(Instance instance) {
284 VtnService service = getVtnService(instance.serviceId());
285 if (service == null) {
286 return;
287 }
288
289 if (service.networkType().equals(PRIVATE) && getInstances(service.id()).isEmpty()) {
290 arpProxy.removeGateway(service.serviceIp());
291 }
292
293 if (!instance.isNestedInstance()) {
294 dhcpService.removeStaticMapping(instance.mac());
295 }
296 }
297
298 private void registerDhcpLease(Instance instance, VtnService service) {
Hyunsun Moon86e25e82016-05-18 21:28:06 -0700299 Ip4Address broadcast = Ip4Address.makeMaskedAddress(
300 instance.ipAddress(),
301 service.subnet().prefixLength());
302
303 IpAssignment.Builder ipBuilder = IpAssignment.builder()
304 .ipAddress(instance.ipAddress())
305 .leasePeriod(DHCP_INFINITE_LEASE)
306 .timestamp(new Date())
307 .subnetMask(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()))
308 .broadcast(broadcast)
309 .domainServer(DEFAULT_DNS)
310 .assignmentStatus(Option_RangeNotEnforced);
311
312 if (service.networkType() != MANAGEMENT) {
313 ipBuilder = ipBuilder.routerAddress(service.serviceIp().getIp4Address());
314 }
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700315
316 log.debug("Set static DHCP mapping for {} {}", instance.mac(), instance.ipAddress());
Hyunsun Moon86e25e82016-05-18 21:28:06 -0700317 dhcpService.setStaticMapping(instance.mac(), ipBuilder.build());
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700318 }
319
320 private VtnService getVtnService(VtnServiceId serviceId) {
321 checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
322 checkNotNull(xosAccess, XOS_ACCESS_ERROR);
323
324 // TODO remove openstack access when XOS provides all information
325 VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
326 VtnService service = serviceApi.service(serviceId, osAccess);
327 if (service == null) {
328 log.warn("Failed to get VtnService for {}", serviceId);
329 }
330 return service;
331 }
332
333 private VtnPort getVtnPort(String portName) {
334 checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
335 checkNotNull(xosAccess, XOS_ACCESS_ERROR);
336
337 // TODO remove openstack access when XOS provides all information
338 VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
339 VtnPort vtnPort = portApi.vtnPort(portName, osAccess);
340 if (vtnPort == null) {
341 log.warn("Failed to get port information of {}", portName);
342 }
343 return vtnPort;
344 }
345
346 private Set<Instance> getInstances(VtnServiceId serviceId) {
347 return StreamSupport.stream(hostService.getHosts().spliterator(), false)
348 .filter(host -> Objects.equals(
349 serviceId.id(),
350 host.annotations().value(Instance.SERVICE_ID)))
351 .map(Instance::of)
352 .collect(Collectors.toSet());
353 }
354
355 private void readConfiguration() {
356 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
357 if (config == null) {
358 log.debug("No configuration found");
359 return;
360 }
361
362 log.info("Load CORD-VTN configurations");
363
364 xosAccess = config.xosAccess();
365 osAccess = config.openstackAccess();
366 privateGatewayMac = config.privateGatewayMac();
367
368 Map<IpAddress, MacAddress> publicGateways = config.publicGateways();
369 publicGateways.entrySet()
370 .stream()
371 .forEach(entry -> {
372 arpProxy.addGateway(entry.getKey(), entry.getValue());
373 log.debug("Added public gateway IP {}, MAC {}",
374 entry.getKey(), entry.getValue());
375 });
376 // TODO notice gateway MAC change to VMs holds this gateway IP
377 }
378
379 private class InternalHostListener implements HostListener {
380
381 @Override
382 public void event(HostEvent event) {
383 Host host = event.subject();
384 if (!mastershipService.isLocalMaster(host.location().deviceId())) {
385 // do not allow to proceed without mastership
386 return;
387 }
388
389 Instance instance = Instance.of(host);
390 switch (event.type()) {
391 case HOST_UPDATED:
392 case HOST_ADDED:
393 eventExecutor.execute(() -> instanceDetected(instance));
394 break;
395 case HOST_REMOVED:
396 eventExecutor.execute(() -> instanceRemoved(instance));
397 break;
398 default:
399 break;
400 }
401 }
402 }
403
404 private class InternalPacketProcessor implements PacketProcessor {
405
406 @Override
407 public void process(PacketContext context) {
408 if (context.isHandled()) {
409 return;
410 }
411 Ethernet ethPacket = context.inPacket().parsed();
412 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
413 return;
414 }
415 arpProxy.processArpPacket(context, ethPacket);
416 }
417 }
418
419 private class InternalConfigListener implements NetworkConfigListener {
420
421 @Override
422 public void event(NetworkConfigEvent event) {
423 if (!event.configClass().equals(CordVtnConfig.class)) {
424 return;
425 }
426
427 switch (event.type()) {
428 case CONFIG_ADDED:
429 case CONFIG_UPDATED:
430 readConfiguration();
431 break;
432 default:
433 break;
434 }
435 }
436 }
437}