blob: b3990e1e3dd5938277ce73384ba5396c3204f007 [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;
53import org.onosproject.scalablegateway.api.GatewayNode;
54import org.onosproject.scalablegateway.api.ScalableGatewayService;
55import org.onosproject.store.serializers.KryoNamespaces;
56import org.onosproject.store.service.ConsistentMap;
57import org.onosproject.store.service.Serializer;
58import org.onosproject.store.service.StorageService;
59import org.onosproject.store.service.Versioned;
60import org.slf4j.Logger;
61import org.slf4j.LoggerFactory;
62
63import java.util.Objects;
64import java.util.Optional;
65import java.util.concurrent.ExecutorService;
66
67import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
68import static org.onlab.util.Tools.groupedThreads;
69import static org.onosproject.openstacknetworking.Constants.*;
70import static org.onosproject.openstacknetworking.RulePopulatorUtil.buildExtension;
71import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
72
73
74@Service
75@Component(immediate = true)
sanghoa2d9f492016-08-10 17:08:17 +090076public class OpenstackFloatingIpManager extends AbstractVmHandler implements OpenstackFloatingIpService {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070077
78 private final Logger log = LoggerFactory.getLogger(getClass());
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected CoreService coreService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected HostService hostService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected DeviceService deviceService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected FlowObjectiveService flowObjectiveService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected StorageService storageService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected OpenstackNodeService nodeService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected ScalableGatewayService gatewayService;
100
sanghoa2d9f492016-08-10 17:08:17 +0900101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected OpenstackInterfaceService openstackService;
103
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700104 private static final String NOT_ASSOCIATED = "null";
105 private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER =
106 KryoNamespace.newBuilder().register(KryoNamespaces.API);
107
108 private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
109 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
110 private final InternalNodeListener nodeListener = new InternalNodeListener();
111 private ConsistentMap<IpAddress, Host> floatingIpMap;
112
113 private ApplicationId appId;
114
115 @Activate
116 protected void activate() {
sanghoa2d9f492016-08-10 17:08:17 +0900117 super.activate();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700118 appId = coreService.registerApplication(ROUTING_APP_ID);
119 nodeService.addListener(nodeListener);
120 floatingIpMap = storageService.<IpAddress, Host>consistentMapBuilder()
121 .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
122 .withName("openstackrouting-floatingip")
123 .withApplicationId(appId)
124 .build();
125
126 log.info("Started");
127 }
128
129 @Deactivate
130 protected void deactivate() {
sanghoa2d9f492016-08-10 17:08:17 +0900131 super.deactivate();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700132 nodeService.removeListener(nodeListener);
133 log.info("Stopped");
134 }
135
136 @Override
sanghoa2d9f492016-08-10 17:08:17 +0900137 protected void hostDetected(Host host) {
138 IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
139 Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
140 .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
141 .findFirst();
142 if (floatingIp.isPresent()) {
143 eventExecutor.execute(() -> associateFloatingIp(floatingIp.get()));
144 }
145 }
146
147 @Override
148 protected void hostRemoved(Host host) {
149 IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
150 Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
151 .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
152 .findFirst();
153 if (floatingIp.isPresent()) {
154 eventExecutor.execute(() -> disassociateFloatingIp(floatingIp.get()));
155 }
156 }
157
158 @Override
jskimaa851932016-10-27 17:45:30 +0900159 public void reinstallVmFlow(Host host) {
160 if (host == null) {
161 hostService.getHosts().forEach(h -> {
162 hostDetected(h);
163 log.info("Re-Install data plane flow of virtual machine {}", h);
164 });
165 } else {
166 hostDetected(host);
167 log.info("Re-Install data plane flow of virtual machine {}", host);
168 }
169 }
170
171 @Override
172 public void purgeVmFlow(Host host) {
173 if (host == null) {
174 hostService.getHosts().forEach(h -> {
175 hostRemoved(h);
176 log.info("Purge data plane flow of virtual machine {}", h);
177 });
178 } else {
179 hostRemoved(host);
180 log.info("Purge data plane flow of virtual machine {}", host);
181 }
182 }
183
184 @Override
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700185 public void createFloatingIp(OpenstackFloatingIP floatingIp) {
186 }
187
188 @Override
189 public void updateFloatingIp(OpenstackFloatingIP floatingIp) {
190 if (Strings.isNullOrEmpty(floatingIp.portId()) ||
191 floatingIp.portId().equals(NOT_ASSOCIATED)) {
192 eventExecutor.execute(() -> disassociateFloatingIp(floatingIp));
193 } else {
194 eventExecutor.execute(() -> associateFloatingIp(floatingIp));
195 }
196 }
197
198 @Override
199 public void deleteFloatingIp(String floatingIpId) {
200 }
201
202 private void associateFloatingIp(OpenstackFloatingIP floatingIp) {
203 Optional<Host> associatedVm = Tools.stream(hostService.getHosts())
204 .filter(host -> Objects.equals(
205 host.annotations().value(PORT_ID),
206 floatingIp.portId()))
207 .findAny();
208 if (!associatedVm.isPresent()) {
209 log.warn("Failed to associate floating IP({}) to port:{}",
210 floatingIp.floatingIpAddress(),
211 floatingIp.portId());
212 return;
213 }
214
215 floatingIpMap.put(floatingIp.floatingIpAddress(), associatedVm.get());
216 populateFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.get());
217
218 log.info("Associated floating IP {} to fixed IP {}",
219 floatingIp.floatingIpAddress(), floatingIp.fixedIpAddress());
220 }
221
222 private void disassociateFloatingIp(OpenstackFloatingIP floatingIp) {
223 Versioned<Host> associatedVm = floatingIpMap.remove(floatingIp.floatingIpAddress());
224 if (associatedVm == null) {
225 log.warn("Failed to disassociate floating IP({})",
226 floatingIp.floatingIpAddress());
227 // No VM is actually associated with the floating IP, do nothing
228 return;
229 }
230
231 removeFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.value());
232 log.info("Disassociated floating IP {} from fixed IP {}",
233 floatingIp.floatingIpAddress(),
234 associatedVm.value().ipAddresses());
235 }
236
237 private void populateFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
238 populateFloatingIpIncomingRules(floatingIp, associatedVm);
239 populateFloatingIpOutgoingRules(floatingIp, associatedVm);
240 }
241
242 private void removeFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
243 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
244 if (!fixedIp.isPresent()) {
245 log.warn("Failed to remove floating IP({}) from {}",
246 floatingIp, associatedVm);
247 return;
248 }
249
250 TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder();
251 TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder();
252
253 sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4)
254 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
255 .matchIPSrc(fixedIp.get().toIpPrefix());
256
257 sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4)
258 .matchIPDst(floatingIp.toIpPrefix());
259
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700260 gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
Daniel Park1b0cb262016-09-07 16:31:53 +0900261 TrafficSelector.Builder sForTrafficFromVmBuilder = DefaultTrafficSelector.builder()
262 .matchEthType(Ethernet.TYPE_IPV4)
263 .matchIPDst(floatingIp.toIpPrefix())
264 .matchInPort(nodeService.tunnelPort(deviceId).get());
265
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700266 RulePopulatorUtil.removeRule(
267 flowObjectiveService,
268 appId,
269 deviceId,
270 sOutgoingBuilder.build(),
271 ForwardingObjective.Flag.VERSATILE,
272 FLOATING_RULE_PRIORITY);
273
274 RulePopulatorUtil.removeRule(
275 flowObjectiveService,
276 appId,
277 deviceId,
278 sIncomingBuilder.build(),
279 ForwardingObjective.Flag.VERSATILE,
280 FLOATING_RULE_PRIORITY);
Daniel Park1b0cb262016-09-07 16:31:53 +0900281
282 RulePopulatorUtil.removeRule(
283 flowObjectiveService,
284 appId,
285 deviceId,
286 sForTrafficFromVmBuilder.build(),
287 ForwardingObjective.Flag.VERSATILE,
288 FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700289 });
290 }
291
292 private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) {
293 DeviceId cnodeId = associatedVm.location().deviceId();
294 Optional<IpAddress> dataIp = nodeService.dataIp(cnodeId);
295 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
296
297 if (!fixedIp.isPresent() || !dataIp.isPresent()) {
298 log.warn("Failed to associate floating IP({})", floatingIp);
299 return;
300 }
301
Daniel Park1b0cb262016-09-07 16:31:53 +0900302 TrafficSelector selectorForTrafficFromExternal = DefaultTrafficSelector.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700303 .matchEthType(Ethernet.TYPE_IPV4)
304 .matchIPDst(floatingIp.toIpPrefix())
305 .build();
306
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700307 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
308 TrafficTreatment treatmentForTrafficFromExternal = DefaultTrafficTreatment.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700309 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
310 .setEthDst(associatedVm.mac())
311 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
312 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
sangho4d287732016-08-04 23:24:13 +0900313 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
314 gnodeId)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700315 .setOutput(nodeService.tunnelPort(gnodeId).get())
316 .build();
317
Daniel Park1b0cb262016-09-07 16:31:53 +0900318 ForwardingObjective forwardingObjectiveForTrafficFromExternal = DefaultForwardingObjective.builder()
319 .withSelector(selectorForTrafficFromExternal)
320 .withTreatment(treatmentForTrafficFromExternal)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700321 .withFlag(ForwardingObjective.Flag.VERSATILE)
322 .withPriority(FLOATING_RULE_PRIORITY)
323 .fromApp(appId)
324 .add();
325
Daniel Park1b0cb262016-09-07 16:31:53 +0900326 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromExternal);
327
328
329 TrafficSelector selectorForTrafficFromVm = DefaultTrafficSelector.builder()
330 .matchEthType(Ethernet.TYPE_IPV4)
331 .matchIPDst(floatingIp.toIpPrefix())
332 .matchInPort(nodeService.tunnelPort(gnodeId).get())
333 .build();
334
335 TrafficTreatment treatmentForTrafficFromVm = DefaultTrafficTreatment.builder()
336 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
337 .setEthDst(associatedVm.mac())
338 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
339 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
340 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
341 gnodeId)
342 .setOutput(PortNumber.IN_PORT)
343 .build();
344
345 ForwardingObjective forwardingObjectiveForTrafficFromVm = DefaultForwardingObjective.builder()
346 .withSelector(selectorForTrafficFromVm)
347 .withTreatment(treatmentForTrafficFromVm)
348 .withFlag(ForwardingObjective.Flag.VERSATILE)
349 .withPriority(FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY)
350 .fromApp(appId)
351 .add();
352
353 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromVm);
354
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700355 });
356 }
357
358 private void populateFloatingIpOutgoingRules(IpAddress floatingIp, Host associatedVm) {
359 TrafficSelector selector = DefaultTrafficSelector.builder()
360 .matchEthType(Ethernet.TYPE_IPV4)
361 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
362 .matchIPSrc(associatedVm.ipAddresses().stream().findFirst().get().toIpPrefix())
363 .build();
364
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700365 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700366 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
367 .setIpSrc(floatingIp)
368 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
369 .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
370 .setOutput(gatewayService.getUplinkPort(gnodeId))
371 .build();
372
373 ForwardingObjective fo = DefaultForwardingObjective.builder()
374 .withSelector(selector)
375 .withTreatment(treatment)
376 .withFlag(ForwardingObjective.Flag.VERSATILE)
377 .withPriority(FLOATING_RULE_PRIORITY)
378 .fromApp(appId)
379 .add();
380
381 flowObjectiveService.forward(gnodeId, fo);
382 });
383 }
384
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700385 // TODO consider the case that port with associated floating IP is attached to a VM
386
387 private class InternalNodeListener implements OpenstackNodeListener {
388
389 @Override
390 public void event(OpenstackNodeEvent event) {
391 OpenstackNode node = event.node();
392
393 switch (event.type()) {
394 case COMPLETE:
395 if (node.type() == GATEWAY) {
396 log.info("GATEWAY node {} detected", node.hostname());
Hyunsun Moon5aa480b2016-08-03 12:23:14 -0700397 eventExecutor.execute(() -> {
398 GatewayNode gnode = GatewayNode.builder()
399 .gatewayDeviceId(node.intBridge())
400 .dataIpAddress(node.dataIp().getIp4Address())
401 .uplinkIntf(node.externalPortName().get())
402 .build();
403 gatewayService.addGatewayNode(gnode);
Hyunsun Moon5aa480b2016-08-03 12:23:14 -0700404 });
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700405 }
406 break;
407 case INIT:
408 case DEVICE_CREATED:
409 case INCOMPLETE:
410 default:
411 break;
412 }
413 }
414 }
415}