blob: f1fdf292319e18de158b86b3d30e6018d2954e88 [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;
Hyunsun Moon44aac662017-02-18 02:07:01 +090039import org.onosproject.openstacknetworking.api.Constants;
40import org.onosproject.openstacknetworking.api.InstancePort;
41import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090042import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
43import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090044import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
45import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
46import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090047import org.onosproject.openstacknode.OpenstackNodeEvent;
48import org.onosproject.openstacknode.OpenstackNodeListener;
49import org.onosproject.openstacknode.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090050import org.openstack4j.model.network.NetFloatingIP;
51import org.openstack4j.model.network.Network;
daniel parkee8700b2017-05-11 15:50:03 +090052import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090053import org.openstack4j.model.network.Port;
54import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
57import java.util.Objects;
58import java.util.Optional;
59import java.util.concurrent.ExecutorService;
60
61import static java.util.concurrent.Executors.newSingleThreadExecutor;
62import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090063import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
64import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
65import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
66import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_INTERNAL;
Hyunsun Moon44aac662017-02-18 02:07:01 +090067import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
68import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
69
70/**
71 * Handles OpenStack floating IP events.
72 */
73@Component(immediate = true)
74public class OpenstackRoutingFloatingIpHandler {
75
76 private final Logger log = LoggerFactory.getLogger(getClass());
77
78 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
daniel parkee8700b2017-05-11 15:50:03 +090079 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
Hyunsun Moon44aac662017-02-18 02:07:01 +090080
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected CoreService coreService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected DeviceService deviceService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +090088 protected LeadershipService leadershipService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected ClusterService clusterService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected OpenstackNodeService osNodeService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected InstancePortService instancePortService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected OpenstackRouterService osRouterService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected OpenstackNetworkService osNetworkService;
104
sanghodc375372017-06-08 10:41:30 +0900105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected OpenstackFlowRuleService osFlowRuleService;
107
Hyunsun Moon44aac662017-02-18 02:07:01 +0900108 private final ExecutorService eventExecutor = newSingleThreadExecutor(
109 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
110 private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpLisener();
111 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
112
113 private ApplicationId appId;
114 private NodeId localNodeId;
115
116 @Activate
117 protected void activate() {
118 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
119 localNodeId = clusterService.getLocalNode().id();
120 leadershipService.runForLeadership(appId.name());
121 osRouterService.addListener(floatingIpLisener);
122 osNodeService.addListener(osNodeListener);
123
124 log.info("Started");
125 }
126
127 @Deactivate
128 protected void deactivate() {
129 osNodeService.removeListener(osNodeListener);
130 osRouterService.removeListener(floatingIpLisener);
131 leadershipService.withdraw(appId.name());
132 eventExecutor.shutdown();
133
134 log.info("Stopped");
135 }
136
Hyunsun Moon44aac662017-02-18 02:07:01 +0900137 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
138 boolean install) {
139 Network osNet = osNetworkService.network(osPort.getNetworkId());
140 if (osNet == null) {
141 final String error = String.format(ERR_FLOW + "no network(%s) exists",
142 floatingIp.getFloatingIpAddress(),
143 osPort.getNetworkId());
144 throw new IllegalStateException(error);
145 }
146
147 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
148 InstancePort instPort = instancePortService.instancePort(srcMac);
149 if (instPort == null) {
150 final String error = String.format(ERR_FLOW + "no host(MAC:%s) found",
151 floatingIp.getFloatingIpAddress(), srcMac);
152 throw new IllegalStateException(error);
153 }
154
155 setDownstreamRules(floatingIp, osNet, instPort, install);
156 setUpstreamRules(floatingIp, osNet, instPort, install);
157 }
158
159 private void setDownstreamRules(NetFloatingIP floatingIp, Network osNet,
160 InstancePort instPort, boolean install) {
161 Optional<IpAddress> dataIp = osNodeService.dataIp(instPort.deviceId());
162 if (!dataIp.isPresent()) {
163 log.warn(ERR_FLOW + "compute node {} is not ready",
164 floatingIp, instPort.deviceId());
165 return;
166 }
167
168 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
169 TrafficSelector externalSelector = DefaultTrafficSelector.builder()
170 .matchEthType(Ethernet.TYPE_IPV4)
171 .matchIPDst(floating.toIpPrefix())
172 .build();
173
daniel parke49eb382017-04-05 16:48:28 +0900174 osNodeService.gatewayDeviceIds().forEach(gnodeId -> {
daniel parkee8700b2017-05-11 15:50:03 +0900175 TrafficTreatment.Builder externalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900176 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
177 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900178 .setIpDst(instPort.ipAddress().getIp4Address());
179
180 switch (osNet.getNetworkType()) {
181 case VXLAN:
182 externalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
183 .extension(buildExtension(
184 deviceService,
185 gnodeId,
186 dataIp.get().getIp4Address()),
187 gnodeId)
188 .setOutput(osNodeService.tunnelPort(gnodeId).get());
189 break;
190 case VLAN:
191 externalBuilder.pushVlan()
192 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
193 .setOutput(osNodeService.vlanPort(gnodeId).get());
194 break;
195 default:
196 final String error = String.format(
197 ERR_UNSUPPORTED_NET_TYPE + "%s",
198 osNet.getNetworkType().toString());
199 throw new IllegalStateException(error);
200 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900201
sanghodc375372017-06-08 10:41:30 +0900202 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900203 appId,
204 gnodeId,
205 externalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900206 externalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900207 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900208 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900209 install);
210
211 // access from one VM to the other via floating IP
212 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
213 .matchEthType(Ethernet.TYPE_IPV4)
214 .matchIPDst(floating.toIpPrefix())
215 .matchInPort(osNodeService.tunnelPort(gnodeId).get())
216 .build();
217
daniel parkee8700b2017-05-11 15:50:03 +0900218 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900219 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
220 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900221 .setIpDst(instPort.ipAddress().getIp4Address());
222
223 switch (osNet.getNetworkType()) {
224 case VXLAN:
225 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
226 .extension(buildExtension(
227 deviceService,
228 gnodeId,
229 dataIp.get().getIp4Address()),
230 gnodeId)
231 .setOutput(PortNumber.IN_PORT);
232 break;
233 case VLAN:
234 internalBuilder.pushVlan()
235 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
236 .setOutput(PortNumber.IN_PORT);
237 break;
238 default:
239 final String error = String.format(
240 ERR_UNSUPPORTED_NET_TYPE + "%s",
241 osNet.getNetworkType().toString());
242 throw new IllegalStateException(error);
243 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900244
sanghodc375372017-06-08 10:41:30 +0900245 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900246 appId,
247 gnodeId,
248 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900249 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900250 PRIORITY_FLOATING_INTERNAL,
sanghodc375372017-06-08 10:41:30 +0900251 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900252 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
sanghodc375372017-06-08 10:41:30 +0900288 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900289 appId,
290 gnodeId,
daniel parkee8700b2017-05-11 15:50:03 +0900291 sBuilder.build(),
292 tBuilder.setOutput(osNodeService.externalPort(gnodeId).get()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900293 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900294 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900295 install);
296 });
297 }
298
299 private class InternalFloatingIpLisener implements OpenstackRouterListener {
300
301 @Override
302 public boolean isRelevant(OpenstackRouterEvent event) {
303 // do not allow to proceed without leadership
304 NodeId leader = leadershipService.getLeader(appId.name());
305 if (!Objects.equals(localNodeId, leader)) {
306 return false;
307 }
308 return event.floatingIp() != null;
309 }
310
311 @Override
312 public void event(OpenstackRouterEvent event) {
313 switch (event.type()) {
314 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900315 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900316 NetFloatingIP osFip = event.floatingIp();
317 associateFloatingIp(osFip);
318 log.info("Associated floating IP {}:{}",
319 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900320 });
321 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900322 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
323 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900324 NetFloatingIP osFip = event.floatingIp();
325 disassociateFloatingIp(osFip, event.portId());
326 log.info("Disassociated floating IP {}:{}",
327 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900328 });
329 break;
330 case OPENSTACK_FLOATING_IP_REMOVED:
331 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900332 NetFloatingIP osFip = event.floatingIp();
333 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
334 disassociateFloatingIp(osFip, osFip.getPortId());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900335 }
Hyunsun Moonb720e632017-05-16 15:41:36 +0900336 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900337 });
338 break;
339 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900340 eventExecutor.execute(() -> {
341 NetFloatingIP osFip = event.floatingIp();
342 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
343 associateFloatingIp(event.floatingIp());
344 }
345 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
346 });
347 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900348 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900349 case OPENSTACK_ROUTER_CREATED:
350 case OPENSTACK_ROUTER_UPDATED:
351 case OPENSTACK_ROUTER_REMOVED:
352 case OPENSTACK_ROUTER_INTERFACE_ADDED:
353 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
354 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
355 default:
356 // do nothing for the other events
357 break;
358 }
359 }
Hyunsun Moonb720e632017-05-16 15:41:36 +0900360
361 private void associateFloatingIp(NetFloatingIP osFip) {
362 Port osPort = osNetworkService.port(osFip.getPortId());
363 if (osPort == null) {
364 final String error = String.format(ERR_FLOW + "port(%s) not found",
365 osFip.getFloatingIpAddress(), osFip.getPortId());
366 throw new IllegalStateException(error);
367 }
368 // set floating IP rules only if the port is associated to a VM
369 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
370 setFloatingIpRules(osFip, osPort, true);
371 }
372 }
373
374 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
375 Port osPort = osNetworkService.port(portId);
376 if (osPort == null) {
377 // FIXME when a port with floating IP removed without
378 // disassociation step, it can reach here
379 return;
380 }
381 // set floating IP rules only if the port is associated to a VM
382 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
383 setFloatingIpRules(osFip, osPort, false);
384 }
385 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900386 }
387
388 private class InternalNodeListener implements OpenstackNodeListener {
389
390 @Override
391 public boolean isRelevant(OpenstackNodeEvent event) {
392 // do not allow to proceed without leadership
393 NodeId leader = leadershipService.getLeader(appId.name());
394 if (!Objects.equals(localNodeId, leader)) {
395 return false;
396 }
397 return event.subject().type() == GATEWAY;
398 }
399
400 @Override
401 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900402
403 switch (event.type()) {
404 case COMPLETE:
405 eventExecutor.execute(() -> {
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900406 for (NetFloatingIP fip : osRouterService.floatingIps()) {
407 if (Strings.isNullOrEmpty(fip.getPortId())) {
408 continue;
409 }
410 Port osPort = osNetworkService.port(fip.getPortId());
411 if (osPort == null) {
412 log.warn("Failed to set floating IP {}", fip.getId());
413 continue;
414 }
415 setFloatingIpRules(fip, osPort, true);
416 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900417 });
418 break;
419 case INIT:
420 case DEVICE_CREATED:
421 case INCOMPLETE:
422 default:
423 break;
424 }
425 }
426 }
427}