blob: 7b7f778785cf8aad7fd70fb624e661c6e6947cbf [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
sanghofb3b5012016-11-10 15:47:53 +0900266 RulePopulatorUtil.setRule(
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700267 flowObjectiveService,
268 appId,
269 deviceId,
270 sOutgoingBuilder.build(),
sanghofb3b5012016-11-10 15:47:53 +0900271 DefaultTrafficTreatment.builder().build(),
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700272 ForwardingObjective.Flag.VERSATILE,
sanghofb3b5012016-11-10 15:47:53 +0900273 FLOATING_RULE_PRIORITY, false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700274
sanghofb3b5012016-11-10 15:47:53 +0900275 RulePopulatorUtil.setRule(
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700276 flowObjectiveService,
277 appId,
278 deviceId,
279 sIncomingBuilder.build(),
sanghofb3b5012016-11-10 15:47:53 +0900280 DefaultTrafficTreatment.builder().build(),
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700281 ForwardingObjective.Flag.VERSATILE,
sanghofb3b5012016-11-10 15:47:53 +0900282 FLOATING_RULE_PRIORITY, false);
Daniel Park1b0cb262016-09-07 16:31:53 +0900283
sanghofb3b5012016-11-10 15:47:53 +0900284 RulePopulatorUtil.setRule(
Daniel Park1b0cb262016-09-07 16:31:53 +0900285 flowObjectiveService,
286 appId,
287 deviceId,
288 sForTrafficFromVmBuilder.build(),
sanghofb3b5012016-11-10 15:47:53 +0900289 DefaultTrafficTreatment.builder().build(),
Daniel Park1b0cb262016-09-07 16:31:53 +0900290 ForwardingObjective.Flag.VERSATILE,
sanghofb3b5012016-11-10 15:47:53 +0900291 FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY, false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700292 });
293 }
294
295 private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) {
296 DeviceId cnodeId = associatedVm.location().deviceId();
297 Optional<IpAddress> dataIp = nodeService.dataIp(cnodeId);
298 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
299
300 if (!fixedIp.isPresent() || !dataIp.isPresent()) {
301 log.warn("Failed to associate floating IP({})", floatingIp);
302 return;
303 }
304
Daniel Park1b0cb262016-09-07 16:31:53 +0900305 TrafficSelector selectorForTrafficFromExternal = DefaultTrafficSelector.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700306 .matchEthType(Ethernet.TYPE_IPV4)
307 .matchIPDst(floatingIp.toIpPrefix())
308 .build();
309
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700310 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
311 TrafficTreatment treatmentForTrafficFromExternal = DefaultTrafficTreatment.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700312 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
313 .setEthDst(associatedVm.mac())
314 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
315 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
sangho4d287732016-08-04 23:24:13 +0900316 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
317 gnodeId)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700318 .setOutput(nodeService.tunnelPort(gnodeId).get())
319 .build();
320
Daniel Park1b0cb262016-09-07 16:31:53 +0900321 ForwardingObjective forwardingObjectiveForTrafficFromExternal = DefaultForwardingObjective.builder()
322 .withSelector(selectorForTrafficFromExternal)
323 .withTreatment(treatmentForTrafficFromExternal)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700324 .withFlag(ForwardingObjective.Flag.VERSATILE)
325 .withPriority(FLOATING_RULE_PRIORITY)
326 .fromApp(appId)
327 .add();
328
Daniel Park1b0cb262016-09-07 16:31:53 +0900329 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromExternal);
330
331
332 TrafficSelector selectorForTrafficFromVm = DefaultTrafficSelector.builder()
333 .matchEthType(Ethernet.TYPE_IPV4)
334 .matchIPDst(floatingIp.toIpPrefix())
335 .matchInPort(nodeService.tunnelPort(gnodeId).get())
336 .build();
337
338 TrafficTreatment treatmentForTrafficFromVm = DefaultTrafficTreatment.builder()
339 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
340 .setEthDst(associatedVm.mac())
341 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
342 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
343 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
344 gnodeId)
345 .setOutput(PortNumber.IN_PORT)
346 .build();
347
348 ForwardingObjective forwardingObjectiveForTrafficFromVm = DefaultForwardingObjective.builder()
349 .withSelector(selectorForTrafficFromVm)
350 .withTreatment(treatmentForTrafficFromVm)
351 .withFlag(ForwardingObjective.Flag.VERSATILE)
352 .withPriority(FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY)
353 .fromApp(appId)
354 .add();
355
356 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromVm);
357
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700358 });
359 }
360
361 private void populateFloatingIpOutgoingRules(IpAddress floatingIp, Host associatedVm) {
362 TrafficSelector selector = DefaultTrafficSelector.builder()
363 .matchEthType(Ethernet.TYPE_IPV4)
364 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
365 .matchIPSrc(associatedVm.ipAddresses().stream().findFirst().get().toIpPrefix())
366 .build();
367
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700368 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700369 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
370 .setIpSrc(floatingIp)
371 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
372 .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
373 .setOutput(gatewayService.getUplinkPort(gnodeId))
374 .build();
375
376 ForwardingObjective fo = DefaultForwardingObjective.builder()
377 .withSelector(selector)
378 .withTreatment(treatment)
379 .withFlag(ForwardingObjective.Flag.VERSATILE)
380 .withPriority(FLOATING_RULE_PRIORITY)
381 .fromApp(appId)
382 .add();
383
384 flowObjectiveService.forward(gnodeId, fo);
385 });
386 }
387
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700388 // TODO consider the case that port with associated floating IP is attached to a VM
389
390 private class InternalNodeListener implements OpenstackNodeListener {
391
392 @Override
393 public void event(OpenstackNodeEvent event) {
394 OpenstackNode node = event.node();
395
396 switch (event.type()) {
397 case COMPLETE:
398 if (node.type() == GATEWAY) {
399 log.info("GATEWAY node {} detected", node.hostname());
Hyunsun Moon5aa480b2016-08-03 12:23:14 -0700400 eventExecutor.execute(() -> {
401 GatewayNode gnode = GatewayNode.builder()
402 .gatewayDeviceId(node.intBridge())
403 .dataIpAddress(node.dataIp().getIp4Address())
404 .uplinkIntf(node.externalPortName().get())
405 .build();
406 gatewayService.addGatewayNode(gnode);
Hyunsun Moon5aa480b2016-08-03 12:23:14 -0700407 });
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700408 }
409 break;
410 case INIT:
411 case DEVICE_CREATED:
412 case INCOMPLETE:
413 default:
414 break;
415 }
416 }
417 }
418}