blob: 602b0f751184562ef961b673d1b28dbc7d04b1f7 [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 */
Hyunsun Moon05400872017-02-07 17:11:25 +090016package org.onosproject.openstacknetworking.impl;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070017
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;
Hyunsun Moon05400872017-02-07 17:11:25 +090045import org.onosproject.openstacknetworking.api.Constants;
46import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070047import org.onosproject.openstacknode.OpenstackNode;
48import org.onosproject.openstacknode.OpenstackNodeEvent;
49import org.onosproject.openstacknode.OpenstackNodeListener;
50import org.onosproject.openstacknode.OpenstackNodeService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070051import org.onosproject.scalablegateway.api.ScalableGatewayService;
52import org.onosproject.store.serializers.KryoNamespaces;
53import org.onosproject.store.service.ConsistentMap;
54import org.onosproject.store.service.Serializer;
55import org.onosproject.store.service.StorageService;
56import org.onosproject.store.service.Versioned;
57import org.slf4j.Logger;
58import org.slf4j.LoggerFactory;
59
60import java.util.Objects;
61import java.util.Optional;
62import java.util.concurrent.ExecutorService;
63
64import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
65import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon05400872017-02-07 17:11:25 +090066import static org.onosproject.openstacknetworking.api.Constants.*;
67import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070068
69
70@Service
71@Component(immediate = true)
sanghoa2d9f492016-08-10 17:08:17 +090072public class OpenstackFloatingIpManager extends AbstractVmHandler implements OpenstackFloatingIpService {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070073
74 private final Logger log = LoggerFactory.getLogger(getClass());
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected CoreService coreService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected HostService hostService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected DeviceService deviceService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected FlowObjectiveService flowObjectiveService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected StorageService storageService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected OpenstackNodeService nodeService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected ScalableGatewayService gatewayService;
96
sanghoa2d9f492016-08-10 17:08:17 +090097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected OpenstackInterfaceService openstackService;
99
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700100 private static final String NOT_ASSOCIATED = "null";
101 private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER =
102 KryoNamespace.newBuilder().register(KryoNamespaces.API);
103
104 private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
105 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
106 private final InternalNodeListener nodeListener = new InternalNodeListener();
107 private ConsistentMap<IpAddress, Host> floatingIpMap;
108
109 private ApplicationId appId;
110
111 @Activate
112 protected void activate() {
sanghoa2d9f492016-08-10 17:08:17 +0900113 super.activate();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700114 appId = coreService.registerApplication(ROUTING_APP_ID);
115 nodeService.addListener(nodeListener);
116 floatingIpMap = storageService.<IpAddress, Host>consistentMapBuilder()
117 .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
118 .withName("openstackrouting-floatingip")
119 .withApplicationId(appId)
120 .build();
121
122 log.info("Started");
123 }
124
125 @Deactivate
126 protected void deactivate() {
sanghoa2d9f492016-08-10 17:08:17 +0900127 super.deactivate();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700128 nodeService.removeListener(nodeListener);
129 log.info("Stopped");
130 }
131
132 @Override
sanghoa2d9f492016-08-10 17:08:17 +0900133 protected void hostDetected(Host host) {
134 IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
135 Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
136 .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
137 .findFirst();
138 if (floatingIp.isPresent()) {
139 eventExecutor.execute(() -> associateFloatingIp(floatingIp.get()));
140 }
141 }
142
143 @Override
144 protected void hostRemoved(Host host) {
145 IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
146 Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
147 .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
148 .findFirst();
149 if (floatingIp.isPresent()) {
150 eventExecutor.execute(() -> disassociateFloatingIp(floatingIp.get()));
151 }
152 }
153
154 @Override
jskimaa851932016-10-27 17:45:30 +0900155 public void reinstallVmFlow(Host host) {
156 if (host == null) {
157 hostService.getHosts().forEach(h -> {
158 hostDetected(h);
159 log.info("Re-Install data plane flow of virtual machine {}", h);
160 });
161 } else {
162 hostDetected(host);
163 log.info("Re-Install data plane flow of virtual machine {}", host);
164 }
165 }
166
167 @Override
168 public void purgeVmFlow(Host host) {
169 if (host == null) {
170 hostService.getHosts().forEach(h -> {
171 hostRemoved(h);
172 log.info("Purge data plane flow of virtual machine {}", h);
173 });
174 } else {
175 hostRemoved(host);
176 log.info("Purge data plane flow of virtual machine {}", host);
177 }
178 }
179
180 @Override
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700181 public void createFloatingIp(OpenstackFloatingIP floatingIp) {
182 }
183
184 @Override
185 public void updateFloatingIp(OpenstackFloatingIP floatingIp) {
186 if (Strings.isNullOrEmpty(floatingIp.portId()) ||
187 floatingIp.portId().equals(NOT_ASSOCIATED)) {
188 eventExecutor.execute(() -> disassociateFloatingIp(floatingIp));
189 } else {
190 eventExecutor.execute(() -> associateFloatingIp(floatingIp));
191 }
192 }
193
194 @Override
195 public void deleteFloatingIp(String floatingIpId) {
196 }
197
198 private void associateFloatingIp(OpenstackFloatingIP floatingIp) {
199 Optional<Host> associatedVm = Tools.stream(hostService.getHosts())
200 .filter(host -> Objects.equals(
201 host.annotations().value(PORT_ID),
202 floatingIp.portId()))
203 .findAny();
204 if (!associatedVm.isPresent()) {
205 log.warn("Failed to associate floating IP({}) to port:{}",
206 floatingIp.floatingIpAddress(),
207 floatingIp.portId());
208 return;
209 }
210
211 floatingIpMap.put(floatingIp.floatingIpAddress(), associatedVm.get());
212 populateFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.get());
213
214 log.info("Associated floating IP {} to fixed IP {}",
215 floatingIp.floatingIpAddress(), floatingIp.fixedIpAddress());
216 }
217
218 private void disassociateFloatingIp(OpenstackFloatingIP floatingIp) {
219 Versioned<Host> associatedVm = floatingIpMap.remove(floatingIp.floatingIpAddress());
220 if (associatedVm == null) {
221 log.warn("Failed to disassociate floating IP({})",
222 floatingIp.floatingIpAddress());
223 // No VM is actually associated with the floating IP, do nothing
224 return;
225 }
226
227 removeFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.value());
228 log.info("Disassociated floating IP {} from fixed IP {}",
229 floatingIp.floatingIpAddress(),
230 associatedVm.value().ipAddresses());
231 }
232
233 private void populateFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
234 populateFloatingIpIncomingRules(floatingIp, associatedVm);
235 populateFloatingIpOutgoingRules(floatingIp, associatedVm);
236 }
237
238 private void removeFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
239 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
240 if (!fixedIp.isPresent()) {
241 log.warn("Failed to remove floating IP({}) from {}",
242 floatingIp, associatedVm);
243 return;
244 }
245
246 TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder();
247 TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder();
248
249 sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4)
250 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
251 .matchIPSrc(fixedIp.get().toIpPrefix());
252
253 sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4)
254 .matchIPDst(floatingIp.toIpPrefix());
255
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700256 gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
Daniel Park1b0cb262016-09-07 16:31:53 +0900257 TrafficSelector.Builder sForTrafficFromVmBuilder = DefaultTrafficSelector.builder()
258 .matchEthType(Ethernet.TYPE_IPV4)
259 .matchIPDst(floatingIp.toIpPrefix())
260 .matchInPort(nodeService.tunnelPort(deviceId).get());
261
sanghofb3b5012016-11-10 15:47:53 +0900262 RulePopulatorUtil.setRule(
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700263 flowObjectiveService,
264 appId,
265 deviceId,
266 sOutgoingBuilder.build(),
sanghofb3b5012016-11-10 15:47:53 +0900267 DefaultTrafficTreatment.builder().build(),
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700268 ForwardingObjective.Flag.VERSATILE,
sanghofb3b5012016-11-10 15:47:53 +0900269 FLOATING_RULE_PRIORITY, false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700270
sanghofb3b5012016-11-10 15:47:53 +0900271 RulePopulatorUtil.setRule(
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700272 flowObjectiveService,
273 appId,
274 deviceId,
275 sIncomingBuilder.build(),
sanghofb3b5012016-11-10 15:47:53 +0900276 DefaultTrafficTreatment.builder().build(),
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700277 ForwardingObjective.Flag.VERSATILE,
sanghofb3b5012016-11-10 15:47:53 +0900278 FLOATING_RULE_PRIORITY, false);
Daniel Park1b0cb262016-09-07 16:31:53 +0900279
sanghofb3b5012016-11-10 15:47:53 +0900280 RulePopulatorUtil.setRule(
Daniel Park1b0cb262016-09-07 16:31:53 +0900281 flowObjectiveService,
282 appId,
283 deviceId,
284 sForTrafficFromVmBuilder.build(),
sanghofb3b5012016-11-10 15:47:53 +0900285 DefaultTrafficTreatment.builder().build(),
Daniel Park1b0cb262016-09-07 16:31:53 +0900286 ForwardingObjective.Flag.VERSATILE,
sanghofb3b5012016-11-10 15:47:53 +0900287 FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY, false);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700288 });
289 }
290
291 private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) {
292 DeviceId cnodeId = associatedVm.location().deviceId();
293 Optional<IpAddress> dataIp = nodeService.dataIp(cnodeId);
294 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
295
296 if (!fixedIp.isPresent() || !dataIp.isPresent()) {
297 log.warn("Failed to associate floating IP({})", floatingIp);
298 return;
299 }
300
Daniel Park1b0cb262016-09-07 16:31:53 +0900301 TrafficSelector selectorForTrafficFromExternal = DefaultTrafficSelector.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700302 .matchEthType(Ethernet.TYPE_IPV4)
303 .matchIPDst(floatingIp.toIpPrefix())
304 .build();
305
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700306 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
307 TrafficTreatment treatmentForTrafficFromExternal = DefaultTrafficTreatment.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700308 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
309 .setEthDst(associatedVm.mac())
310 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
311 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
sangho4d287732016-08-04 23:24:13 +0900312 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
313 gnodeId)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700314 .setOutput(nodeService.tunnelPort(gnodeId).get())
315 .build();
316
Daniel Park1b0cb262016-09-07 16:31:53 +0900317 ForwardingObjective forwardingObjectiveForTrafficFromExternal = DefaultForwardingObjective.builder()
318 .withSelector(selectorForTrafficFromExternal)
319 .withTreatment(treatmentForTrafficFromExternal)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700320 .withFlag(ForwardingObjective.Flag.VERSATILE)
321 .withPriority(FLOATING_RULE_PRIORITY)
322 .fromApp(appId)
323 .add();
324
Daniel Park1b0cb262016-09-07 16:31:53 +0900325 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromExternal);
326
327
328 TrafficSelector selectorForTrafficFromVm = DefaultTrafficSelector.builder()
329 .matchEthType(Ethernet.TYPE_IPV4)
330 .matchIPDst(floatingIp.toIpPrefix())
331 .matchInPort(nodeService.tunnelPort(gnodeId).get())
332 .build();
333
334 TrafficTreatment treatmentForTrafficFromVm = DefaultTrafficTreatment.builder()
335 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
336 .setEthDst(associatedVm.mac())
337 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
338 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
339 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
340 gnodeId)
341 .setOutput(PortNumber.IN_PORT)
342 .build();
343
344 ForwardingObjective forwardingObjectiveForTrafficFromVm = DefaultForwardingObjective.builder()
345 .withSelector(selectorForTrafficFromVm)
346 .withTreatment(treatmentForTrafficFromVm)
347 .withFlag(ForwardingObjective.Flag.VERSATILE)
348 .withPriority(FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY)
349 .fromApp(appId)
350 .add();
351
352 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromVm);
353
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700354 });
355 }
356
357 private void populateFloatingIpOutgoingRules(IpAddress floatingIp, Host associatedVm) {
358 TrafficSelector selector = DefaultTrafficSelector.builder()
359 .matchEthType(Ethernet.TYPE_IPV4)
360 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
361 .matchIPSrc(associatedVm.ipAddresses().stream().findFirst().get().toIpPrefix())
362 .build();
363
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700364 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700365 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
366 .setIpSrc(floatingIp)
367 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
368 .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
369 .setOutput(gatewayService.getUplinkPort(gnodeId))
370 .build();
371
372 ForwardingObjective fo = DefaultForwardingObjective.builder()
373 .withSelector(selector)
374 .withTreatment(treatment)
375 .withFlag(ForwardingObjective.Flag.VERSATILE)
376 .withPriority(FLOATING_RULE_PRIORITY)
377 .fromApp(appId)
378 .add();
379
380 flowObjectiveService.forward(gnodeId, fo);
381 });
382 }
383
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700384 // TODO consider the case that port with associated floating IP is attached to a VM
385
386 private class InternalNodeListener implements OpenstackNodeListener {
387
388 @Override
389 public void event(OpenstackNodeEvent event) {
390 OpenstackNode node = event.node();
391
392 switch (event.type()) {
393 case COMPLETE:
sangho9e5f0f52016-12-23 13:59:02 +0900394 reinstallVmFlow(null);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700395 break;
396 case INIT:
397 case DEVICE_CREATED:
398 case INCOMPLETE:
399 default:
400 break;
401 }
402 }
403 }
404}