blob: 09c65510f19233b93f122eecf280dec67c79c9dd [file] [log] [blame]
Michele Santuari4b6019e2014-12-19 11:31:45 +01001package org.onosproject.net.intent.impl;
2
3import static org.slf4j.LoggerFactory.getLogger;
4
5import java.util.Iterator;
6import java.util.List;
7import java.util.Set;
8
9import org.apache.felix.scr.annotations.Activate;
10import org.apache.felix.scr.annotations.Component;
11import org.apache.felix.scr.annotations.Deactivate;
12import org.apache.felix.scr.annotations.Reference;
13import org.apache.felix.scr.annotations.ReferenceCardinality;
14import org.onlab.packet.Ethernet;
15import org.onosproject.core.ApplicationId;
16import org.onosproject.core.CoreService;
17import org.onosproject.net.ConnectPoint;
18import org.onosproject.net.DeviceId;
19import org.onosproject.net.Link;
20import org.onosproject.net.PortNumber;
21import org.onosproject.net.flow.DefaultFlowRule;
22import org.onosproject.net.flow.DefaultTrafficSelector;
23import org.onosproject.net.flow.DefaultTrafficTreatment;
24import org.onosproject.net.flow.FlowRule;
25import org.onosproject.net.flow.FlowRuleBatchEntry;
26import org.onosproject.net.flow.FlowRuleBatchOperation;
27import org.onosproject.net.flow.TrafficSelector;
28import org.onosproject.net.flow.TrafficTreatment;
29import org.onosproject.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
30import org.onosproject.net.flow.criteria.Criteria.EthTypeCriterion;
31import org.onosproject.net.flow.criteria.Criterion;
32import org.onosproject.net.flow.criteria.Criterion.Type;
33import org.onosproject.net.intent.IntentExtensionService;
34import org.onosproject.net.intent.IntentInstaller;
35import org.onosproject.net.intent.MplsPathIntent;
36import org.onosproject.net.link.LinkStore;
37import org.onosproject.net.resource.DefaultLinkResourceRequest;
38import org.onosproject.net.resource.LinkResourceAllocations;
39import org.onosproject.net.resource.LinkResourceRequest;
40import org.onosproject.net.resource.LinkResourceService;
41import org.onosproject.net.resource.MplsLabel;
42import org.onosproject.net.resource.MplsLabelResourceAllocation;
43import org.onosproject.net.resource.ResourceAllocation;
44import org.onosproject.net.resource.ResourceType;
45import org.slf4j.Logger;
46
47import static com.google.common.base.Preconditions.checkNotNull;
48
49import com.google.common.collect.Lists;
50import com.google.common.collect.Sets;
51
52/**
53 * Installer for {@link MplsPathIntent packet path connectivity intents}.
54 */
55@Component(immediate = true)
56public class MplsPathIntentInstaller implements IntentInstaller<MplsPathIntent> {
57
58 private final Logger log = getLogger(getClass());
59
60 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
61 protected IntentExtensionService intentManager;
62
63 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
64 protected CoreService coreService;
65
66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected LinkResourceService resourceService;
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected LinkStore linkStore;
71
72 protected ApplicationId appId;
73
74 @Activate
75 public void activate() {
76 appId = coreService.registerApplication("org.onosproject.net.intent");
77 intentManager.registerInstaller(MplsPathIntent.class, this);
78 }
79
80 @Deactivate
81 public void deactivate() {
82 intentManager.unregisterInstaller(MplsPathIntent.class);
83 }
84
85 @Override
86 public List<FlowRuleBatchOperation> install(MplsPathIntent intent) {
87 LinkResourceAllocations allocations = assignMplsLabel(intent);
88 return generateRules(intent, allocations, FlowRuleOperation.ADD);
89
90 }
91
92 @Override
93 public List<FlowRuleBatchOperation> uninstall(MplsPathIntent intent) {
94 LinkResourceAllocations allocations = resourceService
95 .getAllocations(intent.id());
96 resourceService.releaseResources(allocations);
97
98 List<FlowRuleBatchOperation> rules = generateRules(intent,
99 allocations,
100 FlowRuleOperation.REMOVE);
101 return rules;
102 }
103
104 @Override
105 public List<FlowRuleBatchOperation> replace(MplsPathIntent oldIntent,
106 MplsPathIntent newIntent) {
107
108 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
109 batches.addAll(uninstall(oldIntent));
110 batches.addAll(install(newIntent));
111 return batches;
112 }
113
114 private LinkResourceAllocations assignMplsLabel(MplsPathIntent intent) {
115
116 // TODO: do it better... Suggestions?
117 Set<Link> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
118 .links().size() - 2);
119 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
120 Link link = intent.path().links().get(i);
121 linkRequest.add(link);
122 // add the inverse link. I want that the label is reserved both for
123 // the direct and inverse link
124 linkRequest.add(linkStore.getLink(link.dst(), link.src()));
125 }
126
127 LinkResourceRequest.Builder request = DefaultLinkResourceRequest
128 .builder(intent.id(), linkRequest).addMplsRequest();
129 LinkResourceAllocations reqMpls = resourceService
130 .requestResources(request.build());
131 return reqMpls;
132 }
133
134 private MplsLabel getMplsLabel(LinkResourceAllocations allocations,
135 Link link) {
136
137 for (ResourceAllocation allocation : allocations
138 .getResourceAllocation(link)) {
139 if (allocation.type() == ResourceType.MPLS_LABEL) {
140 return ((MplsLabelResourceAllocation) allocation).mplsLabel();
141
142 }
143 }
144 log.warn("MPLS label was not assigned successfully");
145 return null;
146 }
147
148 private List<FlowRuleBatchOperation> generateRules(MplsPathIntent intent,
149 LinkResourceAllocations allocations,
150 FlowRuleOperation operation) {
151
152 Iterator<Link> links = intent.path().links().iterator();
153 Link srcLink = links.next();
154 ConnectPoint prev = srcLink.dst();
155
156 Link link = links.next();
157 // List of flow rules to be installed
158 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
159
160 // Ingress traffic
161 // Get the new MPLS label
162 MplsLabel mpls = getMplsLabel(allocations, link);
163 checkNotNull(mpls);
164 MplsLabel prevLabel = mpls;
165 rules.add(ingressFlow(prev.port(), link, intent, mpls, operation));
166
167 prev = link.dst();
168
169 while (links.hasNext()) {
170
171 link = links.next();
172
173 if (links.hasNext()) {
174 // Transit traffic
175 // Get the new MPLS label
176 mpls = getMplsLabel(allocations, link);
177 checkNotNull(mpls);
178 rules.add(transitFlow(prev.port(), link, intent,
179 prevLabel, mpls, operation));
180 prevLabel = mpls;
181
182 } else {
183 // Egress traffic
184 rules.add(egressFlow(prev.port(), link, intent,
185 prevLabel, operation));
186 }
187
188 prev = link.dst();
189 }
190 return Lists.newArrayList(new FlowRuleBatchOperation(rules, null, 0));
191 }
192
193 private FlowRuleBatchEntry ingressFlow(PortNumber inPort, Link link,
194 MplsPathIntent intent,
195 MplsLabel label,
196 FlowRuleOperation operation) {
197
198 TrafficSelector.Builder ingressSelector = DefaultTrafficSelector
199 .builder(intent.selector());
200 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
201 ingressSelector.matchInPort(inPort);
202
203 if (intent.ingressLabel().isPresent()) {
204 ingressSelector.matchEthType(Ethernet.MPLS_UNICAST)
205 .matchMplsLabel(intent.ingressLabel().get());
206
207 // Swap the MPLS label
208 treat.setMpls(label.label());
209 } else {
210 // Push and set the MPLS label
211 treat.pushMpls().setMpls(label.label());
212 }
213 // Add the output action
214 treat.setOutput(link.src().port());
215
216 return flowRuleBatchEntry(intent, link.src().deviceId(),
217 ingressSelector.build(), treat.build(),
218 operation);
219 }
220
221 private FlowRuleBatchEntry transitFlow(PortNumber inPort, Link link,
222 MplsPathIntent intent,
223 MplsLabel prevLabel,
224 MplsLabel outLabel,
225 FlowRuleOperation operation) {
226
227 // Ignore the ingress Traffic Selector and use only the MPLS label
228 // assigned in the previous link
229 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
230 selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
231 .matchMplsLabel(prevLabel.label());
232 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
233
234 // Set the new label only if the label on the packet is
235 // different
236 if (prevLabel.equals(outLabel)) {
237 treat.setMpls(outLabel.label());
238 }
239
240 treat.setOutput(link.src().port());
241 return flowRuleBatchEntry(intent, link.src().deviceId(),
242 selector.build(), treat.build(), operation);
243 }
244
245 private FlowRuleBatchEntry egressFlow(PortNumber inPort, Link link,
246 MplsPathIntent intent,
247 MplsLabel prevLabel,
248 FlowRuleOperation operation) {
249 // egress point: either set the egress MPLS label or pop the
250 // MPLS label based on the intent annotations
251
252 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
253 selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
254 .matchMplsLabel(prevLabel.label());
255
256 // apply the intent's treatments
257 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(intent
258 .treatment());
259
260 if (intent.egressLabel().isPresent()) {
261 treat.setMpls(intent.egressLabel().get());
262 } else {
263 // if the ingress ethertype is defined, the egress traffic
264 // will be use that value, otherwise the IPv4 ethertype is used.
265 Criterion c = intent.selector().getCriterion(Type.ETH_TYPE);
266 if (c != null && c instanceof EthTypeCriterion) {
267 EthTypeCriterion ethertype = (EthTypeCriterion) c;
268 treat.popMpls((short) ethertype.ethType());
269 } else {
270 treat.popMpls(Ethernet.TYPE_IPV4);
271 }
272
273 }
274 treat.setOutput(link.src().port());
275 return flowRuleBatchEntry(intent, link.src().deviceId(),
276 selector.build(), treat.build(), operation);
277 }
278
279 protected FlowRuleBatchEntry flowRuleBatchEntry(MplsPathIntent intent,
280 DeviceId deviceId,
281 TrafficSelector selector,
282 TrafficTreatment treat,
283 FlowRuleOperation operation) {
284 FlowRule rule = new DefaultFlowRule(
285 deviceId,
286 selector,
287 treat,
288 123, // FIXME 123
289 appId,
290 0,
291 true);
292 return new FlowRuleBatchEntry(operation, rule, intent.id()
293 .fingerprint());
294
295 }
296}