blob: e6fd6433bbfdeda66c72fc8bf2644a87a5ce63f4 [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;
Hyunsun Moon60a10672016-06-12 17:39:12 -070031import org.opencord.cordvtn.impl.AbstractInstanceHandler;
alshabibb4d31712016-06-01 18:51:03 -070032import org.opencord.cordvtn.api.Instance;
33import org.opencord.cordvtn.api.InstanceHandler;
alshabibb4d31712016-06-01 18:51:03 -070034import 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;
alshabibb4d31712016-06-01 18:51:03 -070053import org.opencord.cordvtn.impl.CordVtnPipeline;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070054
55import java.util.Map;
Hyunsun Moon60a10672016-06-12 17:39:12 -070056import java.util.Optional;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070057import java.util.Set;
58
59import static com.google.common.base.Preconditions.checkNotNull;
60import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
61import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070062import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
63import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
Hyunsun Moon60a10672016-06-12 17:39:12 -070064import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.VSG;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070065
66/**
67 * Provides network connectivity for vSG instances.
68 */
69@Component(immediate = true)
70@Service(value = VsgInstanceHandler.class)
Hyunsun Moon60a10672016-06-12 17:39:12 -070071public final class VsgInstanceHandler extends AbstractInstanceHandler implements InstanceHandler {
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070072
73 private static final String STAG = "stag";
74 private static final String VSG_VM = "vsgVm";
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected FlowRuleService flowRuleService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected CordVtnInstanceManager instanceManager;
81
82 @Activate
83 protected void activate() {
Hyunsun Moon60a10672016-06-12 17:39:12 -070084 serviceType = Optional.of(VSG);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070085 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-vsg", "event-handler"));
86 super.activate();
87 }
88
89 @Deactivate
90 protected void deactivate() {
91 super.deactivate();
92 }
93
94 @Override
95 public void instanceDetected(Instance instance) {
96 if (isVsgContainer(instance)) {
97 log.info("vSG container detected {}", instance);
98
99 // find vsg vm for this vsg container
100 String vsgVmId = instance.getAnnotation(VSG_VM);
101 if (Strings.isNullOrEmpty(vsgVmId)) {
Hyunsun Moon60a10672016-06-12 17:39:12 -0700102 log.warn("Failed to find vSG VM for {}", instance);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700103 return;
104 }
105
106 Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
107 VtnPort vtnPort = getVtnPort(vsgVm);
108 if (vtnPort == null || getStag(vtnPort) == null) {
109 return;
110 }
111
112 populateVsgRules(vsgVm, getStag(vtnPort),
113 nodeManager.dpPort(vsgVm.deviceId()),
114 vtnPort.addressPairs().keySet(),
115 true);
116
117 } else {
118 VtnPort vtnPort = getVtnPort(instance);
119 if (vtnPort == null || getStag(vtnPort) == null) {
120 return;
121 }
122
Hyunsun Moon60a10672016-06-12 17:39:12 -0700123 log.info("vSG VM detected {}", instance);
124
125 // insert vSG containers inside the vSG VM as a host
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700126 vtnPort.addressPairs().entrySet().stream()
127 .forEach(pair -> addVsgContainer(
128 instance,
129 pair.getKey(),
130 pair.getValue(),
131 getStag(vtnPort).toString()
132 ));
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700133 }
134 }
135
136 @Override
137 public void instanceRemoved(Instance instance) {
Hyunsun Moon60a10672016-06-12 17:39:12 -0700138 if (!isVsgContainer(instance)) {
139 // nothing to do for the vSG VM itself
140 return;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700141 }
Hyunsun Moon60a10672016-06-12 17:39:12 -0700142
143 log.info("vSG container vanished {}", instance);
144
145 // find vsg vm for this vsg container
146 String vsgVmId = instance.getAnnotation(VSG_VM);
147 if (Strings.isNullOrEmpty(vsgVmId)) {
148 log.warn("Failed to find vSG VM for {}", instance);
149 return;
150 }
151
152 Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
153 VtnPort vtnPort = getVtnPort(vsgVm);
154 if (vtnPort == null || getStag(vtnPort) == null) {
155 return;
156 }
157
158 populateVsgRules(vsgVm, getStag(vtnPort),
159 nodeManager.dpPort(vsgVm.deviceId()),
160 vtnPort.addressPairs().keySet(),
161 false);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700162 }
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
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700323 // TODO get stag from XOS when XOS provides it, extract if from port name for now
324 private VlanId getStag(VtnPort vtnPort) {
325 checkNotNull(vtnPort);
326
327 String portName = vtnPort.name();
328 if (portName != null && portName.startsWith(STAG)) {
329 return VlanId.vlanId(portName.split("-")[1]);
330 } else {
331 return null;
332 }
333 }
334
335 private PortNumber getOutputFromTreatment(FlowRule flowRule) {
336 Instruction instruction = flowRule.treatment().allInstructions().stream()
337 .filter(inst -> inst instanceof Instructions.OutputInstruction)
338 .findFirst()
339 .orElse(null);
340 if (instruction == null) {
341 return null;
342 }
343 return ((Instructions.OutputInstruction) instruction).port();
344 }
345
346 private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
347 Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
348 if (criterion != null && criterion instanceof IPCriterion) {
349 IPCriterion ip = (IPCriterion) criterion;
350 return ip.ip();
351 } else {
352 return null;
353 }
354 }
355
356 private boolean isVlanPushFromTreatment(FlowRule flowRule) {
357 Instruction instruction = flowRule.treatment().allInstructions().stream()
358 .filter(inst -> inst instanceof L2ModificationInstruction)
359 .filter(inst -> ((L2ModificationInstruction) inst).subtype().equals(VLAN_PUSH))
360 .findAny()
361 .orElse(null);
362 return instruction != null;
363 }
364}