blob: 8594aea7087c54ebc99e728bb8cad7291b046eba [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;
51import org.onosproject.scalablegateway.api.ScalableGatewayService;
52import org.openstack4j.model.network.NetFloatingIP;
53import org.openstack4j.model.network.Network;
54import 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: ";
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected CoreService coreService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected DeviceService deviceService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected FlowObjectiveService flowObjectiveService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 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
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected ScalableGatewayService gatewayService;
107
108 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
137 private void floatingIpUpdated(NetFloatingIP floatingIp, String portId) {
138 Port osPort = osNetworkService.port(portId);
139 if (osPort == null) {
140 final String error = String.format(ERR_FLOW + "no port(%s) exists",
141 floatingIp.getFloatingIpAddress(),
142 floatingIp.getPortId());
143 throw new IllegalStateException(error);
144 }
145
146 if (Strings.isNullOrEmpty(floatingIp.getPortId())) {
147 setFloatingIpRules(floatingIp, osPort, false);
148 log.info("Disassociated floating IP:{} from fixed IP:{}",
149 floatingIp.getFloatingIpAddress(),
150 osPort.getFixedIps());
151 } else {
152 setFloatingIpRules(floatingIp, osPort, true);
153 log.info("Associated floating IP:{} to fixed IP:{}",
154 floatingIp.getFloatingIpAddress(),
155 floatingIp.getFixedIpAddress());
156 }
157 }
158
159 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
160 boolean install) {
161 Network osNet = osNetworkService.network(osPort.getNetworkId());
162 if (osNet == null) {
163 final String error = String.format(ERR_FLOW + "no network(%s) exists",
164 floatingIp.getFloatingIpAddress(),
165 osPort.getNetworkId());
166 throw new IllegalStateException(error);
167 }
168
169 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
170 InstancePort instPort = instancePortService.instancePort(srcMac);
171 if (instPort == null) {
172 final String error = String.format(ERR_FLOW + "no host(MAC:%s) found",
173 floatingIp.getFloatingIpAddress(), srcMac);
174 throw new IllegalStateException(error);
175 }
176
177 setDownstreamRules(floatingIp, osNet, instPort, install);
178 setUpstreamRules(floatingIp, osNet, instPort, install);
179 }
180
181 private void setDownstreamRules(NetFloatingIP floatingIp, Network osNet,
182 InstancePort instPort, boolean install) {
183 Optional<IpAddress> dataIp = osNodeService.dataIp(instPort.deviceId());
184 if (!dataIp.isPresent()) {
185 log.warn(ERR_FLOW + "compute node {} is not ready",
186 floatingIp, instPort.deviceId());
187 return;
188 }
189
190 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
191 TrafficSelector externalSelector = DefaultTrafficSelector.builder()
192 .matchEthType(Ethernet.TYPE_IPV4)
193 .matchIPDst(floating.toIpPrefix())
194 .build();
195
196 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
197 TrafficTreatment externalTreatment = DefaultTrafficTreatment.builder()
198 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
199 .setEthDst(instPort.macAddress())
200 .setIpDst(instPort.ipAddress().getIp4Address())
201 .setTunnelId(Long.valueOf(osNet.getProviderSegID()))
202 .extension(buildExtension(
203 deviceService,
204 gnodeId,
205 dataIp.get().getIp4Address()),
206 gnodeId)
207 .setOutput(osNodeService.tunnelPort(gnodeId).get())
208 .build();
209
210 RulePopulatorUtil.setRule(
211 flowObjectiveService,
212 appId,
213 gnodeId,
214 externalSelector,
215 externalTreatment,
216 ForwardingObjective.Flag.VERSATILE,
217 PRIORITY_FLOATING_EXTERNAL,
218 install);
219
220 // access from one VM to the other via floating IP
221 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
222 .matchEthType(Ethernet.TYPE_IPV4)
223 .matchIPDst(floating.toIpPrefix())
224 .matchInPort(osNodeService.tunnelPort(gnodeId).get())
225 .build();
226
227 TrafficTreatment internalTreatment = DefaultTrafficTreatment.builder()
228 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
229 .setEthDst(instPort.macAddress())
230 .setIpDst(instPort.ipAddress().getIp4Address())
231 .setTunnelId(Long.valueOf(osNet.getProviderSegID()))
232 .extension(buildExtension(
233 deviceService,
234 gnodeId,
235 dataIp.get().getIp4Address()),
236 gnodeId)
237 .setOutput(PortNumber.IN_PORT)
238 .build();
239
240 RulePopulatorUtil.setRule(
241 flowObjectiveService,
242 appId,
243 gnodeId,
244 internalSelector,
245 internalTreatment,
246 ForwardingObjective.Flag.VERSATILE,
247 PRIORITY_FLOATING_INTERNAL,
248 install);
249 });
250 }
251
252 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
253 InstancePort instPort, boolean install) {
254 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
255 TrafficSelector selector = DefaultTrafficSelector.builder()
256 .matchEthType(Ethernet.TYPE_IPV4)
257 .matchTunnelId(Long.valueOf(osNet.getProviderSegID()))
258 .matchIPSrc(instPort.ipAddress().toIpPrefix())
259 .build();
260
261 gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
262 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
263 .setIpSrc(floating.getIp4Address())
264 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
265 .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
266 .setOutput(gatewayService.getUplinkPort(gnodeId))
267 .build();
268
269 RulePopulatorUtil.setRule(
270 flowObjectiveService,
271 appId,
272 gnodeId,
273 selector,
274 treatment,
275 ForwardingObjective.Flag.VERSATILE,
276 PRIORITY_FLOATING_EXTERNAL,
277 install);
278 });
279 }
280
281 private class InternalFloatingIpLisener implements OpenstackRouterListener {
282
283 @Override
284 public boolean isRelevant(OpenstackRouterEvent event) {
285 // do not allow to proceed without leadership
286 NodeId leader = leadershipService.getLeader(appId.name());
287 if (!Objects.equals(localNodeId, leader)) {
288 return false;
289 }
290 return event.floatingIp() != null;
291 }
292
293 @Override
294 public void event(OpenstackRouterEvent event) {
295 switch (event.type()) {
296 case OPENSTACK_FLOATING_IP_ASSOCIATED:
297 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
298 eventExecutor.execute(() -> {
299 NetFloatingIP fip = event.floatingIp();
300 log.debug("Floating IP {} is updated", fip.getFloatingIpAddress());
301 floatingIpUpdated(fip, event.portId());
302 });
303 break;
304 case OPENSTACK_FLOATING_IP_CREATED:
305 log.debug("Floating IP {} is created",
306 event.floatingIp().getFloatingIpAddress());
307 break;
308 case OPENSTACK_FLOATING_IP_UPDATED:
309 log.debug("Floating IP {} is updated",
310 event.floatingIp().getFloatingIpAddress());
311 break;
312 case OPENSTACK_FLOATING_IP_REMOVED:
313 log.debug("Floating IP {} is removed",
314 event.floatingIp().getFloatingIpAddress());
315 break;
316 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) {
343 OpenstackNode osNode = event.subject();
344
345 switch (event.type()) {
346 case COMPLETE:
347 eventExecutor.execute(() -> {
348 log.info("GATEWAY node {} detected", osNode.hostname());
349 osRouterService.floatingIps().stream()
350 .filter(fip -> !Strings.isNullOrEmpty(fip.getPortId()))
351 .forEach(fip -> {
352 floatingIpUpdated(fip, fip.getPortId());
353 });
354 });
355 break;
356 case INIT:
357 case DEVICE_CREATED:
358 case INCOMPLETE:
359 default:
360 break;
361 }
362 }
363 }
364}