blob: 702248faa9f6c79e75e8451d0da20611e6db4766 [file] [log] [blame]
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -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 */
16package org.onosproject.openstacknetworking.routing;
17
18import com.google.common.base.Strings;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.packet.Ethernet;
26import org.onlab.packet.IpAddress;
27import org.onlab.util.KryoNamespace;
28import org.onlab.util.Tools;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.Host;
Daniel Park1b0cb262016-09-07 16:31:53 +090033import org.onosproject.net.PortNumber;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070034import org.onosproject.net.device.DeviceService;
35import org.onosproject.net.flow.DefaultTrafficSelector;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.net.flowobjective.DefaultForwardingObjective;
40import org.onosproject.net.flowobjective.FlowObjectiveService;
41import org.onosproject.net.flowobjective.ForwardingObjective;
42import org.onosproject.net.host.HostService;
43import org.onosproject.openstackinterface.OpenstackFloatingIP;
sanghoa2d9f492016-08-10 17:08:17 +090044import org.onosproject.openstackinterface.OpenstackInterfaceService;
45import org.onosproject.openstacknetworking.AbstractVmHandler;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070046import org.onosproject.openstacknetworking.Constants;
47import org.onosproject.openstacknetworking.OpenstackFloatingIpService;
48import org.onosproject.openstacknetworking.RulePopulatorUtil;
49import org.onosproject.openstacknode.OpenstackNode;
50import org.onosproject.openstacknode.OpenstackNodeEvent;
51import org.onosproject.openstacknode.OpenstackNodeListener;
52import org.onosproject.openstacknode.OpenstackNodeService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070053import org.onosproject.scalablegateway.api.ScalableGatewayService;
54import org.onosproject.store.serializers.KryoNamespaces;
55import org.onosproject.store.service.ConsistentMap;
56import org.onosproject.store.service.Serializer;
57import org.onosproject.store.service.StorageService;
58import org.onosproject.store.service.Versioned;
59import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61
62import java.util.Objects;
63import java.util.Optional;
64import java.util.concurrent.ExecutorService;
65
66import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
67import static org.onlab.util.Tools.groupedThreads;
68import static org.onosproject.openstacknetworking.Constants.*;
69import static org.onosproject.openstacknetworking.RulePopulatorUtil.buildExtension;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070070
71
72@Service
73@Component(immediate = true)
sanghoa2d9f492016-08-10 17:08:17 +090074public class OpenstackFloatingIpManager extends AbstractVmHandler implements OpenstackFloatingIpService {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070075
76 private final Logger log = LoggerFactory.getLogger(getClass());
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected CoreService coreService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected HostService hostService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected DeviceService deviceService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected FlowObjectiveService flowObjectiveService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected StorageService storageService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected OpenstackNodeService nodeService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected ScalableGatewayService gatewayService;
98
sanghoa2d9f492016-08-10 17:08:17 +090099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected OpenstackInterfaceService openstackService;
101
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700102 private static final String NOT_ASSOCIATED = "null";
103 private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER =
104 KryoNamespace.newBuilder().register(KryoNamespaces.API);
105
106 private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
107 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
108 private final InternalNodeListener nodeListener = new InternalNodeListener();
109 private ConsistentMap<IpAddress, Host> floatingIpMap;
110
111 private ApplicationId appId;
112
113 @Activate
114 protected void activate() {
sanghoa2d9f492016-08-10 17:08:17 +0900115 super.activate();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700116 appId = coreService.registerApplication(ROUTING_APP_ID);
117 nodeService.addListener(nodeListener);
118 floatingIpMap = storageService.<IpAddress, Host>consistentMapBuilder()
119 .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
120 .withName("openstackrouting-floatingip")
121 .withApplicationId(appId)
122 .build();
123
124 log.info("Started");
125 }
126
127 @Deactivate
128 protected void deactivate() {
sanghoa2d9f492016-08-10 17:08:17 +0900129 super.deactivate();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700130 nodeService.removeListener(nodeListener);
131 log.info("Stopped");
132 }
133
134 @Override
sanghoa2d9f492016-08-10 17:08:17 +0900135 protected void hostDetected(Host host) {
136 IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
137 Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
138 .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
139 .findFirst();
140 if (floatingIp.isPresent()) {
141 eventExecutor.execute(() -> associateFloatingIp(floatingIp.get()));
142 }
143 }
144
145 @Override
146 protected void hostRemoved(Host host) {
147 IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
148 Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
149 .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
150 .findFirst();
151 if (floatingIp.isPresent()) {
152 eventExecutor.execute(() -> disassociateFloatingIp(floatingIp.get()));
153 }
154 }
155
156 @Override
jskimaa851932016-10-27 17:45:30 +0900157 public void reinstallVmFlow(Host host) {
158 if (host == null) {
159 hostService.getHosts().forEach(h -> {
160 hostDetected(h);
161 log.info("Re-Install data plane flow of virtual machine {}", h);
162 });
163 } else {
164 hostDetected(host);
165 log.info("Re-Install data plane flow of virtual machine {}", host);
166 }
167 }
168
169 @Override
170 public void purgeVmFlow(Host host) {
171 if (host == null) {
172 hostService.getHosts().forEach(h -> {
173 hostRemoved(h);
174 log.info("Purge data plane flow of virtual machine {}", h);
175 });
176 } else {
177 hostRemoved(host);
178 log.info("Purge data plane flow of virtual machine {}", host);
179 }
180 }
181
182 @Override
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700183 public void createFloatingIp(OpenstackFloatingIP floatingIp) {
184 }
185
186 @Override
187 public void updateFloatingIp(OpenstackFloatingIP floatingIp) {
188 if (Strings.isNullOrEmpty(floatingIp.portId()) ||
189 floatingIp.portId().equals(NOT_ASSOCIATED)) {
190 eventExecutor.execute(() -> disassociateFloatingIp(floatingIp));
191 } else {
192 eventExecutor.execute(() -> associateFloatingIp(floatingIp));
193 }
194 }
195
196 @Override
197 public void deleteFloatingIp(String floatingIpId) {
198 }
199
200 private void associateFloatingIp(OpenstackFloatingIP floatingIp) {
201 Optional<Host> associatedVm = Tools.stream(hostService.getHosts())
202 .filter(host -> Objects.equals(
203 host.annotations().value(PORT_ID),
204 floatingIp.portId()))
205 .findAny();
206 if (!associatedVm.isPresent()) {
207 log.warn("Failed to associate floating IP({}) to port:{}",
208 floatingIp.floatingIpAddress(),
209 floatingIp.portId());
210 return;
211 }
212
213 floatingIpMap.put(floatingIp.floatingIpAddress(), associatedVm.get());
214 populateFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.get());
215
216 log.info("Associated floating IP {} to fixed IP {}",
217 floatingIp.floatingIpAddress(), floatingIp.fixedIpAddress());
218 }
219
220 private void disassociateFloatingIp(OpenstackFloatingIP floatingIp) {
221 Versioned<Host> associatedVm = floatingIpMap.remove(floatingIp.floatingIpAddress());
222 if (associatedVm == null) {
223 log.warn("Failed to disassociate floating IP({})",
224 floatingIp.floatingIpAddress());
225 // No VM is actually associated with the floating IP, do nothing
226 return;
227 }
228
229 removeFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.value());
230 log.info("Disassociated floating IP {} from fixed IP {}",
231 floatingIp.floatingIpAddress(),
232 associatedVm.value().ipAddresses());
233 }
234
235 private void populateFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
236 populateFloatingIpIncomingRules(floatingIp, associatedVm);
237 populateFloatingIpOutgoingRules(floatingIp, associatedVm);
238 }
239
240 private void removeFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
241 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
242 if (!fixedIp.isPresent()) {
243 log.warn("Failed to remove floating IP({}) from {}",
244 floatingIp, associatedVm);
245 return;
246 }
247
248 TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder();
249 TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder();
250
251 sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4)
252 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
253 .matchIPSrc(fixedIp.get().toIpPrefix());
254
255 sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4)
256 .matchIPDst(floatingIp.toIpPrefix());
257
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700258 gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
Daniel Park1b0cb262016-09-07 16:31:53 +0900259 TrafficSelector.Builder sForTrafficFromVmBuilder = DefaultTrafficSelector.builder()
260 .matchEthType(Ethernet.TYPE_IPV4)
261 .matchIPDst(floatingIp.toIpPrefix())
262 .matchInPort(nodeService.tunnelPort(deviceId).get());
263
sanghofb3b5012016-11-10 15:47:53 +0900264 RulePopulatorUtil.setRule(
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700265 flowObjectiveService,
266 appId,
267 deviceId,
268 sOutgoingBuilder.build(),
sanghofb3b5012016-11-10 15:47:53 +0900269 DefaultTrafficTreatment.builder().build(),
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700270 ForwardingObjective.Flag.VERSATILE,
sanghofb3b5012016-11-10 15:47:53 +0900271 FLOATING_RULE_PRIORITY, false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700272
sanghofb3b5012016-11-10 15:47:53 +0900273 RulePopulatorUtil.setRule(
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700274 flowObjectiveService,
275 appId,
276 deviceId,
277 sIncomingBuilder.build(),
sanghofb3b5012016-11-10 15:47:53 +0900278 DefaultTrafficTreatment.builder().build(),
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700279 ForwardingObjective.Flag.VERSATILE,
sanghofb3b5012016-11-10 15:47:53 +0900280 FLOATING_RULE_PRIORITY, false);
Daniel Park1b0cb262016-09-07 16:31:53 +0900281
sanghofb3b5012016-11-10 15:47:53 +0900282 RulePopulatorUtil.setRule(
Daniel Park1b0cb262016-09-07 16:31:53 +0900283 flowObjectiveService,
284 appId,
285 deviceId,
286 sForTrafficFromVmBuilder.build(),
sanghofb3b5012016-11-10 15:47:53 +0900287 DefaultTrafficTreatment.builder().build(),
Daniel Park1b0cb262016-09-07 16:31:53 +0900288 ForwardingObjective.Flag.VERSATILE,
sanghofb3b5012016-11-10 15:47:53 +0900289 FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY, false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700290 });
291 }
292
293 private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) {
294 DeviceId cnodeId = associatedVm.location().deviceId();
295 Optional<IpAddress> dataIp = nodeService.dataIp(cnodeId);
296 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
297
298 if (!fixedIp.isPresent() || !dataIp.isPresent()) {
299 log.warn("Failed to associate floating IP({})", floatingIp);
300 return;
301 }
302
Daniel Park1b0cb262016-09-07 16:31:53 +0900303 TrafficSelector selectorForTrafficFromExternal = DefaultTrafficSelector.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700304 .matchEthType(Ethernet.TYPE_IPV4)
305 .matchIPDst(floatingIp.toIpPrefix())
306 .build();
307
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700308 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
309 TrafficTreatment treatmentForTrafficFromExternal = DefaultTrafficTreatment.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700310 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
311 .setEthDst(associatedVm.mac())
312 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
313 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
sangho4d287732016-08-04 23:24:13 +0900314 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
315 gnodeId)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700316 .setOutput(nodeService.tunnelPort(gnodeId).get())
317 .build();
318
Daniel Park1b0cb262016-09-07 16:31:53 +0900319 ForwardingObjective forwardingObjectiveForTrafficFromExternal = DefaultForwardingObjective.builder()
320 .withSelector(selectorForTrafficFromExternal)
321 .withTreatment(treatmentForTrafficFromExternal)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700322 .withFlag(ForwardingObjective.Flag.VERSATILE)
323 .withPriority(FLOATING_RULE_PRIORITY)
324 .fromApp(appId)
325 .add();
326
Daniel Park1b0cb262016-09-07 16:31:53 +0900327 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromExternal);
328
329
330 TrafficSelector selectorForTrafficFromVm = DefaultTrafficSelector.builder()
331 .matchEthType(Ethernet.TYPE_IPV4)
332 .matchIPDst(floatingIp.toIpPrefix())
333 .matchInPort(nodeService.tunnelPort(gnodeId).get())
334 .build();
335
336 TrafficTreatment treatmentForTrafficFromVm = DefaultTrafficTreatment.builder()
337 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
338 .setEthDst(associatedVm.mac())
339 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
340 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
341 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
342 gnodeId)
343 .setOutput(PortNumber.IN_PORT)
344 .build();
345
346 ForwardingObjective forwardingObjectiveForTrafficFromVm = DefaultForwardingObjective.builder()
347 .withSelector(selectorForTrafficFromVm)
348 .withTreatment(treatmentForTrafficFromVm)
349 .withFlag(ForwardingObjective.Flag.VERSATILE)
350 .withPriority(FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY)
351 .fromApp(appId)
352 .add();
353
354 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromVm);
355
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700356 });
357 }
358
359 private void populateFloatingIpOutgoingRules(IpAddress floatingIp, Host associatedVm) {
360 TrafficSelector selector = DefaultTrafficSelector.builder()
361 .matchEthType(Ethernet.TYPE_IPV4)
362 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
363 .matchIPSrc(associatedVm.ipAddresses().stream().findFirst().get().toIpPrefix())
364 .build();
365
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700366 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700367 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
368 .setIpSrc(floatingIp)
369 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
370 .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
371 .setOutput(gatewayService.getUplinkPort(gnodeId))
372 .build();
373
374 ForwardingObjective fo = DefaultForwardingObjective.builder()
375 .withSelector(selector)
376 .withTreatment(treatment)
377 .withFlag(ForwardingObjective.Flag.VERSATILE)
378 .withPriority(FLOATING_RULE_PRIORITY)
379 .fromApp(appId)
380 .add();
381
382 flowObjectiveService.forward(gnodeId, fo);
383 });
384 }
385
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700386 // TODO consider the case that port with associated floating IP is attached to a VM
387
388 private class InternalNodeListener implements OpenstackNodeListener {
389
390 @Override
391 public void event(OpenstackNodeEvent event) {
392 OpenstackNode node = event.node();
393
394 switch (event.type()) {
395 case COMPLETE:
sangho9e5f0f52016-12-23 13:59:02 +0900396 reinstallVmFlow(null);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700397 break;
398 case INIT:
399 case DEVICE_CREATED:
400 case INCOMPLETE:
401 default:
402 break;
403 }
404 }
405 }
406}