blob: 2e218bd4528b04d539da22fb0a96154d34b4462c [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;
33import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.flowobjective.DefaultForwardingObjective;
39import org.onosproject.net.flowobjective.FlowObjectiveService;
40import org.onosproject.net.flowobjective.ForwardingObjective;
41import org.onosproject.net.host.HostService;
42import org.onosproject.openstackinterface.OpenstackFloatingIP;
sanghoa2d9f492016-08-10 17:08:17 +090043import org.onosproject.openstackinterface.OpenstackInterfaceService;
44import org.onosproject.openstacknetworking.AbstractVmHandler;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070045import org.onosproject.openstacknetworking.Constants;
46import org.onosproject.openstacknetworking.OpenstackFloatingIpService;
47import org.onosproject.openstacknetworking.RulePopulatorUtil;
48import org.onosproject.openstacknode.OpenstackNode;
49import org.onosproject.openstacknode.OpenstackNodeEvent;
50import org.onosproject.openstacknode.OpenstackNodeListener;
51import org.onosproject.openstacknode.OpenstackNodeService;
52import org.onosproject.scalablegateway.api.GatewayNode;
53import 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;
70import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
71
72
73@Service
74@Component(immediate = true)
sanghoa2d9f492016-08-10 17:08:17 +090075public class OpenstackFloatingIpManager extends AbstractVmHandler implements OpenstackFloatingIpService {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070076
77 private final Logger log = LoggerFactory.getLogger(getClass());
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected CoreService coreService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected HostService hostService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected DeviceService deviceService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected FlowObjectiveService flowObjectiveService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected StorageService storageService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected OpenstackNodeService nodeService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected ScalableGatewayService gatewayService;
99
sanghoa2d9f492016-08-10 17:08:17 +0900100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected OpenstackInterfaceService openstackService;
102
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700103 private static final String NOT_ASSOCIATED = "null";
104 private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER =
105 KryoNamespace.newBuilder().register(KryoNamespaces.API);
106
107 private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
108 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
109 private final InternalNodeListener nodeListener = new InternalNodeListener();
110 private ConsistentMap<IpAddress, Host> floatingIpMap;
111
112 private ApplicationId appId;
113
114 @Activate
115 protected void activate() {
sanghoa2d9f492016-08-10 17:08:17 +0900116 super.activate();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700117 appId = coreService.registerApplication(ROUTING_APP_ID);
118 nodeService.addListener(nodeListener);
119 floatingIpMap = storageService.<IpAddress, Host>consistentMapBuilder()
120 .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
121 .withName("openstackrouting-floatingip")
122 .withApplicationId(appId)
123 .build();
124
125 log.info("Started");
126 }
127
128 @Deactivate
129 protected void deactivate() {
sanghoa2d9f492016-08-10 17:08:17 +0900130 super.deactivate();
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700131 nodeService.removeListener(nodeListener);
132 log.info("Stopped");
133 }
134
135 @Override
sanghoa2d9f492016-08-10 17:08:17 +0900136 protected void hostDetected(Host host) {
137 IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
138 Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
139 .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
140 .findFirst();
141 if (floatingIp.isPresent()) {
142 eventExecutor.execute(() -> associateFloatingIp(floatingIp.get()));
143 }
144 }
145
146 @Override
147 protected void hostRemoved(Host host) {
148 IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
149 Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
150 .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
151 .findFirst();
152 if (floatingIp.isPresent()) {
153 eventExecutor.execute(() -> disassociateFloatingIp(floatingIp.get()));
154 }
155 }
156
157 @Override
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700158 public void createFloatingIp(OpenstackFloatingIP floatingIp) {
159 }
160
161 @Override
162 public void updateFloatingIp(OpenstackFloatingIP floatingIp) {
163 if (Strings.isNullOrEmpty(floatingIp.portId()) ||
164 floatingIp.portId().equals(NOT_ASSOCIATED)) {
165 eventExecutor.execute(() -> disassociateFloatingIp(floatingIp));
166 } else {
167 eventExecutor.execute(() -> associateFloatingIp(floatingIp));
168 }
169 }
170
171 @Override
172 public void deleteFloatingIp(String floatingIpId) {
173 }
174
175 private void associateFloatingIp(OpenstackFloatingIP floatingIp) {
176 Optional<Host> associatedVm = Tools.stream(hostService.getHosts())
177 .filter(host -> Objects.equals(
178 host.annotations().value(PORT_ID),
179 floatingIp.portId()))
180 .findAny();
181 if (!associatedVm.isPresent()) {
182 log.warn("Failed to associate floating IP({}) to port:{}",
183 floatingIp.floatingIpAddress(),
184 floatingIp.portId());
185 return;
186 }
187
188 floatingIpMap.put(floatingIp.floatingIpAddress(), associatedVm.get());
189 populateFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.get());
190
191 log.info("Associated floating IP {} to fixed IP {}",
192 floatingIp.floatingIpAddress(), floatingIp.fixedIpAddress());
193 }
194
195 private void disassociateFloatingIp(OpenstackFloatingIP floatingIp) {
196 Versioned<Host> associatedVm = floatingIpMap.remove(floatingIp.floatingIpAddress());
197 if (associatedVm == null) {
198 log.warn("Failed to disassociate floating IP({})",
199 floatingIp.floatingIpAddress());
200 // No VM is actually associated with the floating IP, do nothing
201 return;
202 }
203
204 removeFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.value());
205 log.info("Disassociated floating IP {} from fixed IP {}",
206 floatingIp.floatingIpAddress(),
207 associatedVm.value().ipAddresses());
208 }
209
210 private void populateFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
211 populateFloatingIpIncomingRules(floatingIp, associatedVm);
212 populateFloatingIpOutgoingRules(floatingIp, associatedVm);
213 }
214
215 private void removeFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
216 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
217 if (!fixedIp.isPresent()) {
218 log.warn("Failed to remove floating IP({}) from {}",
219 floatingIp, associatedVm);
220 return;
221 }
222
223 TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder();
224 TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder();
225
226 sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4)
227 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
228 .matchIPSrc(fixedIp.get().toIpPrefix());
229
230 sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4)
231 .matchIPDst(floatingIp.toIpPrefix());
232
233 gatewayService.getGatewayDeviceIds().stream().forEach(deviceId -> {
234 RulePopulatorUtil.removeRule(
235 flowObjectiveService,
236 appId,
237 deviceId,
238 sOutgoingBuilder.build(),
239 ForwardingObjective.Flag.VERSATILE,
240 FLOATING_RULE_PRIORITY);
241
242 RulePopulatorUtil.removeRule(
243 flowObjectiveService,
244 appId,
245 deviceId,
246 sIncomingBuilder.build(),
247 ForwardingObjective.Flag.VERSATILE,
248 FLOATING_RULE_PRIORITY);
249 });
250 }
251
252 private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) {
253 DeviceId cnodeId = associatedVm.location().deviceId();
254 Optional<IpAddress> dataIp = nodeService.dataIp(cnodeId);
255 Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
256
257 if (!fixedIp.isPresent() || !dataIp.isPresent()) {
258 log.warn("Failed to associate floating IP({})", floatingIp);
259 return;
260 }
261
262 TrafficSelector selector = DefaultTrafficSelector.builder()
263 .matchEthType(Ethernet.TYPE_IPV4)
264 .matchIPDst(floatingIp.toIpPrefix())
265 .build();
266
267 gatewayService.getGatewayDeviceIds().stream().forEach(gnodeId -> {
268 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
269 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
270 .setEthDst(associatedVm.mac())
271 .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
272 .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
sangho4d287732016-08-04 23:24:13 +0900273 .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
274 gnodeId)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700275 .setOutput(nodeService.tunnelPort(gnodeId).get())
276 .build();
277
278 ForwardingObjective fo = DefaultForwardingObjective.builder()
279 .withSelector(selector)
280 .withTreatment(treatment)
281 .withFlag(ForwardingObjective.Flag.VERSATILE)
282 .withPriority(FLOATING_RULE_PRIORITY)
283 .fromApp(appId)
284 .add();
285
286 flowObjectiveService.forward(gnodeId, fo);
287 });
288 }
289
290 private void populateFloatingIpOutgoingRules(IpAddress floatingIp, Host associatedVm) {
291 TrafficSelector selector = DefaultTrafficSelector.builder()
292 .matchEthType(Ethernet.TYPE_IPV4)
293 .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
294 .matchIPSrc(associatedVm.ipAddresses().stream().findFirst().get().toIpPrefix())
295 .build();
296
297 gatewayService.getGatewayDeviceIds().stream().forEach(gnodeId -> {
298 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
299 .setIpSrc(floatingIp)
300 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
301 .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
302 .setOutput(gatewayService.getUplinkPort(gnodeId))
303 .build();
304
305 ForwardingObjective fo = DefaultForwardingObjective.builder()
306 .withSelector(selector)
307 .withTreatment(treatment)
308 .withFlag(ForwardingObjective.Flag.VERSATILE)
309 .withPriority(FLOATING_RULE_PRIORITY)
310 .fromApp(appId)
311 .add();
312
313 flowObjectiveService.forward(gnodeId, fo);
314 });
315 }
316
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700317 // TODO consider the case that port with associated floating IP is attached to a VM
318
319 private class InternalNodeListener implements OpenstackNodeListener {
320
321 @Override
322 public void event(OpenstackNodeEvent event) {
323 OpenstackNode node = event.node();
324
325 switch (event.type()) {
326 case COMPLETE:
327 if (node.type() == GATEWAY) {
328 log.info("GATEWAY node {} detected", node.hostname());
Hyunsun Moon5aa480b2016-08-03 12:23:14 -0700329 eventExecutor.execute(() -> {
330 GatewayNode gnode = GatewayNode.builder()
331 .gatewayDeviceId(node.intBridge())
332 .dataIpAddress(node.dataIp().getIp4Address())
333 .uplinkIntf(node.externalPortName().get())
334 .build();
335 gatewayService.addGatewayNode(gnode);
Hyunsun Moon5aa480b2016-08-03 12:23:14 -0700336 });
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700337 }
338 break;
339 case INIT:
340 case DEVICE_CREATED:
341 case INCOMPLETE:
342 default:
343 break;
344 }
345 }
346 }
347}