blob: efa0e4a2c5d1399165b732f76c15acf8ab927aa4 [file] [log] [blame]
Hyunsun Moone9d75992015-09-15 22:39:16 -07001/*
Brian O'Connor8e57fd52016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Hyunsun Moone9d75992015-09-15 22:39:16 -07003 *
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 */
Hyunsun Moonf9480202016-04-14 16:13:42 -070016package org.onosproject.cordvtn.impl;
Hyunsun Moone9d75992015-09-15 22:39:16 -070017
Hyunsun Moon8b530e32016-02-03 00:11:11 -080018import com.google.common.collect.Lists;
Hyunsun Moonb5f92e52016-02-17 15:02:06 -080019import com.google.common.collect.Maps;
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070020import com.google.common.collect.Sets;
Hyunsun Moone9d75992015-09-15 22:39:16 -070021import 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;
Hyunsun Moon022272f2016-01-11 15:30:42 -080027import org.onlab.packet.Ethernet;
Hyunsun Moon8b530e32016-02-03 00:11:11 -080028import org.onlab.packet.Ip4Address;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070029import org.onlab.packet.Ip4Prefix;
Hyunsun Moonf9480202016-04-14 16:13:42 -070030import org.onosproject.cordvtn.api.CordVtnNode;
31import org.onosproject.cordvtn.api.CordVtnService;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070032import org.onosproject.cordvtn.api.Instance;
33import org.onosproject.core.DefaultGroupId;
34import org.onosproject.core.GroupId;
35import org.onosproject.net.DeviceId;
Hyunsun Moone9d75992015-09-15 22:39:16 -070036import org.onosproject.net.Host;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070037import org.onosproject.net.PortNumber;
38import org.onosproject.net.flow.DefaultFlowRule;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.FlowRule;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.flow.instructions.ExtensionTreatment;
45import org.onosproject.net.group.DefaultGroupDescription;
46import org.onosproject.net.group.DefaultGroupKey;
47import org.onosproject.net.group.Group;
48import org.onosproject.net.group.GroupBucket;
49import org.onosproject.net.group.GroupBuckets;
50import org.onosproject.net.group.GroupDescription;
51import org.onosproject.net.group.GroupKey;
Hyunsun Moonba290072015-12-16 20:53:23 -080052import org.onosproject.net.group.GroupService;
Hyunsun Moone9d75992015-09-15 22:39:16 -070053import org.onosproject.net.host.HostEvent;
54import org.onosproject.net.host.HostListener;
Hyunsun Moon486ed1b2016-05-13 18:58:35 -070055import org.onosproject.xosclient.api.VtnService;
Hyunsun Moon5f51f622016-05-13 04:17:53 -070056import org.onosproject.xosclient.api.VtnServiceId;
Hyunsun Moone9d75992015-09-15 22:39:16 -070057import org.slf4j.Logger;
58
Hyunsun Moon8b530e32016-02-03 00:11:11 -080059import java.util.List;
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070060import java.util.Map;
Hyunsun Moond05b32e2016-03-02 19:27:26 -080061import java.util.Objects;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080062import java.util.Set;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080063import java.util.stream.Collectors;
Hyunsun Moone9d75992015-09-15 22:39:16 -070064
Hyunsun Moon3fc17f72016-01-24 21:47:06 -080065import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
Hyunsun Moone9d75992015-09-15 22:39:16 -070066import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070067import static org.onosproject.cordvtn.impl.CordVtnPipeline.*;
68import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
Hyunsun Moone9d75992015-09-15 22:39:16 -070069import static org.slf4j.LoggerFactory.getLogger;
70
71/**
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070072 * Provisions service dependency capabilities between network services.
Hyunsun Moone9d75992015-09-15 22:39:16 -070073 */
74@Component(immediate = true)
75@Service
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070076public class CordVtn extends CordVtnInstanceHandler implements CordVtnService {
Hyunsun Moone9d75992015-09-15 22:39:16 -070077
78 protected final Logger log = getLogger(getClass());
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moonba290072015-12-16 20:53:23 -080081 protected GroupService groupService;
82
Hyunsun Moone9d75992015-09-15 22:39:16 -070083 @Activate
84 protected void activate() {
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070085 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn", "event-handler"));
86 hostListener = new InternalHostListener();
87 super.activate();
Hyunsun Moone9d75992015-09-15 22:39:16 -070088 }
89
90 @Deactivate
91 protected void deactivate() {
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070092 super.deactivate();
Hyunsun Moonde372572016-01-14 03:42:47 -080093 }
94
95 @Override
Hyunsun Moon486ed1b2016-05-13 18:58:35 -070096 public void createServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId,
Hyunsun Moon5f7ed8a2016-02-10 17:02:37 -080097 boolean isBidirectional) {
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070098 VtnService tService = getVtnService(tServiceId);
99 VtnService pService = getVtnService(pServiceId);
Hyunsun Moon97eaf502015-12-07 14:06:28 -0800100
Hyunsun Moonba290072015-12-16 20:53:23 -0800101 if (tService == null || pService == null) {
Hyunsun Moon486ed1b2016-05-13 18:58:35 -0700102 log.error("Failed to create dependency between {} and {}",
103 tServiceId, pServiceId);
Hyunsun Moonba290072015-12-16 20:53:23 -0800104 return;
105 }
106
Hyunsun Moon486ed1b2016-05-13 18:58:35 -0700107 log.info("Created dependency between {} and {}", tService.name(), pService.name());
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700108 serviceDependencyRules(tService, pService, isBidirectional, true);
Hyunsun Moon9274aaf2015-12-04 11:35:25 -0800109 }
110
111 @Override
Hyunsun Moon486ed1b2016-05-13 18:58:35 -0700112 public void removeServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId) {
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700113 VtnService tService = getVtnService(tServiceId);
114 VtnService pService = getVtnService(pServiceId);
Hyunsun Moon97eaf502015-12-07 14:06:28 -0800115
Hyunsun Moonba290072015-12-16 20:53:23 -0800116 if (tService == null || pService == null) {
Hyunsun Moon486ed1b2016-05-13 18:58:35 -0700117 log.error("Failed to remove dependency between {} and {}",
118 tServiceId, pServiceId);
Hyunsun Moonba290072015-12-16 20:53:23 -0800119 return;
120 }
121
Hyunsun Moon486ed1b2016-05-13 18:58:35 -0700122 log.info("Removed dependency between {} and {}", tService.name(), pService.name());
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700123 serviceDependencyRules(tService, pService, true, false);
Hyunsun Moon9274aaf2015-12-04 11:35:25 -0800124 }
125
Hyunsun Mooncb799442016-01-15 20:03:18 -0800126 @Override
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700127 public void instanceDetected(Instance instance) {
128 VtnService service = getVtnService(instance.serviceId());
129 if (service == null) {
Hyunsun Moon4edb0172015-11-07 22:08:43 -0800130 return;
131 }
132
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700133 // TODO get bidirectional information from XOS once XOS supports
134 service.tenantServices().stream().forEach(
135 tServiceId -> createServiceDependency(tServiceId, service.id(), true));
136 service.providerServices().stream().forEach(
137 pServiceId -> createServiceDependency(service.id(), pServiceId, true));
Hyunsun Moon01556a52016-02-12 12:48:47 -0800138
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700139 updateProviderServiceInstances(service);
Hyunsun Moon4edb0172015-11-07 22:08:43 -0800140 }
141
Hyunsun Mooncb799442016-01-15 20:03:18 -0800142 @Override
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700143 public void instanceRemoved(Instance instance) {
144 VtnService service = getVtnService(instance.serviceId());
145 if (service == null) {
Hyunsun Moon01556a52016-02-12 12:48:47 -0800146 return;
147 }
148
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700149 if (!service.providerServices().isEmpty()) {
150 removeInstanceFromTenantService(instance, service);
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800151 }
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700152 if (!service.tenantServices().isEmpty()) {
153 updateProviderServiceInstances(service);
Hyunsun Moon01556a52016-02-12 12:48:47 -0800154 }
155 }
156
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700157 private void updateProviderServiceInstances(VtnService service) {
158 GroupKey groupKey = getGroupKey(service.id());
159
160 Set<DeviceId> devices = nodeManager.completeNodes().stream()
161 .map(CordVtnNode::intBrId)
Hyunsun Mooncb799442016-01-15 20:03:18 -0800162 .collect(Collectors.toSet());
Hyunsun Moon01556a52016-02-12 12:48:47 -0800163
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700164 for (DeviceId deviceId : devices) {
165 Group group = groupService.getGroup(deviceId, groupKey);
166 if (group == null) {
167 log.trace("No group exists for service {} in {}", service.id(), deviceId);
168 continue;
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800169 }
170
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700171 List<GroupBucket> oldBuckets = group.buckets().buckets();
172 List<GroupBucket> newBuckets = getServiceGroupBuckets(
173 deviceId, service.vni(), getInstances(service.id())).buckets();
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800174
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700175 if (oldBuckets.equals(newBuckets)) {
176 continue;
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800177 }
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700178
179 List<GroupBucket> bucketsToRemove = Lists.newArrayList(oldBuckets);
180 bucketsToRemove.removeAll(newBuckets);
181 if (!bucketsToRemove.isEmpty()) {
182 groupService.removeBucketsFromGroup(
183 deviceId,
184 groupKey,
185 new GroupBuckets(bucketsToRemove),
186 groupKey, appId);
187 }
188
189 List<GroupBucket> bucketsToAdd = Lists.newArrayList(newBuckets);
190 bucketsToAdd.removeAll(oldBuckets);
191 if (!bucketsToAdd.isEmpty()) {
192 groupService.addBucketsToGroup(
193 deviceId,
194 groupKey,
195 new GroupBuckets(bucketsToAdd),
196 groupKey, appId);
197 }
198 }
199 }
200
201 private void removeInstanceFromTenantService(Instance instance, VtnService service) {
202 service.providerServices().stream().forEach(pServiceId -> {
203 Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
204 Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
205
206 inPorts.put(instance.deviceId(), Sets.newHashSet(instance.portNumber()));
207 outGroups.put(instance.deviceId(), getGroupId(pServiceId, instance.deviceId()));
208
209 inServiceRule(inPorts, outGroups, false);
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800210 });
211 }
212
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700213 private void serviceDependencyRules(VtnService tService, VtnService pService,
214 boolean isBidirectional, boolean install) {
215 Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
216 Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
217
218 nodeManager.completeNodes().stream().forEach(node -> {
219 DeviceId deviceId = node.intBrId();
220 GroupId groupId = createServiceGroup(deviceId, pService);
221 outGroups.put(deviceId, groupId);
222
223 Set<PortNumber> tServiceInstances = getInstances(tService.id())
224 .stream()
225 .filter(instance -> instance.deviceId().equals(deviceId))
226 .map(Instance::portNumber)
227 .collect(Collectors.toSet());
228 inPorts.put(deviceId, tServiceInstances);
229 });
230
231 Ip4Prefix srcRange = tService.subnet().getIp4Prefix();
232 Ip4Prefix dstRange = pService.subnet().getIp4Prefix();
233
234 indirectAccessRule(srcRange, pService.serviceIp().getIp4Address(), outGroups, install);
235 directAccessRule(srcRange, dstRange, install);
236 if (isBidirectional) {
237 directAccessRule(dstRange, srcRange, install);
238 }
239 inServiceRule(inPorts, outGroups, install);
Hyunsun Moonb5f92e52016-02-17 15:02:06 -0800240 }
241
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700242 private void indirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
243 Map<DeviceId, GroupId> outGroups, boolean install) {
244 TrafficSelector selector = DefaultTrafficSelector.builder()
245 .matchEthType(Ethernet.TYPE_IPV4)
246 .matchIPSrc(srcRange)
247 .matchIPDst(serviceIp.toIpPrefix())
248 .build();
249
250 for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
251 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
252 .group(outGroup.getValue())
253 .build();
254
255 FlowRule flowRule = DefaultFlowRule.builder()
256 .fromApp(appId)
257 .withSelector(selector)
258 .withTreatment(treatment)
259 .withPriority(PRIORITY_HIGH)
260 .forDevice(outGroup.getKey())
261 .forTable(TABLE_ACCESS_TYPE)
262 .makePermanent()
263 .build();
264
265 pipeline.processFlowRule(install, flowRule);
266 }
267 }
268
269 private void directAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange, boolean install) {
270 TrafficSelector selector = DefaultTrafficSelector.builder()
271 .matchEthType(Ethernet.TYPE_IPV4)
272 .matchIPSrc(srcRange)
273 .matchIPDst(dstRange)
274 .build();
275
276 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
277 .transition(TABLE_DST_IP)
278 .build();
279
280 nodeManager.completeNodes().stream().forEach(node -> {
281 DeviceId deviceId = node.intBrId();
282 FlowRule flowRuleDirect = DefaultFlowRule.builder()
283 .fromApp(appId)
284 .withSelector(selector)
285 .withTreatment(treatment)
286 .withPriority(PRIORITY_DEFAULT)
287 .forDevice(deviceId)
288 .forTable(TABLE_ACCESS_TYPE)
289 .makePermanent()
290 .build();
291
292 pipeline.processFlowRule(install, flowRuleDirect);
293 });
294 }
295
296 private void inServiceRule(Map<DeviceId, Set<PortNumber>> inPorts,
297 Map<DeviceId, GroupId> outGroups, boolean install) {
298 for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
299 Set<PortNumber> ports = entry.getValue();
300 DeviceId deviceId = entry.getKey();
301
302 GroupId groupId = outGroups.get(deviceId);
303 if (groupId == null) {
304 continue;
305 }
306
307 ports.stream().forEach(port -> {
308 TrafficSelector selector = DefaultTrafficSelector.builder()
309 .matchInPort(port)
310 .build();
311
312 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
313 .group(groupId)
314 .build();
315
316 FlowRule flowRule = DefaultFlowRule.builder()
317 .fromApp(appId)
318 .withSelector(selector)
319 .withTreatment(treatment)
320 .withPriority(PRIORITY_DEFAULT)
321 .forDevice(deviceId)
322 .forTable(TABLE_IN_SERVICE)
323 .makePermanent()
324 .build();
325
326 pipeline.processFlowRule(install, flowRule);
327 });
328 }
329 }
330
331 private GroupId getGroupId(VtnServiceId serviceId, DeviceId deviceId) {
332 return new DefaultGroupId(Objects.hash(serviceId, deviceId));
333 }
334
335 private GroupKey getGroupKey(VtnServiceId serviceId) {
336 return new DefaultGroupKey(serviceId.id().getBytes());
337 }
338
339 private GroupId createServiceGroup(DeviceId deviceId, VtnService service) {
340 GroupKey groupKey = getGroupKey(service.id());
341 Group group = groupService.getGroup(deviceId, groupKey);
342 GroupId groupId = getGroupId(service.id(), deviceId);
343
344 if (group != null) {
345 log.debug("Group {} is already exist in {}", service.id(), deviceId);
346 return groupId;
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800347 }
348
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700349 GroupBuckets buckets = getServiceGroupBuckets(
350 deviceId, service.vni(), getInstances(service.id()));
351 GroupDescription groupDescription = new DefaultGroupDescription(
352 deviceId,
353 GroupDescription.Type.SELECT,
354 buckets,
355 groupKey,
356 groupId.id(),
357 appId);
Hyunsun Moon486ed1b2016-05-13 18:58:35 -0700358
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700359 groupService.addGroup(groupDescription);
360 return groupId;
361 }
362
363 private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId,
364 Set<Instance> instances) {
365 List<GroupBucket> buckets = Lists.newArrayList();
366 instances.stream().forEach(instance -> {
367 Ip4Address tunnelIp = nodeManager.dpIp(instance.deviceId()).getIp4Address();
368 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
369
370 if (deviceId.equals(instance.deviceId())) {
371 tBuilder.setEthDst(instance.mac())
372 .setOutput(instance.portNumber());
373 } else {
374 ExtensionTreatment tunnelDst =
375 pipeline.tunnelDstTreatment(deviceId, tunnelIp);
376 tBuilder.setEthDst(instance.mac())
377 .extension(tunnelDst, deviceId)
378 .setTunnelId(tunnelId)
379 .setOutput(nodeManager.tunnelPort(instance.deviceId()));
380 }
381 buckets.add(createSelectGroupBucket(tBuilder.build()));
382 });
383 return new GroupBuckets(buckets);
Hyunsun Moon61e79ee2016-04-14 19:04:23 -0700384 }
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800385
Hyunsun Moone9d75992015-09-15 22:39:16 -0700386 private class InternalHostListener implements HostListener {
387
388 @Override
389 public void event(HostEvent event) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800390 Host host = event.subject();
Hyunsun Moone58a4ad2016-03-08 21:59:13 -0800391 if (!mastershipService.isLocalMaster(host.location().deviceId())) {
392 // do not allow to proceed without mastership
393 return;
394 }
Hyunsun Moone9d75992015-09-15 22:39:16 -0700395
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700396 Instance instance = Instance.of(host);
Hyunsun Moone9d75992015-09-15 22:39:16 -0700397 switch (event.type()) {
Hyunsun Moone58a4ad2016-03-08 21:59:13 -0800398 case HOST_UPDATED:
Hyunsun Moone9d75992015-09-15 22:39:16 -0700399 case HOST_ADDED:
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700400 eventExecutor.execute(() -> instanceDetected(instance));
Hyunsun Moone9d75992015-09-15 22:39:16 -0700401 break;
402 case HOST_REMOVED:
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700403 eventExecutor.execute(() -> instanceRemoved(instance));
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800404 break;
405 default:
406 break;
407 }
Hyunsun Moon022272f2016-01-11 15:30:42 -0800408 }
409 }
Hyunsun Moone9d75992015-09-15 22:39:16 -0700410}