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