blob: 039cd1261ef7f91c555d9a6cec26b6fea197b5dd [file] [log] [blame]
Luca Prete9c2ee072016-02-16 11:00:44 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Luca Prete9c2ee072016-02-16 11:00:44 -08003 *
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.vpls;
17
nosignal5fd282e2016-09-16 16:11:40 -070018import com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Lists;
Luca Prete9c2ee072016-02-16 11:00:44 -080020import com.google.common.collect.SetMultimap;
nosignal5fd282e2016-09-16 16:11:40 -070021import com.google.common.collect.Sets;
Luca Prete9c2ee072016-02-16 11:00:44 -080022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.MacAddress;
28import org.onlab.packet.VlanId;
29import org.onosproject.app.ApplicationService;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
nosignal5fd282e2016-09-16 16:11:40 -070032import org.onosproject.incubator.net.intf.Interface;
Luca Pretea8854822016-04-26 16:30:55 -070033import org.onosproject.incubator.net.intf.InterfaceEvent;
34import org.onosproject.incubator.net.intf.InterfaceListener;
Luca Prete9c2ee072016-02-16 11:00:44 -080035import org.onosproject.incubator.net.intf.InterfaceService;
Luca Prete092e8952016-10-26 16:25:56 +020036import org.onosproject.net.EncapsulationType;
nosignal5fd282e2016-09-16 16:11:40 -070037import org.onosproject.net.FilteredConnectPoint;
Luca Prete9c2ee072016-02-16 11:00:44 -080038import org.onosproject.net.Host;
nosignal5fd282e2016-09-16 16:11:40 -070039import org.onosproject.net.config.NetworkConfigEvent;
40import org.onosproject.net.config.NetworkConfigListener;
41import org.onosproject.net.config.NetworkConfigService;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.criteria.Criterion;
45import org.onosproject.net.flow.criteria.VlanIdCriterion;
Luca Prete9c2ee072016-02-16 11:00:44 -080046import org.onosproject.net.host.HostEvent;
47import org.onosproject.net.host.HostListener;
48import org.onosproject.net.host.HostService;
nosignal5fd282e2016-09-16 16:11:40 -070049import org.onosproject.net.intent.Intent;
Luca Prete9c2ee072016-02-16 11:00:44 -080050import org.onosproject.net.intent.IntentService;
nosignal5fd282e2016-09-16 16:11:40 -070051import org.onosproject.net.intent.Key;
Luca Prete9c2ee072016-02-16 11:00:44 -080052import org.onosproject.routing.IntentSynchronizationService;
nosignal5fd282e2016-09-16 16:11:40 -070053import org.onosproject.vpls.config.VplsConfigurationService;
Luca Prete9c2ee072016-02-16 11:00:44 -080054import org.slf4j.Logger;
55
nosignal5fd282e2016-09-16 16:11:40 -070056import java.util.Collection;
57import java.util.HashSet;
58import java.util.List;
Luca Prete9c2ee072016-02-16 11:00:44 -080059import java.util.Set;
nosignal5fd282e2016-09-16 16:11:40 -070060import java.util.stream.Collectors;
Luca Prete9c2ee072016-02-16 11:00:44 -080061
62import static org.slf4j.LoggerFactory.getLogger;
nosignal5fd282e2016-09-16 16:11:40 -070063import static org.onosproject.vpls.IntentInstaller.PREFIX_BROADCAST;
64import static org.onosproject.vpls.IntentInstaller.PREFIX_UNICAST;
Luca Prete9c2ee072016-02-16 11:00:44 -080065
66/**
Luca Prete092e8952016-10-26 16:25:56 +020067 * Application to create L2 broadcast overlay networks using VLANs.
Luca Prete9c2ee072016-02-16 11:00:44 -080068 */
69@Component(immediate = true)
70public class Vpls {
nosignal5fd282e2016-09-16 16:11:40 -070071 static final String VPLS_APP = "org.onosproject.vpls";
72
73 private static final String HOST_FCP_NOT_FOUND =
74 "Filtered connected point for host {} not found";
75 private static final String HOST_EVENT = "Received HostEvent {}";
76 private static final String INTF_CONF_EVENT =
77 "Received InterfaceConfigEvent {}";
78 private static final String NET_CONF_EVENT =
79 "Received NetworkConfigEvent {}";
Yi Tseng28767f02016-09-13 04:27:20 -070080
Luca Prete9c2ee072016-02-16 11:00:44 -080081 private final Logger log = getLogger(getClass());
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected ApplicationService applicationService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected CoreService coreService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected HostService hostService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected IntentService intentService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected InterfaceService interfaceService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected IntentSynchronizationService intentSynchronizer;
100
nosignal5fd282e2016-09-16 16:11:40 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected NetworkConfigService configService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected VplsConfigurationService vplsConfigService;
106
Luca Prete9c2ee072016-02-16 11:00:44 -0800107 private final HostListener hostListener = new InternalHostListener();
108
Luca Prete092e8952016-10-26 16:25:56 +0200109 private final InternalInterfaceListener interfaceListener =
110 new InternalInterfaceListener();
Luca Pretea8854822016-04-26 16:30:55 -0700111
nosignal5fd282e2016-09-16 16:11:40 -0700112 private final InternalNetworkConfigListener configListener =
113 new InternalNetworkConfigListener();
114
Luca Prete9c2ee072016-02-16 11:00:44 -0800115 private IntentInstaller intentInstaller;
116
117 private ApplicationId appId;
118
Yi Tseng28767f02016-09-13 04:27:20 -0700119
Luca Prete9c2ee072016-02-16 11:00:44 -0800120 @Activate
121 public void activate() {
122 appId = coreService.registerApplication(VPLS_APP);
123
124 intentInstaller = new IntentInstaller(appId,
125 intentService,
126 intentSynchronizer);
127
Luca Prete2705d662016-04-29 15:30:23 -0700128 applicationService.registerDeactivateHook(appId, () -> {
129 intentSynchronizer.removeIntentsByAppId(appId);
130 });
Luca Prete9c2ee072016-02-16 11:00:44 -0800131
132 hostService.addListener(hostListener);
Luca Pretea8854822016-04-26 16:30:55 -0700133 interfaceService.addListener(interfaceListener);
nosignal5fd282e2016-09-16 16:11:40 -0700134 configService.addListener(configListener);
Luca Prete9c2ee072016-02-16 11:00:44 -0800135
nosignal5fd282e2016-09-16 16:11:40 -0700136 setupConnectivity(false);
Luca Prete9c2ee072016-02-16 11:00:44 -0800137
Luca Pretec21c6e62016-09-22 14:52:21 -0700138 log.info("Activated");
Luca Prete9c2ee072016-02-16 11:00:44 -0800139 }
140
141 @Deactivate
142 public void deactivate() {
nosignal5fd282e2016-09-16 16:11:40 -0700143 configService.removeListener(configListener);
Yi Tseng28767f02016-09-13 04:27:20 -0700144 intentSynchronizer.removeIntentsByAppId(appId);
Luca Pretec21c6e62016-09-22 14:52:21 -0700145 log.info("Deactivated");
Luca Prete9c2ee072016-02-16 11:00:44 -0800146 }
147
nosignal5fd282e2016-09-16 16:11:40 -0700148 /**
149 * Sets up connectivity for all VPLSs.
150 *
151 * @param isNetworkConfigEvent true if this function is triggered
152 * by NetworkConfigEvent; false otherwise
153 */
154 private void setupConnectivity(boolean isNetworkConfigEvent) {
155 SetMultimap<String, Interface> networkInterfaces =
Luca Prete092e8952016-10-26 16:25:56 +0200156 vplsConfigService.ifacesByVplsName();
Luca Prete9c2ee072016-02-16 11:00:44 -0800157
nosignal5fd282e2016-09-16 16:11:40 -0700158 Set<String> vplsAffectedByApi =
Luca Prete092e8952016-10-26 16:25:56 +0200159 new HashSet<>(vplsConfigService.vplsAffectedByApi());
Luca Prete9c2ee072016-02-16 11:00:44 -0800160
nosignal5fd282e2016-09-16 16:11:40 -0700161 if (isNetworkConfigEvent && vplsAffectedByApi.isEmpty()) {
Luca Prete092e8952016-10-26 16:25:56 +0200162 vplsAffectedByApi.addAll(vplsConfigService.vplsNamesOld());
nosignal5fd282e2016-09-16 16:11:40 -0700163 }
Yi Tseng28767f02016-09-13 04:27:20 -0700164
Luca Prete092e8952016-10-26 16:25:56 +0200165 networkInterfaces.asMap().forEach((vplsName, interfaces) -> {
nosignal5fd282e2016-09-16 16:11:40 -0700166 Set<Host> hosts = Sets.newHashSet();
167 interfaces.forEach(intf -> {
168 // Add hosts that belongs to the specific VPLS
169 hostService.getConnectedHosts(intf.connectPoint())
170 .stream()
171 .filter(host -> host.vlan().equals(intf.vlan()))
172 .forEach(hosts::add);
173 });
174
Luca Prete092e8952016-10-26 16:25:56 +0200175 EncapsulationType encap =
176 vplsConfigService.encap(vplsName);
177
178 setupConnectivity(vplsName, interfaces, hosts, encap,
179 vplsAffectedByApi.contains(vplsName));
180 vplsAffectedByApi.remove(vplsName);
nosignal5fd282e2016-09-16 16:11:40 -0700181 });
182
183 if (!vplsAffectedByApi.isEmpty()) {
Luca Prete092e8952016-10-26 16:25:56 +0200184 for (String vplsName : vplsAffectedByApi) {
185 withdrawIntents(vplsName, Lists.newArrayList());
nosignal5fd282e2016-09-16 16:11:40 -0700186 }
187 }
Luca Prete9c2ee072016-02-16 11:00:44 -0800188 }
189
190 /**
nosignal5fd282e2016-09-16 16:11:40 -0700191 * Sets up connectivity for specific VPLS.
Luca Prete9c2ee072016-02-16 11:00:44 -0800192 *
Luca Prete092e8952016-10-26 16:25:56 +0200193 * @param vplsName the VPLS name
nosignal5fd282e2016-09-16 16:11:40 -0700194 * @param interfaces the interfaces that belong to the VPLS
195 * @param hosts the hosts that belong to the VPLS
Luca Prete092e8952016-10-26 16:25:56 +0200196 * @param encap the encapsulation type
nosignal5fd282e2016-09-16 16:11:40 -0700197 * @param affectedByApi true if this function is triggered from the APIs;
198 * false otherwise
Luca Prete9c2ee072016-02-16 11:00:44 -0800199 */
Luca Prete092e8952016-10-26 16:25:56 +0200200 private void setupConnectivity(String vplsName,
nosignal5fd282e2016-09-16 16:11:40 -0700201 Collection<Interface> interfaces,
202 Set<Host> hosts,
Luca Prete092e8952016-10-26 16:25:56 +0200203 EncapsulationType encap,
nosignal5fd282e2016-09-16 16:11:40 -0700204 boolean affectedByApi) {
205 List<Intent> intents = Lists.newArrayList();
206 List<Key> keys = Lists.newArrayList();
207 Set<FilteredConnectPoint> fcPoints = buildFCPoints(interfaces);
Luca Prete9c2ee072016-02-16 11:00:44 -0800208
nosignal5fd282e2016-09-16 16:11:40 -0700209 intents.addAll(buildBroadcastIntents(
Luca Prete092e8952016-10-26 16:25:56 +0200210 vplsName, fcPoints, encap, affectedByApi));
211 intents.addAll(buildUnicastIntents(
212 vplsName, hosts, fcPoints, encap, affectedByApi));
Luca Prete9c2ee072016-02-16 11:00:44 -0800213
nosignal5fd282e2016-09-16 16:11:40 -0700214 if (affectedByApi) {
215 intents.forEach(intent -> keys.add(intent.key()));
Luca Prete092e8952016-10-26 16:25:56 +0200216 withdrawIntents(vplsName, keys);
nosignal5fd282e2016-09-16 16:11:40 -0700217 }
218
219 intentInstaller.submitIntents(intents);
Luca Prete9c2ee072016-02-16 11:00:44 -0800220 }
221
222 /**
nosignal5fd282e2016-09-16 16:11:40 -0700223 * Withdraws intents belonging to a VPLS, given a VPLS name.
Luca Prete9c2ee072016-02-16 11:00:44 -0800224 *
Luca Prete092e8952016-10-26 16:25:56 +0200225 * @param vplsName the VPLS name
nosignal5fd282e2016-09-16 16:11:40 -0700226 * @param keys the keys of the intents to be installed
Luca Prete9c2ee072016-02-16 11:00:44 -0800227 */
Luca Prete092e8952016-10-26 16:25:56 +0200228 private void withdrawIntents(String vplsName, List<Key> keys) {
nosignal5fd282e2016-09-16 16:11:40 -0700229 List<Intent> intents = Lists.newArrayList();
Luca Prete9c2ee072016-02-16 11:00:44 -0800230
Luca Prete092e8952016-10-26 16:25:56 +0200231 intentInstaller.getIntentsFromVpls(vplsName)
nosignal5fd282e2016-09-16 16:11:40 -0700232 .forEach(intent -> {
233 if (!keys.contains(intent.key())) {
234 intents.add(intent);
235 }
236 });
Luca Prete9c2ee072016-02-16 11:00:44 -0800237
nosignal5fd282e2016-09-16 16:11:40 -0700238 intentInstaller.withdrawIntents(intents);
Luca Prete9c2ee072016-02-16 11:00:44 -0800239 }
240
nosignal5fd282e2016-09-16 16:11:40 -0700241 /**
242 * Sets up broadcast intents between any given filtered connect point.
243 *
Luca Prete092e8952016-10-26 16:25:56 +0200244 * @param vplsName the VPLS name
nosignal5fd282e2016-09-16 16:11:40 -0700245 * @param fcPoints the set of filtered connect points
Luca Prete092e8952016-10-26 16:25:56 +0200246 * @param encap the encapsulation type
nosignal5fd282e2016-09-16 16:11:40 -0700247 * @param affectedByApi true if the function triggered from APIs;
248 * false otherwise
249 * @return the set of broadcast intents
250 */
Luca Prete092e8952016-10-26 16:25:56 +0200251 private Set<Intent> buildBroadcastIntents(String vplsName,
nosignal5fd282e2016-09-16 16:11:40 -0700252 Set<FilteredConnectPoint> fcPoints,
Luca Prete092e8952016-10-26 16:25:56 +0200253 EncapsulationType encap,
nosignal5fd282e2016-09-16 16:11:40 -0700254 boolean affectedByApi) {
255 Set<Intent> intents = Sets.newHashSet();
256 fcPoints.forEach(point -> {
257 Set<FilteredConnectPoint> otherPoints =
258 fcPoints.stream()
259 .filter(fcp -> !fcp.equals(point))
260 .collect(Collectors.toSet());
261
262 Key brcKey = intentInstaller.buildKey(PREFIX_BROADCAST,
263 point.connectPoint(),
Luca Prete092e8952016-10-26 16:25:56 +0200264 vplsName,
nosignal5fd282e2016-09-16 16:11:40 -0700265 MacAddress.BROADCAST);
266
Luca Prete092e8952016-10-26 16:25:56 +0200267 if ((!intentInstaller.intentExists(brcKey) || affectedByApi)
268 && !otherPoints.isEmpty()) {
nosignal5fd282e2016-09-16 16:11:40 -0700269 intents.add(intentInstaller.buildBrcIntent(brcKey,
270 point,
Luca Prete092e8952016-10-26 16:25:56 +0200271 otherPoints,
272 encap));
Yi Tsengbf8b67e2016-09-01 15:02:11 +0800273 }
274 });
nosignal5fd282e2016-09-16 16:11:40 -0700275
276 return ImmutableSet.copyOf(intents);
277 }
278
279 /**
280 * Sets up unicast intents between any given filtered connect point.
281 *
Luca Prete092e8952016-10-26 16:25:56 +0200282 * @param vplsName the VPLS name
nosignal5fd282e2016-09-16 16:11:40 -0700283 * @param hosts the set of destination hosts
284 * @param fcPoints the set of filtered connect points
Luca Prete092e8952016-10-26 16:25:56 +0200285 * @param encap the encapsulation type
nosignal5fd282e2016-09-16 16:11:40 -0700286 * @param affectedByApi true if the function triggered from APIs;
287 * false otherwise
288 * @return the set of unicast intents
289 */
Luca Prete092e8952016-10-26 16:25:56 +0200290 private Set<Intent> buildUnicastIntents(String vplsName,
nosignal5fd282e2016-09-16 16:11:40 -0700291 Set<Host> hosts,
292 Set<FilteredConnectPoint> fcPoints,
Luca Prete092e8952016-10-26 16:25:56 +0200293 EncapsulationType encap,
nosignal5fd282e2016-09-16 16:11:40 -0700294 boolean affectedByApi) {
295 Set<Intent> intents = Sets.newHashSet();
296 hosts.forEach(host -> {
297 FilteredConnectPoint hostPoint = getHostPoint(host, fcPoints);
298
299 if (hostPoint == null) {
300 log.warn(HOST_FCP_NOT_FOUND, host);
301 return;
302 }
303
304 Set<FilteredConnectPoint> otherPoints =
305 fcPoints.stream()
306 .filter(fcp -> !fcp.equals(hostPoint))
307 .collect(Collectors.toSet());
308
309 Key uniKey = intentInstaller.buildKey(PREFIX_UNICAST,
310 host.location(),
Luca Prete092e8952016-10-26 16:25:56 +0200311 vplsName,
nosignal5fd282e2016-09-16 16:11:40 -0700312 host.mac());
313
314 if ((!intentInstaller.intentExists(uniKey) || affectedByApi) &&
315 !otherPoints.isEmpty()) {
316 intents.add(intentInstaller.buildUniIntent(uniKey,
317 otherPoints,
318 hostPoint,
Luca Prete092e8952016-10-26 16:25:56 +0200319 host,
320 encap));
nosignal5fd282e2016-09-16 16:11:40 -0700321 }
322 });
323
324 return ImmutableSet.copyOf(intents);
325 }
326
327 /**
328 * Finds the filtered connect point a host is attached to.
329 *
330 * @param host the target host
331 * @param fcps the filtered connected points
332 * @return null if not found; the filtered connect point otherwise
333 */
334 private FilteredConnectPoint getHostPoint(Host host,
335 Set<FilteredConnectPoint> fcps) {
336 return fcps.stream()
337 .filter(fcp -> fcp.connectPoint().equals(host.location()))
338 .filter(fcp -> {
339 VlanIdCriterion vlanCriterion =
340 (VlanIdCriterion) fcp.trafficSelector().
341 getCriterion(Criterion.Type.VLAN_VID);
342
343 return vlanCriterion != null &&
344 vlanCriterion.vlanId().equals(host.vlan());
345 })
346 .findFirst()
347 .orElse(null);
348 }
349
350 /**
351 * Computes a set of filtered connect points from a list of given interfaces.
352 *
353 * @param interfaces the interfaces to compute
354 * @return the set of filtered connect points
355 */
356 private Set<FilteredConnectPoint> buildFCPoints(Collection<Interface> interfaces) {
Luca Prete092e8952016-10-26 16:25:56 +0200357 // Build all filtered connected points in the VPLS
nosignal5fd282e2016-09-16 16:11:40 -0700358 return interfaces
359 .stream()
360 .map(intf -> {
361 TrafficSelector.Builder selectorBuilder =
362 DefaultTrafficSelector.builder();
363
364 if (!intf.vlan().equals(VlanId.NONE)) {
365 selectorBuilder.matchVlanId(intf.vlan());
366 }
367
368 return new FilteredConnectPoint(intf.connectPoint(),
369 selectorBuilder.build());
370 })
371 .collect(Collectors.toSet());
Luca Prete9c2ee072016-02-16 11:00:44 -0800372 }
373
374 /**
375 * Listener for host events.
376 */
nosignal5fd282e2016-09-16 16:11:40 -0700377 private class InternalHostListener implements HostListener {
Luca Prete9c2ee072016-02-16 11:00:44 -0800378 @Override
379 public void event(HostEvent event) {
nosignal5fd282e2016-09-16 16:11:40 -0700380 log.debug(HOST_EVENT, event);
Luca Prete9c2ee072016-02-16 11:00:44 -0800381 switch (event.type()) {
382 case HOST_ADDED:
383 case HOST_UPDATED:
384 case HOST_REMOVED:
nosignal5fd282e2016-09-16 16:11:40 -0700385 setupConnectivity(false);
Luca Prete9c2ee072016-02-16 11:00:44 -0800386 break;
nosignal5fd282e2016-09-16 16:11:40 -0700387
Luca Prete9c2ee072016-02-16 11:00:44 -0800388 default:
389 break;
390 }
391 }
392 }
Luca Pretea8854822016-04-26 16:30:55 -0700393
394 /**
395 * Listener for interface configuration events.
396 */
397 private class InternalInterfaceListener implements InterfaceListener {
398 @Override
399 public void event(InterfaceEvent event) {
nosignal5fd282e2016-09-16 16:11:40 -0700400 log.debug(INTF_CONF_EVENT, event);
Luca Pretea8854822016-04-26 16:30:55 -0700401 switch (event.type()) {
402 case INTERFACE_ADDED:
403 case INTERFACE_UPDATED:
404 case INTERFACE_REMOVED:
nosignal5fd282e2016-09-16 16:11:40 -0700405 setupConnectivity(false);
Luca Pretea8854822016-04-26 16:30:55 -0700406 break;
nosignal5fd282e2016-09-16 16:11:40 -0700407
Luca Pretea8854822016-04-26 16:30:55 -0700408 default:
409 break;
410 }
411 }
412 }
nosignal5fd282e2016-09-16 16:11:40 -0700413
414 /**
415 * Listener for VPLS configuration events.
416 */
417 private class InternalNetworkConfigListener implements NetworkConfigListener {
418 @Override
419 public void event(NetworkConfigEvent event) {
420 if (event.configClass() == VplsConfigurationService.CONFIG_CLASS) {
421 log.debug(NET_CONF_EVENT, event.configClass());
422 switch (event.type()) {
423 case CONFIG_ADDED:
424 case CONFIG_UPDATED:
425 case CONFIG_REMOVED:
426 setupConnectivity(true);
427 break;
428
429 default:
430 break;
431 }
432 }
433 }
434 }
Luca Prete9c2ee072016-02-16 11:00:44 -0800435}