blob: 6f1cc4b30fcea738aa07190ee588f4902f591504 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
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.impl;
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.onlab.packet.Ethernet;
25import org.onlab.packet.IpAddress;
26import org.onlab.packet.MacAddress;
daniel parkee8700b2017-05-11 15:50:03 +090027import org.onlab.packet.VlanId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090028import org.onosproject.cluster.ClusterService;
29import org.onosproject.cluster.LeadershipService;
30import org.onosproject.cluster.NodeId;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.net.PortNumber;
34import 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.FlowObjectiveService;
40import org.onosproject.net.flowobjective.ForwardingObjective;
41import org.onosproject.openstacknetworking.api.Constants;
42import org.onosproject.openstacknetworking.api.InstancePort;
43import org.onosproject.openstacknetworking.api.InstancePortService;
44import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
45import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
46import org.onosproject.openstacknetworking.api.OpenstackRouterService;
47import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090048import org.onosproject.openstacknode.OpenstackNodeEvent;
49import org.onosproject.openstacknode.OpenstackNodeListener;
50import org.onosproject.openstacknode.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090051import org.openstack4j.model.network.NetFloatingIP;
52import org.openstack4j.model.network.Network;
daniel parkee8700b2017-05-11 15:50:03 +090053import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090054import org.openstack4j.model.network.Port;
55import org.slf4j.Logger;
56import org.slf4j.LoggerFactory;
57
58import java.util.Objects;
59import java.util.Optional;
60import java.util.concurrent.ExecutorService;
61
62import static java.util.concurrent.Executors.newSingleThreadExecutor;
63import static org.onlab.util.Tools.groupedThreads;
64import static org.onosproject.openstacknetworking.api.Constants.*;
65import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
66import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
67
68/**
69 * Handles OpenStack floating IP events.
70 */
71@Component(immediate = true)
72public class OpenstackRoutingFloatingIpHandler {
73
74 private final Logger log = LoggerFactory.getLogger(getClass());
75
76 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
daniel parkee8700b2017-05-11 15:50:03 +090077 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
Hyunsun Moon44aac662017-02-18 02:07:01 +090078
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected CoreService coreService;
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 LeadershipService leadershipService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected ClusterService clusterService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected OpenstackNodeService osNodeService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected InstancePortService instancePortService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected OpenstackRouterService osRouterService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected OpenstackNetworkService osNetworkService;
105
Hyunsun Moon44aac662017-02-18 02:07:01 +0900106 private final ExecutorService eventExecutor = newSingleThreadExecutor(
107 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
108 private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpLisener();
109 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
110
111 private ApplicationId appId;
112 private NodeId localNodeId;
113
114 @Activate
115 protected void activate() {
116 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
117 localNodeId = clusterService.getLocalNode().id();
118 leadershipService.runForLeadership(appId.name());
119 osRouterService.addListener(floatingIpLisener);
120 osNodeService.addListener(osNodeListener);
121
122 log.info("Started");
123 }
124
125 @Deactivate
126 protected void deactivate() {
127 osNodeService.removeListener(osNodeListener);
128 osRouterService.removeListener(floatingIpLisener);
129 leadershipService.withdraw(appId.name());
130 eventExecutor.shutdown();
131
132 log.info("Stopped");
133 }
134
Hyunsun Moon44aac662017-02-18 02:07:01 +0900135 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
136 boolean install) {
137 Network osNet = osNetworkService.network(osPort.getNetworkId());
138 if (osNet == null) {
139 final String error = String.format(ERR_FLOW + "no network(%s) exists",
140 floatingIp.getFloatingIpAddress(),
141 osPort.getNetworkId());
142 throw new IllegalStateException(error);
143 }
144
145 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
146 InstancePort instPort = instancePortService.instancePort(srcMac);
147 if (instPort == null) {
148 final String error = String.format(ERR_FLOW + "no host(MAC:%s) found",
149 floatingIp.getFloatingIpAddress(), srcMac);
150 throw new IllegalStateException(error);
151 }
152
153 setDownstreamRules(floatingIp, osNet, instPort, install);
154 setUpstreamRules(floatingIp, osNet, instPort, install);
155 }
156
157 private void setDownstreamRules(NetFloatingIP floatingIp, Network osNet,
158 InstancePort instPort, boolean install) {
159 Optional<IpAddress> dataIp = osNodeService.dataIp(instPort.deviceId());
160 if (!dataIp.isPresent()) {
161 log.warn(ERR_FLOW + "compute node {} is not ready",
162 floatingIp, instPort.deviceId());
163 return;
164 }
165
166 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
167 TrafficSelector externalSelector = DefaultTrafficSelector.builder()
168 .matchEthType(Ethernet.TYPE_IPV4)
169 .matchIPDst(floating.toIpPrefix())
170 .build();
171
daniel parke49eb382017-04-05 16:48:28 +0900172 osNodeService.gatewayDeviceIds().forEach(gnodeId -> {
daniel parkee8700b2017-05-11 15:50:03 +0900173 TrafficTreatment.Builder externalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900174 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
175 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900176 .setIpDst(instPort.ipAddress().getIp4Address());
177
178 switch (osNet.getNetworkType()) {
179 case VXLAN:
180 externalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
181 .extension(buildExtension(
182 deviceService,
183 gnodeId,
184 dataIp.get().getIp4Address()),
185 gnodeId)
186 .setOutput(osNodeService.tunnelPort(gnodeId).get());
187 break;
188 case VLAN:
189 externalBuilder.pushVlan()
190 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
191 .setOutput(osNodeService.vlanPort(gnodeId).get());
192 break;
193 default:
194 final String error = String.format(
195 ERR_UNSUPPORTED_NET_TYPE + "%s",
196 osNet.getNetworkType().toString());
197 throw new IllegalStateException(error);
198 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900199
200 RulePopulatorUtil.setRule(
201 flowObjectiveService,
202 appId,
203 gnodeId,
204 externalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900205 externalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900206 ForwardingObjective.Flag.VERSATILE,
207 PRIORITY_FLOATING_EXTERNAL,
208 install);
209
210 // access from one VM to the other via floating IP
211 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
212 .matchEthType(Ethernet.TYPE_IPV4)
213 .matchIPDst(floating.toIpPrefix())
214 .matchInPort(osNodeService.tunnelPort(gnodeId).get())
215 .build();
216
daniel parkee8700b2017-05-11 15:50:03 +0900217 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900218 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
219 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900220 .setIpDst(instPort.ipAddress().getIp4Address());
221
222 switch (osNet.getNetworkType()) {
223 case VXLAN:
224 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
225 .extension(buildExtension(
226 deviceService,
227 gnodeId,
228 dataIp.get().getIp4Address()),
229 gnodeId)
230 .setOutput(PortNumber.IN_PORT);
231 break;
232 case VLAN:
233 internalBuilder.pushVlan()
234 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
235 .setOutput(PortNumber.IN_PORT);
236 break;
237 default:
238 final String error = String.format(
239 ERR_UNSUPPORTED_NET_TYPE + "%s",
240 osNet.getNetworkType().toString());
241 throw new IllegalStateException(error);
242 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900243
244 RulePopulatorUtil.setRule(
245 flowObjectiveService,
246 appId,
247 gnodeId,
248 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900249 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900250 ForwardingObjective.Flag.VERSATILE,
251 PRIORITY_FLOATING_INTERNAL,
252 install);
253 });
254 }
255
256 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
257 InstancePort instPort, boolean install) {
258 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900259
260 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900261 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900262 .matchIPSrc(instPort.ipAddress().toIpPrefix());
263
264 switch (osNet.getNetworkType()) {
265 case VXLAN:
266 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
267 break;
268 case VLAN:
269 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
270 break;
271 default:
272 final String error = String.format(
273 ERR_UNSUPPORTED_NET_TYPE + "%s",
274 osNet.getNetworkType().toString());
275 throw new IllegalStateException(error);
276 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900277
daniel parke49eb382017-04-05 16:48:28 +0900278 osNodeService.gatewayDeviceIds().forEach(gnodeId -> {
daniel parkee8700b2017-05-11 15:50:03 +0900279 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900280 .setIpSrc(floating.getIp4Address())
281 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
daniel parkee8700b2017-05-11 15:50:03 +0900282 .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC);
283
284 if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
285 tBuilder.popVlan();
286 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900287
288 RulePopulatorUtil.setRule(
289 flowObjectiveService,
290 appId,
291 gnodeId,
daniel parkee8700b2017-05-11 15:50:03 +0900292 sBuilder.build(),
293 tBuilder.setOutput(osNodeService.externalPort(gnodeId).get()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900294 ForwardingObjective.Flag.VERSATILE,
295 PRIORITY_FLOATING_EXTERNAL,
296 install);
297 });
298 }
299
300 private class InternalFloatingIpLisener implements OpenstackRouterListener {
301
302 @Override
303 public boolean isRelevant(OpenstackRouterEvent event) {
304 // do not allow to proceed without leadership
305 NodeId leader = leadershipService.getLeader(appId.name());
306 if (!Objects.equals(localNodeId, leader)) {
307 return false;
308 }
309 return event.floatingIp() != null;
310 }
311
312 @Override
313 public void event(OpenstackRouterEvent event) {
314 switch (event.type()) {
315 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900316 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900317 NetFloatingIP osFip = event.floatingIp();
318 associateFloatingIp(osFip);
319 log.info("Associated floating IP {}:{}",
320 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900321 });
322 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900323 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
324 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900325 NetFloatingIP osFip = event.floatingIp();
326 disassociateFloatingIp(osFip, event.portId());
327 log.info("Disassociated floating IP {}:{}",
328 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900329 });
330 break;
331 case OPENSTACK_FLOATING_IP_REMOVED:
332 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900333 NetFloatingIP osFip = event.floatingIp();
334 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
335 disassociateFloatingIp(osFip, osFip.getPortId());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900336 }
Hyunsun Moonb720e632017-05-16 15:41:36 +0900337 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900338 });
339 break;
340 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900341 eventExecutor.execute(() -> {
342 NetFloatingIP osFip = event.floatingIp();
343 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
344 associateFloatingIp(event.floatingIp());
345 }
346 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
347 });
348 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900349 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900350 case OPENSTACK_ROUTER_CREATED:
351 case OPENSTACK_ROUTER_UPDATED:
352 case OPENSTACK_ROUTER_REMOVED:
353 case OPENSTACK_ROUTER_INTERFACE_ADDED:
354 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
355 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
356 default:
357 // do nothing for the other events
358 break;
359 }
360 }
Hyunsun Moonb720e632017-05-16 15:41:36 +0900361
362 private void associateFloatingIp(NetFloatingIP osFip) {
363 Port osPort = osNetworkService.port(osFip.getPortId());
364 if (osPort == null) {
365 final String error = String.format(ERR_FLOW + "port(%s) not found",
366 osFip.getFloatingIpAddress(), osFip.getPortId());
367 throw new IllegalStateException(error);
368 }
369 // set floating IP rules only if the port is associated to a VM
370 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
371 setFloatingIpRules(osFip, osPort, true);
372 }
373 }
374
375 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
376 Port osPort = osNetworkService.port(portId);
377 if (osPort == null) {
378 // FIXME when a port with floating IP removed without
379 // disassociation step, it can reach here
380 return;
381 }
382 // set floating IP rules only if the port is associated to a VM
383 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
384 setFloatingIpRules(osFip, osPort, false);
385 }
386 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900387 }
388
389 private class InternalNodeListener implements OpenstackNodeListener {
390
391 @Override
392 public boolean isRelevant(OpenstackNodeEvent event) {
393 // do not allow to proceed without leadership
394 NodeId leader = leadershipService.getLeader(appId.name());
395 if (!Objects.equals(localNodeId, leader)) {
396 return false;
397 }
398 return event.subject().type() == GATEWAY;
399 }
400
401 @Override
402 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900403
404 switch (event.type()) {
405 case COMPLETE:
406 eventExecutor.execute(() -> {
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900407 for (NetFloatingIP fip : osRouterService.floatingIps()) {
408 if (Strings.isNullOrEmpty(fip.getPortId())) {
409 continue;
410 }
411 Port osPort = osNetworkService.port(fip.getPortId());
412 if (osPort == null) {
413 log.warn("Failed to set floating IP {}", fip.getId());
414 continue;
415 }
416 setFloatingIpRules(fip, osPort, true);
417 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900418 });
419 break;
420 case INIT:
421 case DEVICE_CREATED:
422 case INCOMPLETE:
423 default:
424 break;
425 }
426 }
427 }
428}