blob: 4e4bd4bf9db8b9bfb85756ab3f117396a54b0f3c [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;
nosignal5fd282e2016-09-16 16:11:40 -070036import org.onosproject.net.FilteredConnectPoint;
Luca Prete9c2ee072016-02-16 11:00:44 -080037import org.onosproject.net.Host;
nosignal5fd282e2016-09-16 16:11:40 -070038import org.onosproject.net.config.NetworkConfigEvent;
39import org.onosproject.net.config.NetworkConfigListener;
40import org.onosproject.net.config.NetworkConfigService;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.criteria.Criterion;
44import org.onosproject.net.flow.criteria.VlanIdCriterion;
Luca Prete9c2ee072016-02-16 11:00:44 -080045import org.onosproject.net.host.HostEvent;
46import org.onosproject.net.host.HostListener;
47import org.onosproject.net.host.HostService;
nosignal5fd282e2016-09-16 16:11:40 -070048import org.onosproject.net.intent.Intent;
Luca Prete9c2ee072016-02-16 11:00:44 -080049import org.onosproject.net.intent.IntentService;
nosignal5fd282e2016-09-16 16:11:40 -070050import org.onosproject.net.intent.Key;
Luca Prete9c2ee072016-02-16 11:00:44 -080051import org.onosproject.routing.IntentSynchronizationService;
nosignal5fd282e2016-09-16 16:11:40 -070052import org.onosproject.vpls.config.VplsConfigurationService;
Luca Prete9c2ee072016-02-16 11:00:44 -080053import org.slf4j.Logger;
54
nosignal5fd282e2016-09-16 16:11:40 -070055import java.util.Collection;
56import java.util.HashSet;
57import java.util.List;
Luca Prete9c2ee072016-02-16 11:00:44 -080058import java.util.Set;
nosignal5fd282e2016-09-16 16:11:40 -070059import java.util.stream.Collectors;
Luca Prete9c2ee072016-02-16 11:00:44 -080060
61import static org.slf4j.LoggerFactory.getLogger;
nosignal5fd282e2016-09-16 16:11:40 -070062import static org.onosproject.vpls.IntentInstaller.PREFIX_BROADCAST;
63import static org.onosproject.vpls.IntentInstaller.PREFIX_UNICAST;
Luca Prete9c2ee072016-02-16 11:00:44 -080064
65/**
66 * Application to create L2 broadcast overlay networks using VLAN.
67 */
68@Component(immediate = true)
69public class Vpls {
nosignal5fd282e2016-09-16 16:11:40 -070070 /**
71 * Application name of VPLS.
72 */
73 static final String VPLS_APP = "org.onosproject.vpls";
74
75 private static final String HOST_FCP_NOT_FOUND =
76 "Filtered connected point for host {} not found";
77 private static final String HOST_EVENT = "Received HostEvent {}";
78 private static final String INTF_CONF_EVENT =
79 "Received InterfaceConfigEvent {}";
80 private static final String NET_CONF_EVENT =
81 "Received NetworkConfigEvent {}";
Yi Tseng28767f02016-09-13 04:27:20 -070082
Luca Prete9c2ee072016-02-16 11:00:44 -080083 private final Logger log = getLogger(getClass());
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected ApplicationService applicationService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected CoreService coreService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected HostService hostService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected IntentService intentService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected InterfaceService interfaceService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected IntentSynchronizationService intentSynchronizer;
102
nosignal5fd282e2016-09-16 16:11:40 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected NetworkConfigService configService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected VplsConfigurationService vplsConfigService;
108
Luca Prete9c2ee072016-02-16 11:00:44 -0800109 private final HostListener hostListener = new InternalHostListener();
110
Luca Pretea8854822016-04-26 16:30:55 -0700111 private final InternalInterfaceListener interfaceListener
112 = new InternalInterfaceListener();
113
nosignal5fd282e2016-09-16 16:11:40 -0700114 private final InternalNetworkConfigListener configListener =
115 new InternalNetworkConfigListener();
116
Luca Prete9c2ee072016-02-16 11:00:44 -0800117 private IntentInstaller intentInstaller;
118
119 private ApplicationId appId;
120
Yi Tseng28767f02016-09-13 04:27:20 -0700121
Luca Prete9c2ee072016-02-16 11:00:44 -0800122 @Activate
123 public void activate() {
124 appId = coreService.registerApplication(VPLS_APP);
125
126 intentInstaller = new IntentInstaller(appId,
127 intentService,
128 intentSynchronizer);
129
Luca Prete2705d662016-04-29 15:30:23 -0700130 applicationService.registerDeactivateHook(appId, () -> {
131 intentSynchronizer.removeIntentsByAppId(appId);
132 });
Luca Prete9c2ee072016-02-16 11:00:44 -0800133
134 hostService.addListener(hostListener);
Luca Pretea8854822016-04-26 16:30:55 -0700135 interfaceService.addListener(interfaceListener);
nosignal5fd282e2016-09-16 16:11:40 -0700136 configService.addListener(configListener);
Luca Prete9c2ee072016-02-16 11:00:44 -0800137
nosignal5fd282e2016-09-16 16:11:40 -0700138 setupConnectivity(false);
Luca Prete9c2ee072016-02-16 11:00:44 -0800139
Luca Pretec21c6e62016-09-22 14:52:21 -0700140 log.info("Activated");
Luca Prete9c2ee072016-02-16 11:00:44 -0800141 }
142
143 @Deactivate
144 public void deactivate() {
nosignal5fd282e2016-09-16 16:11:40 -0700145 configService.removeListener(configListener);
Yi Tseng28767f02016-09-13 04:27:20 -0700146 intentSynchronizer.removeIntentsByAppId(appId);
Luca Pretec21c6e62016-09-22 14:52:21 -0700147 log.info("Deactivated");
Luca Prete9c2ee072016-02-16 11:00:44 -0800148 }
149
nosignal5fd282e2016-09-16 16:11:40 -0700150 /**
151 * Sets up connectivity for all VPLSs.
152 *
153 * @param isNetworkConfigEvent true if this function is triggered
154 * by NetworkConfigEvent; false otherwise
155 */
156 private void setupConnectivity(boolean isNetworkConfigEvent) {
157 SetMultimap<String, Interface> networkInterfaces =
158 vplsConfigService.getVplsNetworks();
Luca Prete9c2ee072016-02-16 11:00:44 -0800159
nosignal5fd282e2016-09-16 16:11:40 -0700160 Set<String> vplsAffectedByApi =
161 new HashSet<>(vplsConfigService.getVplsAffectedByApi());
Luca Prete9c2ee072016-02-16 11:00:44 -0800162
nosignal5fd282e2016-09-16 16:11:40 -0700163 if (isNetworkConfigEvent && vplsAffectedByApi.isEmpty()) {
164 vplsAffectedByApi.addAll(vplsConfigService.getOldVpls());
165 }
Yi Tseng28767f02016-09-13 04:27:20 -0700166
nosignal5fd282e2016-09-16 16:11:40 -0700167 networkInterfaces.asMap().forEach((networkName, interfaces) -> {
168 Set<Host> hosts = Sets.newHashSet();
169 interfaces.forEach(intf -> {
170 // Add hosts that belongs to the specific VPLS
171 hostService.getConnectedHosts(intf.connectPoint())
172 .stream()
173 .filter(host -> host.vlan().equals(intf.vlan()))
174 .forEach(hosts::add);
175 });
176
177 setupConnectivity(networkName, interfaces, hosts,
178 vplsAffectedByApi.contains(networkName));
179 vplsAffectedByApi.remove(networkName);
180 });
181
182 if (!vplsAffectedByApi.isEmpty()) {
183 for (String networkName:vplsAffectedByApi) {
184 withdrawIntents(networkName, Lists.newArrayList());
185 }
186 }
Luca Prete9c2ee072016-02-16 11:00:44 -0800187 }
188
189 /**
nosignal5fd282e2016-09-16 16:11:40 -0700190 * Sets up connectivity for specific VPLS.
Luca Prete9c2ee072016-02-16 11:00:44 -0800191 *
nosignal5fd282e2016-09-16 16:11:40 -0700192 * @param networkName the VPLS name
193 * @param interfaces the interfaces that belong to the VPLS
194 * @param hosts the hosts that belong to the VPLS
195 * @param affectedByApi true if this function is triggered from the APIs;
196 * false otherwise
Luca Prete9c2ee072016-02-16 11:00:44 -0800197 */
nosignal5fd282e2016-09-16 16:11:40 -0700198 private void setupConnectivity(String networkName,
199 Collection<Interface> interfaces,
200 Set<Host> hosts,
201 boolean affectedByApi) {
202 List<Intent> intents = Lists.newArrayList();
203 List<Key> keys = Lists.newArrayList();
204 Set<FilteredConnectPoint> fcPoints = buildFCPoints(interfaces);
Luca Prete9c2ee072016-02-16 11:00:44 -0800205
nosignal5fd282e2016-09-16 16:11:40 -0700206 intents.addAll(buildUnicastIntents(
207 networkName, hosts, fcPoints, affectedByApi));
208 intents.addAll(buildBroadcastIntents(
209 networkName, fcPoints, affectedByApi));
Luca Prete9c2ee072016-02-16 11:00:44 -0800210
nosignal5fd282e2016-09-16 16:11:40 -0700211 if (affectedByApi) {
212 intents.forEach(intent -> keys.add(intent.key()));
213 withdrawIntents(networkName, keys);
214 }
215
216 intentInstaller.submitIntents(intents);
Luca Prete9c2ee072016-02-16 11:00:44 -0800217 }
218
219 /**
nosignal5fd282e2016-09-16 16:11:40 -0700220 * Withdraws intents belonging to a VPLS, given a VPLS name.
Luca Prete9c2ee072016-02-16 11:00:44 -0800221 *
nosignal5fd282e2016-09-16 16:11:40 -0700222 * @param networkName the VPLS name
223 * @param keys the keys of the intents to be installed
Luca Prete9c2ee072016-02-16 11:00:44 -0800224 */
nosignal5fd282e2016-09-16 16:11:40 -0700225 private void withdrawIntents(String networkName,
226 List<Key> keys) {
227 List<Intent> intents = Lists.newArrayList();
Luca Prete9c2ee072016-02-16 11:00:44 -0800228
nosignal5fd282e2016-09-16 16:11:40 -0700229 intentInstaller.getIntentsFromVpls(networkName)
230 .forEach(intent -> {
231 if (!keys.contains(intent.key())) {
232 intents.add(intent);
233 }
234 });
Luca Prete9c2ee072016-02-16 11:00:44 -0800235
nosignal5fd282e2016-09-16 16:11:40 -0700236 intentInstaller.withdrawIntents(intents);
Luca Prete9c2ee072016-02-16 11:00:44 -0800237 }
238
nosignal5fd282e2016-09-16 16:11:40 -0700239 /**
240 * Sets up broadcast intents between any given filtered connect point.
241 *
242 * @param networkName the VPLS name
243 * @param fcPoints the set of filtered connect points
244 * @param affectedByApi true if the function triggered from APIs;
245 * false otherwise
246 * @return the set of broadcast intents
247 */
248 private Set<Intent> buildBroadcastIntents(String networkName,
249 Set<FilteredConnectPoint> fcPoints,
250 boolean affectedByApi) {
251 Set<Intent> intents = Sets.newHashSet();
252 fcPoints.forEach(point -> {
253 Set<FilteredConnectPoint> otherPoints =
254 fcPoints.stream()
255 .filter(fcp -> !fcp.equals(point))
256 .collect(Collectors.toSet());
257
258 Key brcKey = intentInstaller.buildKey(PREFIX_BROADCAST,
259 point.connectPoint(),
260 networkName,
261 MacAddress.BROADCAST);
262
263 if ((!intentInstaller.intentExists(brcKey) || affectedByApi) &&
264 !otherPoints.isEmpty()) {
265 intents.add(intentInstaller.buildBrcIntent(brcKey,
266 point,
267 otherPoints));
Yi Tsengbf8b67e2016-09-01 15:02:11 +0800268 }
269 });
nosignal5fd282e2016-09-16 16:11:40 -0700270
271 return ImmutableSet.copyOf(intents);
272 }
273
274 /**
275 * Sets up unicast intents between any given filtered connect point.
276 *
277 * @param networkName the VPLS name
278 * @param hosts the set of destination hosts
279 * @param fcPoints the set of filtered connect points
280 * @param affectedByApi true if the function triggered from APIs;
281 * false otherwise
282 * @return the set of unicast intents
283 */
284 private Set<Intent> buildUnicastIntents(String networkName,
285 Set<Host> hosts,
286 Set<FilteredConnectPoint> fcPoints,
287 boolean affectedByApi) {
288 Set<Intent> intents = Sets.newHashSet();
289 hosts.forEach(host -> {
290 FilteredConnectPoint hostPoint = getHostPoint(host, fcPoints);
291
292 if (hostPoint == null) {
293 log.warn(HOST_FCP_NOT_FOUND, host);
294 return;
295 }
296
297 Set<FilteredConnectPoint> otherPoints =
298 fcPoints.stream()
299 .filter(fcp -> !fcp.equals(hostPoint))
300 .collect(Collectors.toSet());
301
302 Key uniKey = intentInstaller.buildKey(PREFIX_UNICAST,
303 host.location(),
304 networkName,
305 host.mac());
306
307 if ((!intentInstaller.intentExists(uniKey) || affectedByApi) &&
308 !otherPoints.isEmpty()) {
309 intents.add(intentInstaller.buildUniIntent(uniKey,
310 otherPoints,
311 hostPoint,
312 host));
313 }
314 });
315
316 return ImmutableSet.copyOf(intents);
317 }
318
319 /**
320 * Finds the filtered connect point a host is attached to.
321 *
322 * @param host the target host
323 * @param fcps the filtered connected points
324 * @return null if not found; the filtered connect point otherwise
325 */
326 private FilteredConnectPoint getHostPoint(Host host,
327 Set<FilteredConnectPoint> fcps) {
328 return fcps.stream()
329 .filter(fcp -> fcp.connectPoint().equals(host.location()))
330 .filter(fcp -> {
331 VlanIdCriterion vlanCriterion =
332 (VlanIdCriterion) fcp.trafficSelector().
333 getCriterion(Criterion.Type.VLAN_VID);
334
335 return vlanCriterion != null &&
336 vlanCriterion.vlanId().equals(host.vlan());
337 })
338 .findFirst()
339 .orElse(null);
340 }
341
342 /**
343 * Computes a set of filtered connect points from a list of given interfaces.
344 *
345 * @param interfaces the interfaces to compute
346 * @return the set of filtered connect points
347 */
348 private Set<FilteredConnectPoint> buildFCPoints(Collection<Interface> interfaces) {
349 // Build all filtered connected points in the network
350 return interfaces
351 .stream()
352 .map(intf -> {
353 TrafficSelector.Builder selectorBuilder =
354 DefaultTrafficSelector.builder();
355
356 if (!intf.vlan().equals(VlanId.NONE)) {
357 selectorBuilder.matchVlanId(intf.vlan());
358 }
359
360 return new FilteredConnectPoint(intf.connectPoint(),
361 selectorBuilder.build());
362 })
363 .collect(Collectors.toSet());
Luca Prete9c2ee072016-02-16 11:00:44 -0800364 }
365
366 /**
367 * Listener for host events.
368 */
nosignal5fd282e2016-09-16 16:11:40 -0700369 private class InternalHostListener implements HostListener {
Luca Prete9c2ee072016-02-16 11:00:44 -0800370 @Override
371 public void event(HostEvent event) {
nosignal5fd282e2016-09-16 16:11:40 -0700372 log.debug(HOST_EVENT, event);
Luca Prete9c2ee072016-02-16 11:00:44 -0800373 switch (event.type()) {
374 case HOST_ADDED:
375 case HOST_UPDATED:
376 case HOST_REMOVED:
nosignal5fd282e2016-09-16 16:11:40 -0700377 setupConnectivity(false);
Luca Prete9c2ee072016-02-16 11:00:44 -0800378 break;
nosignal5fd282e2016-09-16 16:11:40 -0700379
Luca Prete9c2ee072016-02-16 11:00:44 -0800380 default:
381 break;
382 }
383 }
384 }
Luca Pretea8854822016-04-26 16:30:55 -0700385
386 /**
387 * Listener for interface configuration events.
388 */
389 private class InternalInterfaceListener implements InterfaceListener {
390 @Override
391 public void event(InterfaceEvent event) {
nosignal5fd282e2016-09-16 16:11:40 -0700392 log.debug(INTF_CONF_EVENT, event);
Luca Pretea8854822016-04-26 16:30:55 -0700393 switch (event.type()) {
394 case INTERFACE_ADDED:
395 case INTERFACE_UPDATED:
396 case INTERFACE_REMOVED:
nosignal5fd282e2016-09-16 16:11:40 -0700397 setupConnectivity(false);
Luca Pretea8854822016-04-26 16:30:55 -0700398 break;
nosignal5fd282e2016-09-16 16:11:40 -0700399
Luca Pretea8854822016-04-26 16:30:55 -0700400 default:
401 break;
402 }
403 }
404 }
nosignal5fd282e2016-09-16 16:11:40 -0700405
406 /**
407 * Listener for VPLS configuration events.
408 */
409 private class InternalNetworkConfigListener implements NetworkConfigListener {
410 @Override
411 public void event(NetworkConfigEvent event) {
412 if (event.configClass() == VplsConfigurationService.CONFIG_CLASS) {
413 log.debug(NET_CONF_EVENT, event.configClass());
414 switch (event.type()) {
415 case CONFIG_ADDED:
416 case CONFIG_UPDATED:
417 case CONFIG_REMOVED:
418 setupConnectivity(true);
419 break;
420
421 default:
422 break;
423 }
424 }
425 }
426 }
Luca Prete9c2ee072016-02-16 11:00:44 -0800427}