blob: bd12b5bb7af6734cb8b85eac02c4b2a3d43e5dc1 [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
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700159 public void createFloatingIp(OpenstackFloatingIP floatingIp) {
160 }
161
162 @Override
163 public void updateFloatingIp(OpenstackFloatingIP floatingIp) {
164 if (Strings.isNullOrEmpty(floatingIp.portId()) ||
165 floatingIp.portId().equals(NOT_ASSOCIATED)) {
166 eventExecutor.execute(() -> disassociateFloatingIp(floatingIp));
167 } else {
168 eventExecutor.execute(() -> associateFloatingIp(floatingIp));
169 }
170 }
171
172 @Override
173 public void deleteFloatingIp(String floatingIpId) {
174 }
175
176 private void associateFloatingIp(OpenstackFloatingIP floatingIp) {
177 Optional<Host> associatedVm = Tools.stream(hostService.getHosts())
178 .filter(host -> Objects.equals(
179 host.annotations().value(PORT_ID),
180 floatingIp.portId()))
181 .findAny();
182 if (!associatedVm.isPresent()) {
183 log.warn("Failed to associate floating IP({}) to port:{}",
184 floatingIp.floatingIpAddress(),
185 floatingIp.portId());
186 return;
187 }
188
189 floatingIpMap.put(floatingIp.floatingIpAddress(), associatedVm.get());
190 populateFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.get());
191
192 log.info("Associated floating IP {} to fixed IP {}",
193 floatingIp.floatingIpAddress(), floatingIp.fixedIpAddress());
194 }
195
196 private void disassociateFloatingIp(OpenstackFloatingIP floatingIp) {
197 Versioned<Host> associatedVm = floatingIpMap.remove(floatingIp.floatingIpAddress());
198 if (associatedVm == null) {
199 log.warn("Failed to disassociate floating IP({})",
200 floatingIp.floatingIpAddress());
201 // No VM is actually associated with the floating IP, do nothing
202 return;
203 }
204
205 removeFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.value());
206 log.info("Disassociated floating IP {} from fixed IP {}",
207 floatingIp.floatingIpAddress(),
208 associatedVm.value().ipAddresses());
209 }
210
211 private void populateFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
212 populateFloatingIpIncomingRules(floatingIp, associatedVm);
213 populateFloatingIpOutgoingRules(floatingIp, associatedVm);
214 }
215
216 private void removeFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
217 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
218 if (!fixedIp.isPresent()) {
219 log.warn("Failed to remove floating IP({}) from {}",
220 floatingIp, associatedVm);
221 return;
222 }
223
224 TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder();
225 TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder();
226
227 sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4)
228 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
229 .matchIPSrc(fixedIp.get().toIpPrefix());
230
231 sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4)
232 .matchIPDst(floatingIp.toIpPrefix());
233
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700234 gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
Daniel Park1b0cb262016-09-07 16:31:53 +0900235 TrafficSelector.Builder sForTrafficFromVmBuilder = DefaultTrafficSelector.builder()
236 .matchEthType(Ethernet.TYPE_IPV4)
237 .matchIPDst(floatingIp.toIpPrefix())
238 .matchInPort(nodeService.tunnelPort(deviceId).get());
239
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700240 RulePopulatorUtil.removeRule(
241 flowObjectiveService,
242 appId,
243 deviceId,
244 sOutgoingBuilder.build(),
245 ForwardingObjective.Flag.VERSATILE,
246 FLOATING_RULE_PRIORITY);
247
248 RulePopulatorUtil.removeRule(
249 flowObjectiveService,
250 appId,
251 deviceId,
252 sIncomingBuilder.build(),
253 ForwardingObjective.Flag.VERSATILE,
254 FLOATING_RULE_PRIORITY);
Daniel Park1b0cb262016-09-07 16:31:53 +0900255
256 RulePopulatorUtil.removeRule(
257 flowObjectiveService,
258 appId,
259 deviceId,
260 sForTrafficFromVmBuilder.build(),
261 ForwardingObjective.Flag.VERSATILE,
262 FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY);
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700263 });
264 }
265
266 private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) {
267 DeviceId cnodeId = associatedVm.location().deviceId();
268 Optional<IpAddress> dataIp = nodeService.dataIp(cnodeId);
269 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
270
271 if (!fixedIp.isPresent() || !dataIp.isPresent()) {
272 log.warn("Failed to associate floating IP({})", floatingIp);
273 return;
274 }
275
Daniel Park1b0cb262016-09-07 16:31:53 +0900276 TrafficSelector selectorForTrafficFromExternal = DefaultTrafficSelector.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700277 .matchEthType(Ethernet.TYPE_IPV4)
278 .matchIPDst(floatingIp.toIpPrefix())
279 .build();
280
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700281 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
282 TrafficTreatment treatmentForTrafficFromExternal = DefaultTrafficTreatment.builder()
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700283 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
284 .setEthDst(associatedVm.mac())
285 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
286 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
sangho4d287732016-08-04 23:24:13 +0900287 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
288 gnodeId)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700289 .setOutput(nodeService.tunnelPort(gnodeId).get())
290 .build();
291
Daniel Park1b0cb262016-09-07 16:31:53 +0900292 ForwardingObjective forwardingObjectiveForTrafficFromExternal = DefaultForwardingObjective.builder()
293 .withSelector(selectorForTrafficFromExternal)
294 .withTreatment(treatmentForTrafficFromExternal)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700295 .withFlag(ForwardingObjective.Flag.VERSATILE)
296 .withPriority(FLOATING_RULE_PRIORITY)
297 .fromApp(appId)
298 .add();
299
Daniel Park1b0cb262016-09-07 16:31:53 +0900300 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromExternal);
301
302
303 TrafficSelector selectorForTrafficFromVm = DefaultTrafficSelector.builder()
304 .matchEthType(Ethernet.TYPE_IPV4)
305 .matchIPDst(floatingIp.toIpPrefix())
306 .matchInPort(nodeService.tunnelPort(gnodeId).get())
307 .build();
308
309 TrafficTreatment treatmentForTrafficFromVm = DefaultTrafficTreatment.builder()
310 .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)))
314 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
315 gnodeId)
316 .setOutput(PortNumber.IN_PORT)
317 .build();
318
319 ForwardingObjective forwardingObjectiveForTrafficFromVm = DefaultForwardingObjective.builder()
320 .withSelector(selectorForTrafficFromVm)
321 .withTreatment(treatmentForTrafficFromVm)
322 .withFlag(ForwardingObjective.Flag.VERSATILE)
323 .withPriority(FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY)
324 .fromApp(appId)
325 .add();
326
327 flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromVm);
328
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700329 });
330 }
331
332 private void populateFloatingIpOutgoingRules(IpAddress floatingIp, Host associatedVm) {
333 TrafficSelector selector = DefaultTrafficSelector.builder()
334 .matchEthType(Ethernet.TYPE_IPV4)
335 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
336 .matchIPSrc(associatedVm.ipAddresses().stream().findFirst().get().toIpPrefix())
337 .build();
338
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700339 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700340 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
341 .setIpSrc(floatingIp)
342 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
343 .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
344 .setOutput(gatewayService.getUplinkPort(gnodeId))
345 .build();
346
347 ForwardingObjective fo = DefaultForwardingObjective.builder()
348 .withSelector(selector)
349 .withTreatment(treatment)
350 .withFlag(ForwardingObjective.Flag.VERSATILE)
351 .withPriority(FLOATING_RULE_PRIORITY)
352 .fromApp(appId)
353 .add();
354
355 flowObjectiveService.forward(gnodeId, fo);
356 });
357 }
358
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700359 // TODO consider the case that port with associated floating IP is attached to a VM
360
361 private class InternalNodeListener implements OpenstackNodeListener {
362
363 @Override
364 public void event(OpenstackNodeEvent event) {
365 OpenstackNode node = event.node();
366
367 switch (event.type()) {
368 case COMPLETE:
369 if (node.type() == GATEWAY) {
370 log.info("GATEWAY node {} detected", node.hostname());
Hyunsun Moon5aa480b2016-08-03 12:23:14 -0700371 eventExecutor.execute(() -> {
372 GatewayNode gnode = GatewayNode.builder()
373 .gatewayDeviceId(node.intBridge())
374 .dataIpAddress(node.dataIp().getIp4Address())
375 .uplinkIntf(node.externalPortName().get())
376 .build();
377 gatewayService.addGatewayNode(gnode);
Hyunsun Moon5aa480b2016-08-03 12:23:14 -0700378 });
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700379 }
380 break;
381 case INIT:
382 case DEVICE_CREATED:
383 case INCOMPLETE:
384 default:
385 break;
386 }
387 }
388 }
389}