blob: 90f32c3683cbd3001da2ca54ee466e59d2f6c4f4 [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(() -> {
317 NetFloatingIP fip = event.floatingIp();
318 Port osPort = osNetworkService.port(event.portId());
319 if (osPort == null) {
320 final String error = String.format(ERR_FLOW + "port(%s) not found",
321 fip.getFloatingIpAddress(), fip.getPortId());
322 throw new IllegalStateException(error);
323 }
324 setFloatingIpRules(fip, osPort, true);
325 log.info("Associated floating IP {}:{}", fip.getFloatingIpAddress(),
326 fip.getFixedIpAddress());
327 });
328 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900329 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
330 eventExecutor.execute(() -> {
331 NetFloatingIP fip = event.floatingIp();
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900332 Port osPort = osNetworkService.port(event.portId());
333 if (osPort == null) {
334 final String error = String.format(ERR_FLOW + "port(%s) not found",
335 fip.getFloatingIpAddress(), event.portId());
336 throw new IllegalStateException(error);
337 }
338 setFloatingIpRules(fip, osPort, false);
339 log.info("Disassociated floating IP {}:{}", fip.getFloatingIpAddress(),
340 fip.getFixedIpAddress());
341 });
342 break;
343 case OPENSTACK_FLOATING_IP_REMOVED:
344 eventExecutor.execute(() -> {
345 NetFloatingIP fip = event.floatingIp();
346 if (Strings.isNullOrEmpty(fip.getPortId())) {
347 return;
348 }
349 Port osPort = osNetworkService.port(fip.getPortId());
350 if (osPort == null) {
351 // FIXME when a port with floating IP removed without
352 // disassociation step, it can reach here
353 return;
354 }
355 setFloatingIpRules(fip, osPort, false);
356 log.info("Disassociated floating IP {}:{}", fip.getFloatingIpAddress(),
357 fip.getFixedIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900358 });
359 break;
360 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900361 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900362 case OPENSTACK_ROUTER_CREATED:
363 case OPENSTACK_ROUTER_UPDATED:
364 case OPENSTACK_ROUTER_REMOVED:
365 case OPENSTACK_ROUTER_INTERFACE_ADDED:
366 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
367 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
368 default:
369 // do nothing for the other events
370 break;
371 }
372 }
373 }
374
375 private class InternalNodeListener implements OpenstackNodeListener {
376
377 @Override
378 public boolean isRelevant(OpenstackNodeEvent event) {
379 // do not allow to proceed without leadership
380 NodeId leader = leadershipService.getLeader(appId.name());
381 if (!Objects.equals(localNodeId, leader)) {
382 return false;
383 }
384 return event.subject().type() == GATEWAY;
385 }
386
387 @Override
388 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900389
390 switch (event.type()) {
391 case COMPLETE:
392 eventExecutor.execute(() -> {
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900393 for (NetFloatingIP fip : osRouterService.floatingIps()) {
394 if (Strings.isNullOrEmpty(fip.getPortId())) {
395 continue;
396 }
397 Port osPort = osNetworkService.port(fip.getPortId());
398 if (osPort == null) {
399 log.warn("Failed to set floating IP {}", fip.getId());
400 continue;
401 }
402 setFloatingIpRules(fip, osPort, true);
403 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900404 });
405 break;
406 case INIT:
407 case DEVICE_CREATED:
408 case INCOMPLETE:
409 default:
410 break;
411 }
412 }
413 }
414}