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