blob: c8ecdd7b1947de48e53d77883009074ca17adef3 [file] [log] [blame]
Lee Yongjae7c27bb42017-11-17 12:00:45 +09001/*
2 * Copyright 2017-present Open Networking Foundation
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 */
16
17package org.onosproject.simplefabric;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070023import org.osgi.service.component.annotations.Activate;
24import org.osgi.service.component.annotations.Component;
25import org.osgi.service.component.annotations.Deactivate;
26import org.osgi.service.component.annotations.Reference;
27import org.osgi.service.component.annotations.ReferenceCardinality;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090028import org.onlab.packet.MacAddress;
29import org.onlab.packet.VlanId;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.net.intf.Interface;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.EncapsulationType;
35import org.onosproject.net.FilteredConnectPoint;
36import org.onosproject.net.Host;
37import org.onosproject.net.ResourceGroup;
38import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.host.HostService;
41import org.onosproject.net.intent.Constraint;
42import org.onosproject.net.intent.Intent;
43import org.onosproject.net.intent.IntentService;
44import org.onosproject.net.intent.Key;
45import org.onosproject.net.intent.MultiPointToSinglePointIntent;
46import org.onosproject.net.intent.SinglePointToMultiPointIntent;
47import org.onosproject.net.intent.constraint.EncapsulationConstraint;
48import org.onosproject.net.intent.constraint.PartialFailureConstraint;
49import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
51
52import java.io.PrintStream;
53import java.util.ArrayList;
54import java.util.Collection;
55import java.util.Set;
56import java.util.HashSet;
57import java.util.List;
58import java.util.Objects;
59import java.util.Map;
60import java.util.stream.Collectors;
61
62
63/**
64 * An implementation of L2NetworkOperationService.
65 * Handles the execution order of the L2 Network operations generated by the
66 * application.
67 */
68@Component(immediate = true, enabled = false)
69public class SimpleFabricL2Forward {
70
71 public static final String BROADCAST = "BCAST";
72 public static final String UNICAST = "UNI";
73
74 private final Logger log = LoggerFactory.getLogger(getClass());
75 protected ApplicationId l2ForwardAppId;
76
Ray Milkeyd84f89b2018-08-17 14:54:17 -070077 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +090078 protected CoreService coreService;
79
Ray Milkeyd84f89b2018-08-17 14:54:17 -070080 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +090081 protected IntentService intentService;
82
Ray Milkeyd84f89b2018-08-17 14:54:17 -070083 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +090084 protected HostService hostService;
85
Ray Milkeyd84f89b2018-08-17 14:54:17 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +090087 protected SimpleFabricService simpleFabric;
88
89 public static final ImmutableList<Constraint> L2NETWORK_CONSTRAINTS =
90 ImmutableList.of(new PartialFailureConstraint());
91
92 private Map<Key, SinglePointToMultiPointIntent> bctIntentsMap = Maps.newConcurrentMap();
93 private Map<Key, MultiPointToSinglePointIntent> uniIntentsMap = Maps.newConcurrentMap();
94 private Set<Key> toBePurgedIntentKeys = new HashSet<>();
95
96 private final InternalSimpleFabricListener simpleFabricListener = new InternalSimpleFabricListener();
97
98 @Activate
99 public void activate() {
100 l2ForwardAppId = coreService.registerApplication(simpleFabric.L2FORWARD_APP_ID);
101 log.info("simple fabric l2 forwaring starting with l2net app id {}", l2ForwardAppId.toString());
102
103 simpleFabric.addListener(simpleFabricListener);
104
105 refresh();
106 checkIntentsPurge();
107
108 log.info("simple fabric l2forward started");
109 }
110
111 @Deactivate
112 public void deactivate() {
113 log.info("simple fabric l2forward stopping");
114
115 simpleFabric.removeListener(simpleFabricListener);
116
117 for (Intent intent : bctIntentsMap.values()) {
118 intentService.withdraw(intent);
119 toBePurgedIntentKeys.add(intent.key());
120 }
121 for (Intent intent : uniIntentsMap.values()) {
122 intentService.withdraw(intent);
123 toBePurgedIntentKeys.add(intent.key());
124 }
125 for (Key key : toBePurgedIntentKeys) {
126 Intent intentToPurge = intentService.getIntent(key);
127 if (intentToPurge != null) {
128 intentService.purge(intentToPurge);
129 }
130 }
131
132 // do not set clear for switch compatibility
133 //bctIntentsMap.clear();
134 //uniIntentsMap.clear();
135
136 log.info("simple fabric l2forward stopped");
137 }
138
139 private void refresh() {
140 log.debug("simple fabric l2forward refresh");
141
142 Map<Key, SinglePointToMultiPointIntent> newBctIntentsMap = Maps.newConcurrentMap();
143 Map<Key, MultiPointToSinglePointIntent> newUniIntentsMap = Maps.newConcurrentMap();
144
145 for (L2Network l2Network : simpleFabric.getL2Networks()) {
146 // scans all l2network regardless of dirty flag
147 // if l2Network.l2Forward == false or number of interfaces() < 2, no Intents generated
148 for (SinglePointToMultiPointIntent intent : buildBrcIntents(l2Network)) {
149 newBctIntentsMap.put(intent.key(), intent);
150 }
151 for (MultiPointToSinglePointIntent intent : buildUniIntents(l2Network, hostsFromL2Network(l2Network))) {
152 newUniIntentsMap.put(intent.key(), intent);
153 }
154 if (l2Network.dirty()) {
155 l2Network.setDirty(false);
156 }
157 }
158
159 boolean bctUpdated = false;
160 for (SinglePointToMultiPointIntent intent : bctIntentsMap.values()) {
161 SinglePointToMultiPointIntent newIntent = newBctIntentsMap.get(intent.key());
162 if (newIntent == null) {
163 log.info("simple fabric l2forward withdraw broadcast intent: {}", intent.key().toString());
164 toBePurgedIntentKeys.add(intent.key());
165 intentService.withdraw(intent);
166 bctUpdated = true;
167 }
168 }
169 for (SinglePointToMultiPointIntent intent : newBctIntentsMap.values()) {
170 SinglePointToMultiPointIntent oldIntent = bctIntentsMap.get(intent.key());
171 if (oldIntent == null ||
172 !oldIntent.filteredEgressPoints().equals(intent.filteredEgressPoints()) ||
173 !oldIntent.filteredIngressPoint().equals(intent.filteredIngressPoint()) ||
174 !oldIntent.selector().equals(intent.selector()) ||
175 !oldIntent.treatment().equals(intent.treatment()) ||
176 !oldIntent.constraints().equals(intent.constraints())) {
177 log.info("simple fabric l2forward submit broadcast intent: {}", intent.key().toString());
178 toBePurgedIntentKeys.remove(intent.key());
179 intentService.submit(intent);
180 bctUpdated = true;
181 }
182 }
183
184 boolean uniUpdated = false;
185 for (MultiPointToSinglePointIntent intent : uniIntentsMap.values()) {
186 MultiPointToSinglePointIntent newIntent = newUniIntentsMap.get(intent.key());
187 if (newIntent == null) {
188 log.info("simple fabric l2forward withdraw unicast intent: {}", intent.key().toString());
189 toBePurgedIntentKeys.add(intent.key());
190 intentService.withdraw(intent);
191 uniUpdated = true;
192 }
193 }
194 for (MultiPointToSinglePointIntent intent : newUniIntentsMap.values()) {
195 MultiPointToSinglePointIntent oldIntent = uniIntentsMap.get(intent.key());
196 if (oldIntent == null ||
197 !oldIntent.filteredEgressPoint().equals(intent.filteredEgressPoint()) ||
198 !oldIntent.filteredIngressPoints().equals(intent.filteredIngressPoints()) ||
199 !oldIntent.selector().equals(intent.selector()) ||
200 !oldIntent.treatment().equals(intent.treatment()) ||
201 !oldIntent.constraints().equals(intent.constraints())) {
202 log.info("simple fabric l2forward submit unicast intent: {}", intent.key().toString());
203 toBePurgedIntentKeys.remove(intent.key());
204 intentService.submit(intent);
205 uniUpdated = true;
206 }
207 }
208
209 if (bctUpdated) {
210 bctIntentsMap = newBctIntentsMap;
211 }
212 if (uniUpdated) {
213 uniIntentsMap = newUniIntentsMap;
214 }
215 }
216
217 private void checkIntentsPurge() {
218 // check intents to be purge
219 if (!toBePurgedIntentKeys.isEmpty()) {
220 Set<Key> purgedKeys = new HashSet<>();
221 for (Key key : toBePurgedIntentKeys) {
222 Intent intentToPurge = intentService.getIntent(key);
223 if (intentToPurge == null) {
224 log.info("simple fabric l2forward purged intent: key={}", key.toString());
225 purgedKeys.add(key);
226 } else {
227 switch (intentService.getIntentState(key)) {
228 case FAILED:
229 case WITHDRAWN:
230 log.info("simple fabric l2forward try to purge intent: key={}", key.toString());
231 intentService.purge(intentToPurge);
232 break;
233 case INSTALL_REQ:
234 case INSTALLED:
235 case INSTALLING:
236 case RECOMPILING:
237 case COMPILING:
238 log.warn("simple fabric l2forward withdraw intent to purge: key={}", key);
239 intentService.withdraw(intentToPurge);
240 break;
241 case WITHDRAW_REQ:
242 case WITHDRAWING:
243 case PURGE_REQ:
244 case CORRUPT:
245 default:
246 // no action
247 break;
248 }
249 }
250 }
251 toBePurgedIntentKeys.removeAll(purgedKeys);
252 }
253 }
254
255 // Generates Unicast Intents and broadcast Intents for the L2 Network.
256
257 private Set<Intent> generateL2NetworkIntents(L2Network l2Network) {
258 return new ImmutableSet.Builder<Intent>()
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900259 .addAll(buildBrcIntents(l2Network))
260 .addAll(buildUniIntents(l2Network, hostsFromL2Network(l2Network)))
261 .build();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900262 }
263
264 // Build Boadcast Intents for a L2 Network.
265 private Set<SinglePointToMultiPointIntent> buildBrcIntents(L2Network l2Network) {
266 Set<Interface> interfaces = l2Network.interfaces();
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900267 if (interfaces.size() < 2 || !l2Network.l2Forward() || !l2Network.l2Broadcast()) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900268 return ImmutableSet.of();
269 }
270 Set<SinglePointToMultiPointIntent> brcIntents = Sets.newHashSet();
271 ResourceGroup resourceGroup = ResourceGroup.of(l2Network.name());
272
273 // Generates broadcast Intents from any network interface to other
274 // network interface from the L2 Network.
275 interfaces
276 .forEach(src -> {
277 FilteredConnectPoint srcFcp = buildFilteredConnectedPoint(src);
278 Set<FilteredConnectPoint> dstFcps = interfaces.stream()
279 .filter(iface -> !iface.equals(src))
280 .map(this::buildFilteredConnectedPoint)
281 .collect(Collectors.toSet());
282 Key key = buildKey(l2Network.name(), "BCAST", srcFcp.connectPoint(), MacAddress.BROADCAST);
283 TrafficSelector selector = DefaultTrafficSelector.builder()
284 .matchEthDst(MacAddress.BROADCAST)
285 .build();
286 SinglePointToMultiPointIntent.Builder intentBuilder = SinglePointToMultiPointIntent.builder()
287 .appId(l2ForwardAppId)
288 .key(key)
289 .selector(selector)
290 .filteredIngressPoint(srcFcp)
291 .filteredEgressPoints(dstFcps)
292 .constraints(buildConstraints(L2NETWORK_CONSTRAINTS, l2Network.encapsulation()))
293 .priority(SimpleFabricService.PRI_L2NETWORK_BROADCAST)
294 .resourceGroup(resourceGroup);
295 brcIntents.add(intentBuilder.build());
296 });
297 return brcIntents;
298 }
299
300 // Builds unicast Intents for a L2 Network.
301 private Set<MultiPointToSinglePointIntent> buildUniIntents(L2Network l2Network, Set<Host> hosts) {
302 Set<Interface> interfaces = l2Network.interfaces();
303 if (!l2Network.l2Forward() || interfaces.size() < 2) {
304 return ImmutableSet.of();
305 }
306 Set<MultiPointToSinglePointIntent> uniIntents = Sets.newHashSet();
307 ResourceGroup resourceGroup = ResourceGroup.of(l2Network.name());
308 hosts.forEach(host -> {
309 FilteredConnectPoint hostFcp = buildFilteredConnectedPoint(host);
310 Set<FilteredConnectPoint> srcFcps = interfaces.stream()
311 .map(this::buildFilteredConnectedPoint)
312 .filter(fcp -> !fcp.equals(hostFcp))
313 .collect(Collectors.toSet());
314 Key key = buildKey(l2Network.name(), "UNI", hostFcp.connectPoint(), host.mac());
315 TrafficSelector selector = DefaultTrafficSelector.builder()
316 .matchEthDst(host.mac()).build();
317 MultiPointToSinglePointIntent.Builder intentBuilder = MultiPointToSinglePointIntent.builder()
318 .appId(l2ForwardAppId)
319 .key(key)
320 .selector(selector)
321 .filteredIngressPoints(srcFcps)
322 .filteredEgressPoint(hostFcp)
323 .constraints(buildConstraints(L2NETWORK_CONSTRAINTS, l2Network.encapsulation()))
324 .priority(SimpleFabricService.PRI_L2NETWORK_UNICAST)
325 .resourceGroup(resourceGroup);
326 uniIntents.add(intentBuilder.build());
327 });
328
329 return uniIntents;
330 }
331
332 // Intent generate utilities
333
334 private Set<Host> hostsFromL2Network(L2Network l2Network) {
335 Set<Interface> interfaces = l2Network.interfaces();
336 return interfaces.stream()
337 .map(this::hostsFromInterface)
338 .flatMap(Collection::stream)
339 .collect(Collectors.toSet());
340 }
341
342 private Set<Host> hostsFromInterface(Interface iface) {
343 return hostService.getConnectedHosts(iface.connectPoint())
344 .stream()
345 .filter(host -> host.vlan().equals(iface.vlan()))
346 .collect(Collectors.toSet());
347 }
348
349 private Key buildKey(String l2NetworkName, String type, ConnectPoint cPoint, MacAddress dstMac) {
350 return Key.of(l2NetworkName + "-" + type + "-" + cPoint.toString() + "-" + dstMac, l2ForwardAppId);
351 }
352
353 private List<Constraint> buildConstraints(List<Constraint> constraints, EncapsulationType encapsulation) {
354 if (!encapsulation.equals(EncapsulationType.NONE)) {
355 List<Constraint> newConstraints = new ArrayList<>(constraints);
356 constraints.stream()
357 .filter(c -> c instanceof EncapsulationConstraint)
358 .forEach(newConstraints::remove);
359 newConstraints.add(new EncapsulationConstraint(encapsulation));
360 return ImmutableList.copyOf(newConstraints);
361 }
362 return constraints;
363 }
364
365 private FilteredConnectPoint buildFilteredConnectedPoint(Interface iface) {
366 Objects.requireNonNull(iface);
367 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
368
369 if (iface.vlan() != null && !iface.vlan().equals(VlanId.NONE)) {
370 trafficSelector.matchVlanId(iface.vlan());
371 }
372 return new FilteredConnectPoint(iface.connectPoint(), trafficSelector.build());
373 }
374
375 protected FilteredConnectPoint buildFilteredConnectedPoint(Host host) {
376 Objects.requireNonNull(host);
377 TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
378
379 if (host.vlan() != null && !host.vlan().equals(VlanId.NONE)) {
380 trafficSelector.matchVlanId(host.vlan());
381 }
382 return new FilteredConnectPoint(host.location(), trafficSelector.build());
383 }
384
385 // Dump command handler
386 private void dump(String subject, PrintStream out) {
Ray Milkey2ff67162018-01-22 10:14:19 -0800387 if ("intents".equals(subject)) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900388 out.println("L2Forward Broadcast Intents:\n");
389 for (SinglePointToMultiPointIntent intent: bctIntentsMap.values()) {
390 out.println(" " + intent.key().toString()
391 + ": " + intent.selector().criteria()
392 + ", [" + intent.filteredIngressPoint().connectPoint()
393 + "] -> " + intent.filteredEgressPoints().stream()
394 .map(FilteredConnectPoint::connectPoint).collect(Collectors.toSet()));
395 }
396 out.println("");
397 out.println("L2Forward Unicast Intents:\n");
398 for (MultiPointToSinglePointIntent intent: uniIntentsMap.values()) {
399 out.println(" " + intent.key().toString()
400 + ": " + intent.selector().criteria()
401 + ", [" + intent.filteredIngressPoints().stream()
402 .map(FilteredConnectPoint::connectPoint).collect(Collectors.toSet())
403 + "] -> " + intent.filteredEgressPoint().connectPoint());
404 }
405 out.println("");
406 out.println("L2Forward Intents to Be Purged:\n");
407 for (Key key: toBePurgedIntentKeys) {
408 out.println(" " + key.toString());
409 }
410 out.println("");
411 }
412 }
413
414 // Listener
415 private class InternalSimpleFabricListener implements SimpleFabricListener {
416 @Override
417 public void event(SimpleFabricEvent event) {
418 switch (event.type()) {
419 case SIMPLE_FABRIC_UPDATED:
420 refresh();
421 checkIntentsPurge();
422 break;
423 case SIMPLE_FABRIC_IDLE:
424 refresh();
425 checkIntentsPurge();
426 break;
427 case SIMPLE_FABRIC_DUMP:
428 dump(event.subject(), event.out());
429 break;
430 default:
431 // NOTE: nothing to do on SIMPLE_FABRIC_FLUSH
432 break;
433 }
434 }
435 }
436
437}