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