blob: f3e83636870b4b3a45c409c59c15df912e08e3dc [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;
27import org.onosproject.cluster.ClusterService;
28import org.onosproject.cluster.LeadershipService;
29import org.onosproject.cluster.NodeId;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.net.PortNumber;
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.FlowObjectiveService;
39import org.onosproject.net.flowobjective.ForwardingObjective;
40import org.onosproject.openstacknetworking.api.Constants;
41import org.onosproject.openstacknetworking.api.InstancePort;
42import org.onosproject.openstacknetworking.api.InstancePortService;
43import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
44import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
45import org.onosproject.openstacknetworking.api.OpenstackRouterService;
46import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
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;
52import org.openstack4j.model.network.Port;
53import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
55
56import java.util.Objects;
57import java.util.Optional;
58import java.util.concurrent.ExecutorService;
59
60import static java.util.concurrent.Executors.newSingleThreadExecutor;
61import static org.onlab.util.Tools.groupedThreads;
62import static org.onosproject.openstacknetworking.api.Constants.*;
63import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
64import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
65
66/**
67 * Handles OpenStack floating IP events.
68 */
69@Component(immediate = true)
70public class OpenstackRoutingFloatingIpHandler {
71
72 private final Logger log = LoggerFactory.getLogger(getClass());
73
74 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected CoreService coreService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected DeviceService deviceService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected FlowObjectiveService flowObjectiveService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected LeadershipService leadershipService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected ClusterService clusterService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected OpenstackNodeService osNodeService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected InstancePortService instancePortService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected OpenstackRouterService osRouterService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected OpenstackNetworkService osNetworkService;
102
Hyunsun Moon44aac662017-02-18 02:07:01 +0900103 private final ExecutorService eventExecutor = newSingleThreadExecutor(
104 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
105 private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpLisener();
106 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
107
108 private ApplicationId appId;
109 private NodeId localNodeId;
110
111 @Activate
112 protected void activate() {
113 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
114 localNodeId = clusterService.getLocalNode().id();
115 leadershipService.runForLeadership(appId.name());
116 osRouterService.addListener(floatingIpLisener);
117 osNodeService.addListener(osNodeListener);
118
119 log.info("Started");
120 }
121
122 @Deactivate
123 protected void deactivate() {
124 osNodeService.removeListener(osNodeListener);
125 osRouterService.removeListener(floatingIpLisener);
126 leadershipService.withdraw(appId.name());
127 eventExecutor.shutdown();
128
129 log.info("Stopped");
130 }
131
Hyunsun Moon44aac662017-02-18 02:07:01 +0900132 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
133 boolean install) {
134 Network osNet = osNetworkService.network(osPort.getNetworkId());
135 if (osNet == null) {
136 final String error = String.format(ERR_FLOW + "no network(%s) exists",
137 floatingIp.getFloatingIpAddress(),
138 osPort.getNetworkId());
139 throw new IllegalStateException(error);
140 }
141
142 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
143 InstancePort instPort = instancePortService.instancePort(srcMac);
144 if (instPort == null) {
145 final String error = String.format(ERR_FLOW + "no host(MAC:%s) found",
146 floatingIp.getFloatingIpAddress(), srcMac);
147 throw new IllegalStateException(error);
148 }
149
150 setDownstreamRules(floatingIp, osNet, instPort, install);
151 setUpstreamRules(floatingIp, osNet, instPort, install);
152 }
153
154 private void setDownstreamRules(NetFloatingIP floatingIp, Network osNet,
155 InstancePort instPort, boolean install) {
156 Optional<IpAddress> dataIp = osNodeService.dataIp(instPort.deviceId());
157 if (!dataIp.isPresent()) {
158 log.warn(ERR_FLOW + "compute node {} is not ready",
159 floatingIp, instPort.deviceId());
160 return;
161 }
162
163 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
164 TrafficSelector externalSelector = DefaultTrafficSelector.builder()
165 .matchEthType(Ethernet.TYPE_IPV4)
166 .matchIPDst(floating.toIpPrefix())
167 .build();
168
daniel parke49eb382017-04-05 16:48:28 +0900169 osNodeService.gatewayDeviceIds().forEach(gnodeId -> {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900170 TrafficTreatment externalTreatment = DefaultTrafficTreatment.builder()
171 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
172 .setEthDst(instPort.macAddress())
173 .setIpDst(instPort.ipAddress().getIp4Address())
174 .setTunnelId(Long.valueOf(osNet.getProviderSegID()))
175 .extension(buildExtension(
176 deviceService,
177 gnodeId,
178 dataIp.get().getIp4Address()),
179 gnodeId)
180 .setOutput(osNodeService.tunnelPort(gnodeId).get())
181 .build();
182
183 RulePopulatorUtil.setRule(
184 flowObjectiveService,
185 appId,
186 gnodeId,
187 externalSelector,
188 externalTreatment,
189 ForwardingObjective.Flag.VERSATILE,
190 PRIORITY_FLOATING_EXTERNAL,
191 install);
192
193 // access from one VM to the other via floating IP
194 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
195 .matchEthType(Ethernet.TYPE_IPV4)
196 .matchIPDst(floating.toIpPrefix())
197 .matchInPort(osNodeService.tunnelPort(gnodeId).get())
198 .build();
199
200 TrafficTreatment internalTreatment = DefaultTrafficTreatment.builder()
201 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
202 .setEthDst(instPort.macAddress())
203 .setIpDst(instPort.ipAddress().getIp4Address())
204 .setTunnelId(Long.valueOf(osNet.getProviderSegID()))
205 .extension(buildExtension(
206 deviceService,
207 gnodeId,
208 dataIp.get().getIp4Address()),
209 gnodeId)
210 .setOutput(PortNumber.IN_PORT)
211 .build();
212
213 RulePopulatorUtil.setRule(
214 flowObjectiveService,
215 appId,
216 gnodeId,
217 internalSelector,
218 internalTreatment,
219 ForwardingObjective.Flag.VERSATILE,
220 PRIORITY_FLOATING_INTERNAL,
221 install);
222 });
223 }
224
225 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
226 InstancePort instPort, boolean install) {
227 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
228 TrafficSelector selector = DefaultTrafficSelector.builder()
229 .matchEthType(Ethernet.TYPE_IPV4)
230 .matchTunnelId(Long.valueOf(osNet.getProviderSegID()))
231 .matchIPSrc(instPort.ipAddress().toIpPrefix())
232 .build();
233
daniel parke49eb382017-04-05 16:48:28 +0900234 osNodeService.gatewayDeviceIds().forEach(gnodeId -> {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900235 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
236 .setIpSrc(floating.getIp4Address())
237 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
238 .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
daniel parke49eb382017-04-05 16:48:28 +0900239 .setOutput(osNodeService.externalPort(gnodeId).get())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900240 .build();
241
242 RulePopulatorUtil.setRule(
243 flowObjectiveService,
244 appId,
245 gnodeId,
246 selector,
247 treatment,
248 ForwardingObjective.Flag.VERSATILE,
249 PRIORITY_FLOATING_EXTERNAL,
250 install);
251 });
252 }
253
254 private class InternalFloatingIpLisener implements OpenstackRouterListener {
255
256 @Override
257 public boolean isRelevant(OpenstackRouterEvent event) {
258 // do not allow to proceed without leadership
259 NodeId leader = leadershipService.getLeader(appId.name());
260 if (!Objects.equals(localNodeId, leader)) {
261 return false;
262 }
263 return event.floatingIp() != null;
264 }
265
266 @Override
267 public void event(OpenstackRouterEvent event) {
268 switch (event.type()) {
269 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900270 eventExecutor.execute(() -> {
271 NetFloatingIP fip = event.floatingIp();
272 Port osPort = osNetworkService.port(event.portId());
273 if (osPort == null) {
274 final String error = String.format(ERR_FLOW + "port(%s) not found",
275 fip.getFloatingIpAddress(), fip.getPortId());
276 throw new IllegalStateException(error);
277 }
278 setFloatingIpRules(fip, osPort, true);
279 log.info("Associated floating IP {}:{}", fip.getFloatingIpAddress(),
280 fip.getFixedIpAddress());
281 });
282 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900283 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
284 eventExecutor.execute(() -> {
285 NetFloatingIP fip = event.floatingIp();
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900286 Port osPort = osNetworkService.port(event.portId());
287 if (osPort == null) {
288 final String error = String.format(ERR_FLOW + "port(%s) not found",
289 fip.getFloatingIpAddress(), event.portId());
290 throw new IllegalStateException(error);
291 }
292 setFloatingIpRules(fip, osPort, false);
293 log.info("Disassociated floating IP {}:{}", fip.getFloatingIpAddress(),
294 fip.getFixedIpAddress());
295 });
296 break;
297 case OPENSTACK_FLOATING_IP_REMOVED:
298 eventExecutor.execute(() -> {
299 NetFloatingIP fip = event.floatingIp();
300 if (Strings.isNullOrEmpty(fip.getPortId())) {
301 return;
302 }
303 Port osPort = osNetworkService.port(fip.getPortId());
304 if (osPort == null) {
305 // FIXME when a port with floating IP removed without
306 // disassociation step, it can reach here
307 return;
308 }
309 setFloatingIpRules(fip, osPort, false);
310 log.info("Disassociated floating IP {}:{}", fip.getFloatingIpAddress(),
311 fip.getFixedIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900312 });
313 break;
314 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900315 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900316 case OPENSTACK_ROUTER_CREATED:
317 case OPENSTACK_ROUTER_UPDATED:
318 case OPENSTACK_ROUTER_REMOVED:
319 case OPENSTACK_ROUTER_INTERFACE_ADDED:
320 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
321 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
322 default:
323 // do nothing for the other events
324 break;
325 }
326 }
327 }
328
329 private class InternalNodeListener implements OpenstackNodeListener {
330
331 @Override
332 public boolean isRelevant(OpenstackNodeEvent event) {
333 // do not allow to proceed without leadership
334 NodeId leader = leadershipService.getLeader(appId.name());
335 if (!Objects.equals(localNodeId, leader)) {
336 return false;
337 }
338 return event.subject().type() == GATEWAY;
339 }
340
341 @Override
342 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900343
344 switch (event.type()) {
345 case COMPLETE:
346 eventExecutor.execute(() -> {
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900347 for (NetFloatingIP fip : osRouterService.floatingIps()) {
348 if (Strings.isNullOrEmpty(fip.getPortId())) {
349 continue;
350 }
351 Port osPort = osNetworkService.port(fip.getPortId());
352 if (osPort == null) {
353 log.warn("Failed to set floating IP {}", fip.getId());
354 continue;
355 }
356 setFloatingIpRules(fip, osPort, true);
357 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900358 });
359 break;
360 case INIT:
361 case DEVICE_CREATED:
362 case INCOMPLETE:
363 default:
364 break;
365 }
366 }
367 }
368}