blob: 46c4a011debed1db2c3f0bb2d88b1a1de1d26843 [file] [log] [blame]
Hyunsun Moone7e4bb32016-05-16 04:32:45 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
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 */
alshabibb4d31712016-06-01 18:51:03 -070016package org.opencord.cordvtn.impl.service;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070017
18import com.google.common.base.Strings;
19import com.google.common.collect.Sets;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.IpPrefix;
29import org.onlab.packet.MacAddress;
30import org.onlab.packet.VlanId;
alshabibb4d31712016-06-01 18:51:03 -070031import org.opencord.cordvtn.api.Instance;
32import org.opencord.cordvtn.api.InstanceHandler;
33import org.opencord.cordvtn.impl.CordVtnInstanceHandler;
34import org.opencord.cordvtn.impl.CordVtnInstanceManager;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070035import org.onosproject.net.DefaultAnnotations;
36import org.onosproject.net.HostId;
37import org.onosproject.net.PortNumber;
38import org.onosproject.net.flow.DefaultFlowRule;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.FlowRule;
42import org.onosproject.net.flow.FlowRuleService;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.flow.criteria.Criterion;
46import org.onosproject.net.flow.criteria.IPCriterion;
47import org.onosproject.net.flow.instructions.Instruction;
48import org.onosproject.net.flow.instructions.Instructions;
49import org.onosproject.net.flow.instructions.L2ModificationInstruction;
50import org.onosproject.net.host.DefaultHostDescription;
51import org.onosproject.net.host.HostDescription;
52import org.onosproject.xosclient.api.VtnPort;
53import org.onosproject.xosclient.api.VtnPortApi;
54import org.onosproject.xosclient.api.VtnPortId;
55import org.onosproject.xosclient.api.VtnService;
alshabibb4d31712016-06-01 18:51:03 -070056import org.opencord.cordvtn.impl.CordVtnPipeline;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070057
58import java.util.Map;
59import java.util.Set;
60
61import static com.google.common.base.Preconditions.checkNotNull;
62import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
63import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070064import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
65import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
66
67/**
68 * Provides network connectivity for vSG instances.
69 */
70@Component(immediate = true)
71@Service(value = VsgInstanceHandler.class)
72public final class VsgInstanceHandler extends CordVtnInstanceHandler implements InstanceHandler {
73
74 private static final String STAG = "stag";
75 private static final String VSG_VM = "vsgVm";
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected FlowRuleService flowRuleService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected CordVtnInstanceManager instanceManager;
82
83 @Activate
84 protected void activate() {
85 serviceType = VtnService.ServiceType.VSG;
86 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-vsg", "event-handler"));
87 super.activate();
88 }
89
90 @Deactivate
91 protected void deactivate() {
92 super.deactivate();
93 }
94
95 @Override
96 public void instanceDetected(Instance instance) {
97 if (isVsgContainer(instance)) {
98 log.info("vSG container detected {}", instance);
99
100 // find vsg vm for this vsg container
101 String vsgVmId = instance.getAnnotation(VSG_VM);
102 if (Strings.isNullOrEmpty(vsgVmId)) {
103 log.warn("Failed to find VSG VM for {}", instance);
104 return;
105 }
106
107 Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
108 VtnPort vtnPort = getVtnPort(vsgVm);
109 if (vtnPort == null || getStag(vtnPort) == null) {
110 return;
111 }
112
113 populateVsgRules(vsgVm, getStag(vtnPort),
114 nodeManager.dpPort(vsgVm.deviceId()),
115 vtnPort.addressPairs().keySet(),
116 true);
117
118 } else {
119 VtnPort vtnPort = getVtnPort(instance);
120 if (vtnPort == null || getStag(vtnPort) == null) {
121 return;
122 }
123
124 vtnPort.addressPairs().entrySet().stream()
125 .forEach(pair -> addVsgContainer(
126 instance,
127 pair.getKey(),
128 pair.getValue(),
129 getStag(vtnPort).toString()
130 ));
131 super.instanceDetected(instance);
132 }
133 }
134
135 @Override
136 public void instanceRemoved(Instance instance) {
137 if (isVsgContainer(instance)) {
138 log.info("vSG container vanished {}", instance);
139
140 // find vsg vm for this vsg container
141 String vsgVmId = instance.getAnnotation(VSG_VM);
142 if (Strings.isNullOrEmpty(vsgVmId)) {
143 log.warn("Failed to find VSG VM for {}", instance);
144 return;
145 }
146
147 Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
148 VtnPort vtnPort = getVtnPort(vsgVm);
149 if (vtnPort == null || getStag(vtnPort) == null) {
150 return;
151 }
152
153 populateVsgRules(vsgVm, getStag(vtnPort),
154 nodeManager.dpPort(vsgVm.deviceId()),
155 vtnPort.addressPairs().keySet(),
156 false);
157
158 } else {
159 // TODO remove vsg vm related rules
160 super.instanceRemoved(instance);
161 }
162 }
163
164 /**
165 * Updates set of vSGs in a given vSG VM.
166 *
167 * @param vsgVmId vsg vm host id
168 * @param stag stag
169 * @param vsgInstances full set of vsg wan ip and mac address pairs in this vsg vm
170 */
171 public void updateVsgInstances(HostId vsgVmId, String stag, Map<IpAddress, MacAddress> vsgInstances) {
172 if (hostService.getHost(vsgVmId) == null) {
173 log.debug("vSG VM {} is not added yet, ignore this update", vsgVmId);
174 return;
175 }
176
177 Instance vsgVm = Instance.of(hostService.getHost(vsgVmId));
178 if (vsgVm == null) {
179 log.warn("Failed to find existing vSG VM for STAG: {}", stag);
180 return;
181 }
182
183 log.info("Updates vSGs in {} with STAG: {}", vsgVm, stag);
184
185 // adds vSGs in the address pair
186 vsgInstances.entrySet().stream()
187 .filter(addr -> hostService.getHostsByMac(addr.getValue()).isEmpty())
188 .forEach(addr -> addVsgContainer(
189 vsgVm,
190 addr.getKey(),
191 addr.getValue(),
192 stag));
193
194 // removes vSGs not listed in the address pair
195 hostService.getConnectedHosts(vsgVm.host().location()).stream()
196 .filter(host -> !host.mac().equals(vsgVm.mac()))
197 .filter(host -> !vsgInstances.values().contains(host.mac()))
198 .forEach(host -> {
199 log.info("Removed vSG {}", host.toString());
200 instanceManager.removeInstance(host.id());
201 });
202 }
203
204 private boolean isVsgContainer(Instance instance) {
205 return !Strings.isNullOrEmpty(instance.host().annotations().value(STAG));
206 }
207
208 private void addVsgContainer(Instance vsgVm, IpAddress vsgWanIp, MacAddress vsgMac,
209 String stag) {
210 HostId hostId = HostId.hostId(vsgMac);
211 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
alshabibb4d31712016-06-01 18:51:03 -0700212 .set(Instance.SERVICE_TYPE, vsgVm.serviceType().toString())
213 .set(Instance.SERVICE_ID, vsgVm.serviceId().id())
214 .set(Instance.PORT_ID, vsgVm.portId().id())
215 .set(Instance.NESTED_INSTANCE, Instance.TRUE)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700216 .set(STAG, stag)
217 .set(VSG_VM, vsgVm.host().id().toString())
alshabibb4d31712016-06-01 18:51:03 -0700218 .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700219
220 HostDescription hostDesc = new DefaultHostDescription(
221 vsgMac,
222 VlanId.NONE,
223 vsgVm.host().location(),
224 Sets.newHashSet(vsgWanIp),
225 annotations.build());
226
227 instanceManager.addInstance(hostId, hostDesc);
228 }
229
230 private void populateVsgRules(Instance vsgVm, VlanId stag, PortNumber dpPort,
231 Set<IpAddress> vsgWanIps, boolean install) {
232 // for traffics with s-tag, strip the tag and take through the vSG VM
233 TrafficSelector selector = DefaultTrafficSelector.builder()
234 .matchInPort(dpPort)
235 .matchVlanId(stag)
236 .build();
237
238 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
239 .setOutput(vsgVm.portNumber())
240 .build();
241
242 FlowRule flowRule = DefaultFlowRule.builder()
243 .fromApp(appId)
244 .withSelector(selector)
245 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700246 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700247 .forDevice(vsgVm.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700248 .forTable(CordVtnPipeline.TABLE_VLAN)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700249 .makePermanent()
250 .build();
251
252 pipeline.processFlowRule(install, flowRule);
253
254 // for traffics with customer vlan, tag with the service vlan based on input port with
255 // lower priority to avoid conflict with WAN tag
256 selector = DefaultTrafficSelector.builder()
257 .matchInPort(vsgVm.portNumber())
258 .matchVlanId(stag)
259 .build();
260
261 treatment = DefaultTrafficTreatment.builder()
262 .setOutput(dpPort)
263 .build();
264
265 flowRule = DefaultFlowRule.builder()
266 .fromApp(appId)
267 .withSelector(selector)
268 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700269 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700270 .forDevice(vsgVm.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700271 .forTable(CordVtnPipeline.TABLE_VLAN)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700272 .makePermanent()
273 .build();
274
275 pipeline.processFlowRule(install, flowRule);
276
277 // for traffic coming from WAN, tag 500 and take through the vSG VM
278 // based on destination ip
279 vsgWanIps.stream().forEach(ip -> {
280 TrafficSelector downstream = DefaultTrafficSelector.builder()
281 .matchEthType(Ethernet.TYPE_IPV4)
282 .matchIPDst(ip.toIpPrefix())
283 .build();
284
285 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
286 .pushVlan()
alshabibb4d31712016-06-01 18:51:03 -0700287 .setVlanId(CordVtnPipeline.VLAN_WAN)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700288 .setEthDst(vsgVm.mac())
289 .setOutput(vsgVm.portNumber())
290 .build();
291
292 FlowRule downstreamFlowRule = DefaultFlowRule.builder()
293 .fromApp(appId)
294 .withSelector(downstream)
295 .withTreatment(downstreamTreatment)
alshabibb4d31712016-06-01 18:51:03 -0700296 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700297 .forDevice(vsgVm.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700298 .forTable(CordVtnPipeline.TABLE_DST_IP)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700299 .makePermanent()
300 .build();
301
302 pipeline.processFlowRule(install, downstreamFlowRule);
303 });
304
305 // remove downstream flow rules for the vSG not shown in vsgWanIps
306 for (FlowRule rule : flowRuleService.getFlowRulesById(appId)) {
307 if (!rule.deviceId().equals(vsgVm.deviceId())) {
308 continue;
309 }
310 PortNumber output = getOutputFromTreatment(rule);
311 if (output == null || !output.equals(vsgVm.portNumber()) ||
312 !isVlanPushFromTreatment(rule)) {
313 continue;
314 }
315
316 IpPrefix dstIp = getDstIpFromSelector(rule);
317 if (dstIp != null && !vsgWanIps.contains(dstIp.address())) {
318 pipeline.processFlowRule(false, rule);
319 }
320 }
321 }
322
323 private VtnPort getVtnPort(Instance instance) {
324 checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
325 checkNotNull(xosAccess, XOS_ACCESS_ERROR);
326
327 VtnPortId vtnPortId = instance.portId();
328 VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
329 VtnPort vtnPort = portApi.vtnPort(vtnPortId, osAccess);
330 if (vtnPort == null) {
331 log.warn("Failed to get port information of {}", instance);
332 return null;
333 }
334 return vtnPort;
335 }
336
337 // TODO get stag from XOS when XOS provides it, extract if from port name for now
338 private VlanId getStag(VtnPort vtnPort) {
339 checkNotNull(vtnPort);
340
341 String portName = vtnPort.name();
342 if (portName != null && portName.startsWith(STAG)) {
343 return VlanId.vlanId(portName.split("-")[1]);
344 } else {
345 return null;
346 }
347 }
348
349 private PortNumber getOutputFromTreatment(FlowRule flowRule) {
350 Instruction instruction = flowRule.treatment().allInstructions().stream()
351 .filter(inst -> inst instanceof Instructions.OutputInstruction)
352 .findFirst()
353 .orElse(null);
354 if (instruction == null) {
355 return null;
356 }
357 return ((Instructions.OutputInstruction) instruction).port();
358 }
359
360 private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
361 Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
362 if (criterion != null && criterion instanceof IPCriterion) {
363 IPCriterion ip = (IPCriterion) criterion;
364 return ip.ip();
365 } else {
366 return null;
367 }
368 }
369
370 private boolean isVlanPushFromTreatment(FlowRule flowRule) {
371 Instruction instruction = flowRule.treatment().allInstructions().stream()
372 .filter(inst -> inst instanceof L2ModificationInstruction)
373 .filter(inst -> ((L2ModificationInstruction) inst).subtype().equals(VLAN_PUSH))
374 .findAny()
375 .orElse(null);
376 return instruction != null;
377 }
378}