blob: ee8688d2f0342d75be22cd865b3242c3f086cfb8 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Hyunsun Moon44aac662017-02-18 02:07:01 +09003 *
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;
Jian Li1064e4f2018-05-29 16:16:53 +090019import com.google.common.collect.ImmutableSet;
Jian Li99892e92018-04-13 14:59:39 +090020import com.google.common.collect.Maps;
Jian Li1064e4f2018-05-29 16:16:53 +090021import com.google.common.collect.Sets;
Hyunsun Moon44aac662017-02-18 02:07:01 +090022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.Ethernet;
28import org.onlab.packet.IpAddress;
29import org.onlab.packet.MacAddress;
daniel parkee8700b2017-05-11 15:50:03 +090030import org.onlab.packet.VlanId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090031import org.onosproject.cluster.ClusterService;
32import org.onosproject.cluster.LeadershipService;
33import org.onosproject.cluster.NodeId;
34import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Jian Li99892e92018-04-13 14:59:39 +090036import org.onosproject.net.Host;
Hyunsun Moon44aac662017-02-18 02:07:01 +090037import org.onosproject.net.PortNumber;
38import org.onosproject.net.device.DeviceService;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
Jian Li99892e92018-04-13 14:59:39 +090043import org.onosproject.net.host.HostEvent;
44import org.onosproject.net.host.HostListener;
45import org.onosproject.net.host.HostService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090046import org.onosproject.openstacknetworking.api.Constants;
daniel park32b42202018-03-14 16:53:44 +090047import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Hyunsun Moon44aac662017-02-18 02:07:01 +090048import org.onosproject.openstacknetworking.api.InstancePort;
49import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090050import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
51import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090052import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
53import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
54import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090055import org.onosproject.openstacknode.api.OpenstackNode;
56import org.onosproject.openstacknode.api.OpenstackNodeEvent;
57import org.onosproject.openstacknode.api.OpenstackNodeListener;
58import org.onosproject.openstacknode.api.OpenstackNodeService;
daniel parkeeb8e042018-02-21 14:06:58 +090059import org.openstack4j.model.network.ExternalGateway;
Hyunsun Moon44aac662017-02-18 02:07:01 +090060import org.openstack4j.model.network.NetFloatingIP;
61import org.openstack4j.model.network.Network;
daniel parkee8700b2017-05-11 15:50:03 +090062import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090063import org.openstack4j.model.network.Port;
daniel parkeeb8e042018-02-21 14:06:58 +090064import org.openstack4j.model.network.Router;
65import org.openstack4j.model.network.RouterInterface;
66import org.openstack4j.model.network.Subnet;
Hyunsun Moon44aac662017-02-18 02:07:01 +090067import org.slf4j.Logger;
68import org.slf4j.LoggerFactory;
69
Jian Li99892e92018-04-13 14:59:39 +090070import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090071import java.util.Objects;
Jian Li99892e92018-04-13 14:59:39 +090072import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090073import java.util.concurrent.ExecutorService;
74
75import static java.util.concurrent.Executors.newSingleThreadExecutor;
76import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090077import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
78import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
daniel parkeeb8e042018-02-21 14:06:58 +090079import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE;
sanghodc375372017-06-08 10:41:30 +090080import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
81import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_INTERNAL;
daniel parkeeb8e042018-02-21 14:06:58 +090082import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
Jian Li99892e92018-04-13 14:59:39 +090083import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
84import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Jian Li1064e4f2018-05-29 16:16:53 +090085import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Jian Li26949762018-03-30 15:46:37 +090086import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090087import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090088
89/**
90 * Handles OpenStack floating IP events.
91 */
92@Component(immediate = true)
93public class OpenstackRoutingFloatingIpHandler {
94
95 private final Logger log = LoggerFactory.getLogger(getClass());
96
97 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
Ray Milkeyc6c9b172018-02-26 09:36:31 -080098 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type %s";
Hyunsun Moon44aac662017-02-18 02:07:01 +090099
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected CoreService coreService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected DeviceService deviceService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900107 protected LeadershipService leadershipService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected ClusterService clusterService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li99892e92018-04-13 14:59:39 +0900113 protected HostService hostService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900116 protected OpenstackNodeService osNodeService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected InstancePortService instancePortService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected OpenstackRouterService osRouterService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected OpenstackNetworkService osNetworkService;
126
sanghodc375372017-06-08 10:41:30 +0900127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected OpenstackFlowRuleService osFlowRuleService;
129
Hyunsun Moon44aac662017-02-18 02:07:01 +0900130 private final ExecutorService eventExecutor = newSingleThreadExecutor(
131 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900132 private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900133 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
Jian Li99892e92018-04-13 14:59:39 +0900134 private final HostListener hostListener = new InternalHostListener();
135 private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900136
137 private ApplicationId appId;
138 private NodeId localNodeId;
139
140 @Activate
141 protected void activate() {
142 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
143 localNodeId = clusterService.getLocalNode().id();
144 leadershipService.runForLeadership(appId.name());
Jian Li99892e92018-04-13 14:59:39 +0900145 hostService.addListener(hostListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900146 osRouterService.addListener(floatingIpLisener);
147 osNodeService.addListener(osNodeListener);
148
149 log.info("Started");
150 }
151
152 @Deactivate
153 protected void deactivate() {
Jian Li99892e92018-04-13 14:59:39 +0900154 hostService.removeListener(hostListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900155 osNodeService.removeListener(osNodeListener);
156 osRouterService.removeListener(floatingIpLisener);
157 leadershipService.withdraw(appId.name());
158 eventExecutor.shutdown();
159
160 log.info("Stopped");
161 }
162
Hyunsun Moon44aac662017-02-18 02:07:01 +0900163 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
Jian Li1064e4f2018-05-29 16:16:53 +0900164 OpenstackNode gateway, boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900165 Network osNet = osNetworkService.network(osPort.getNetworkId());
166 if (osNet == null) {
Jian Li71670d12018-03-02 21:31:07 +0900167 final String errorFormat = ERR_FLOW + "no network(%s) exists";
168 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169 floatingIp.getFloatingIpAddress(),
170 osPort.getNetworkId());
171 throw new IllegalStateException(error);
172 }
173
174 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
daniel parkc2a2ed62018-04-10 15:17:42 +0900175 log.trace("Mac address of openstack port: {}", srcMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900176 InstancePort instPort = instancePortService.instancePort(srcMac);
Jian Li99892e92018-04-13 14:59:39 +0900177
178 // sweep through removed port map
179 if (instPort == null) {
180 instPort = removedPorts.get(srcMac);
181 removedPorts.remove(srcMac);
182 }
183
Hyunsun Moon44aac662017-02-18 02:07:01 +0900184 if (instPort == null) {
Jian Li71670d12018-03-02 21:31:07 +0900185 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
186 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900187 floatingIp.getFloatingIpAddress(), srcMac);
188 throw new IllegalStateException(error);
189 }
190
daniel park32b42202018-03-14 16:53:44 +0900191
192 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
193 if (externalPeerRouter == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900194 final String errorFormat = ERR_FLOW + "no external peer router found";
195 throw new IllegalStateException(errorFormat);
daniel park32b42202018-03-14 16:53:44 +0900196 }
197
Jian Lide679782018-06-05 01:41:29 +0900198 updateComputeNodeRules(instPort, osNet, gateway, install);
199 updateGatewayNodeRules(floatingIp, instPort, osNet, externalPeerRouter, gateway, install);
200
201 // FIXME: downstream internal rules are still duplicated in all gateway nodes
202 // need to make the internal rules de-duplicated sooner or later
203 setDownstreamInternalRules(floatingIp, osNet, instPort, install);
204
205 // TODO: need to refactor setUpstreamRules if possible
daniel park32b42202018-03-14 16:53:44 +0900206 setUpstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900207 log.trace("Succeeded to set flow rules for floating ip {}:{} and install: {}",
208 floatingIp.getFloatingIpAddress(),
209 floatingIp.getFixedIpAddress(),
210 install);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900211 }
212
Jian Lide679782018-06-05 01:41:29 +0900213 private synchronized void updateGatewayNodeRules(NetFloatingIP fip,
214 InstancePort instPort,
215 Network osNet,
216 ExternalPeerRouter router,
217 OpenstackNode gateway,
218 boolean install) {
219
220 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
221 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
222 finalGws.addAll(ImmutableSet.copyOf(completedGws));
223
224 if (install) {
225 if (completedGws.contains(gateway)) {
226 if (completedGws.size() > 1) {
227 finalGws.remove(gateway);
228 if (fip.getPortId() != null) {
229 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
230 ImmutableSet.copyOf(finalGws), false);
231 finalGws.add(gateway);
232 }
233 }
234 if (fip.getPortId() != null) {
235 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
236 ImmutableSet.copyOf(finalGws), true);
237 }
238 } else {
239 log.warn("Detected node should be included in completed gateway set");
240 }
241 } else {
242 if (!completedGws.contains(gateway)) {
243 if (completedGws.size() >= 1) {
244 if (fip.getPortId() != null) {
245 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
246 ImmutableSet.copyOf(finalGws), true);
247 }
248 }
249 } else {
250 log.warn("Detected node should NOT be included in completed gateway set");
251 }
252 }
253 }
254
255 private synchronized void updateComputeNodeRules(InstancePort instPort,
256 Network osNet,
257 OpenstackNode gateway,
258 boolean install) {
Jian Li1064e4f2018-05-29 16:16:53 +0900259
260 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
261 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
262 finalGws.addAll(ImmutableSet.copyOf(completedGws));
263
264 if (gateway == null) {
265 // these are floating IP related cases...
266 setComputeNodeToGatewayHelper(instPort, osNet,
267 ImmutableSet.copyOf(finalGws), install);
Jian Lide679782018-06-05 01:41:29 +0900268
Jian Li1064e4f2018-05-29 16:16:53 +0900269 } else {
270 // these are openstack node related cases...
271 if (install) {
272 if (completedGws.contains(gateway)) {
273 if (completedGws.size() > 1) {
274 finalGws.remove(gateway);
275 setComputeNodeToGatewayHelper(instPort, osNet,
276 ImmutableSet.copyOf(finalGws), false);
277 finalGws.add(gateway);
278 }
279
280 setComputeNodeToGatewayHelper(instPort, osNet,
281 ImmutableSet.copyOf(finalGws), true);
282 } else {
283 log.warn("Detected node should be included in completed gateway set");
284 }
285 } else {
286 if (!completedGws.contains(gateway)) {
287 finalGws.add(gateway);
288 setComputeNodeToGatewayHelper(instPort, osNet,
289 ImmutableSet.copyOf(finalGws), false);
290 finalGws.remove(gateway);
291 if (completedGws.size() >= 1) {
292 setComputeNodeToGatewayHelper(instPort, osNet,
293 ImmutableSet.copyOf(finalGws), true);
294 }
295 } else {
296 log.warn("Detected node should NOT be included in completed gateway set");
297 }
298 }
299 }
300 }
301
302 // a helper method
303 private void setComputeNodeToGatewayHelper(InstancePort instPort,
304 Network osNet,
305 Set<OpenstackNode> gateways,
306 boolean install) {
daniel parkeeb8e042018-02-21 14:06:58 +0900307 TrafficTreatment treatment;
308
309 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
310 .matchEthType(Ethernet.TYPE_IPV4)
311 .matchIPSrc(instPort.ipAddress().toIpPrefix())
312 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
313
314 switch (osNet.getNetworkType()) {
315 case VXLAN:
316 sBuilder.matchTunnelId(Long.parseLong(osNet.getProviderSegID()));
317 break;
318 case VLAN:
319 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
320 break;
321 default:
322 final String error = String.format(
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800323 ERR_UNSUPPORTED_NET_TYPE,
daniel parkeeb8e042018-02-21 14:06:58 +0900324 osNet.getNetworkType().toString());
325 throw new IllegalStateException(error);
326 }
327
Jian Li1064e4f2018-05-29 16:16:53 +0900328 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
329
daniel parkeeb8e042018-02-21 14:06:58 +0900330 if (selectedGatewayNode == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900331 final String errorFormat = ERR_FLOW + "no gateway node selected";
332 throw new IllegalStateException(errorFormat);
daniel parkeeb8e042018-02-21 14:06:58 +0900333 }
334 treatment = DefaultTrafficTreatment.builder()
335 .extension(buildExtension(
336 deviceService,
337 instPort.deviceId(),
338 selectedGatewayNode.dataIp().getIp4Address()),
339 instPort.deviceId())
340 .setOutput(osNodeService.node(instPort.deviceId()).tunnelPortNum())
341 .build();
342
343 osFlowRuleService.setRule(
344 appId,
345 instPort.deviceId(),
346 sBuilder.build(),
347 treatment,
348 PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE,
349 ROUTING_TABLE,
350 install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900351 log.trace("Succeeded to set flow rules from compute node to gateway on compute node");
daniel parkeeb8e042018-02-21 14:06:58 +0900352 }
353
Jian Lide679782018-06-05 01:41:29 +0900354 private void setDownstreamInternalRules(NetFloatingIP floatingIp,
355 Network osNet,
356 InstancePort instPort,
357 boolean install) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900358 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
359 if (cNode == null) {
360 final String error = String.format("Cannot find openstack node for device %s",
361 instPort.deviceId());
362 throw new IllegalStateException(error);
363 }
364 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900365 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
366 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900367 throw new IllegalStateException(error);
368 }
369 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900370 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
371 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900372 throw new IllegalStateException(error);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900373 }
374
375 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900376
Jian Lide679782018-06-05 01:41:29 +0900377 // TODO: following code snippet will be refactored sooner or later
Hyunsun Moon0d457362017-06-27 17:19:41 +0900378 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900379 // access from one VM to the others via floating IP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900380 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
381 .matchEthType(Ethernet.TYPE_IPV4)
382 .matchIPDst(floating.toIpPrefix())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900383 .matchInPort(gNode.tunnelPortNum())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900384 .build();
385
daniel parkee8700b2017-05-11 15:50:03 +0900386 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900387 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
388 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900389 .setIpDst(instPort.ipAddress().getIp4Address());
390
391 switch (osNet.getNetworkType()) {
392 case VXLAN:
393 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
394 .extension(buildExtension(
395 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900396 gNode.intgBridge(),
397 cNode.dataIp().getIp4Address()),
398 gNode.intgBridge())
daniel parkee8700b2017-05-11 15:50:03 +0900399 .setOutput(PortNumber.IN_PORT);
400 break;
401 case VLAN:
402 internalBuilder.pushVlan()
403 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
404 .setOutput(PortNumber.IN_PORT);
405 break;
406 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800407 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900408 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900409 throw new IllegalStateException(error);
410 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900411
sanghodc375372017-06-08 10:41:30 +0900412 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900413 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900414 gNode.intgBridge(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900415 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900416 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900417 PRIORITY_FLOATING_INTERNAL,
sanghodc375372017-06-08 10:41:30 +0900418 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900419 install);
420 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900421 log.trace("Succeeded to set flow rules for downstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900422 }
423
Jian Lide679782018-06-05 01:41:29 +0900424 private void setDownstreamExternalRulesHelper(NetFloatingIP floatingIp,
425 Network osNet,
426 InstancePort instPort,
427 ExternalPeerRouter externalPeerRouter,
428 Set<OpenstackNode> gateways, boolean install) {
429 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
430 if (cNode == null) {
431 final String error = String.format("Cannot find openstack node for device %s",
432 instPort.deviceId());
433 throw new IllegalStateException(error);
434 }
435 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
436 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
437 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
438 throw new IllegalStateException(error);
439 }
440 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
441 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
442 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
443 throw new IllegalStateException(error);
444 }
445
446 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
447
448 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
449
450 if (selectedGatewayNode == null) {
451 final String errorFormat = ERR_FLOW + "no gateway node selected";
452 throw new IllegalStateException(errorFormat);
453 }
454
455 TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
456 .matchEthType(Ethernet.TYPE_IPV4)
457 .matchIPDst(floating.toIpPrefix());
458
459 TrafficTreatment.Builder externalTreatmentBuilder = DefaultTrafficTreatment.builder()
460 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
461 .setEthDst(instPort.macAddress())
462 .setIpDst(instPort.ipAddress().getIp4Address());
463
464 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
465 externalSelectorBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId()).build();
466 externalTreatmentBuilder.popVlan();
467 }
468
469 switch (osNet.getNetworkType()) {
470 case VXLAN:
471 externalTreatmentBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
472 .extension(buildExtension(
473 deviceService,
474 selectedGatewayNode.intgBridge(),
475 cNode.dataIp().getIp4Address()),
476 selectedGatewayNode.intgBridge())
477 .setOutput(selectedGatewayNode.tunnelPortNum());
478 break;
479 case VLAN:
480 externalTreatmentBuilder.pushVlan()
481 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
482 .setOutput(selectedGatewayNode.vlanPortNum());
483 break;
484 default:
485 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
486 osNet.getNetworkType());
487 throw new IllegalStateException(error);
488 }
489
490 osFlowRuleService.setRule(
491 appId,
492 selectedGatewayNode.intgBridge(),
493 externalSelectorBuilder.build(),
494 externalTreatmentBuilder.build(),
495 PRIORITY_FLOATING_EXTERNAL,
496 GW_COMMON_TABLE,
497 install);
498 }
499
Hyunsun Moon44aac662017-02-18 02:07:01 +0900500 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900501 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
502 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900503 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900504 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900505 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900506 .matchIPSrc(instPort.ipAddress().toIpPrefix());
507
508 switch (osNet.getNetworkType()) {
509 case VXLAN:
510 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
511 break;
512 case VLAN:
513 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
514 break;
515 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800516 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900517 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900518 throw new IllegalStateException(error);
519 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900520
Daniel Park75e3d7f2018-05-29 14:43:53 +0900521 TrafficSelector selector = sBuilder.build();
daniel parkeeb8e042018-02-21 14:06:58 +0900522
Hyunsun Moon0d457362017-06-27 17:19:41 +0900523 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900524 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
525 .setIpSrc(floating.getIp4Address())
526 .setEthSrc(instPort.macAddress())
527 .setEthDst(externalPeerRouter.externalPeerRouterMac());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900528
Daniel Park75e3d7f2018-05-29 14:43:53 +0900529 if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
530 tBuilder.popVlan();
531 }
532
533 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
534 tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
535 }
sanghodc375372017-06-08 10:41:30 +0900536 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900537 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900538 gNode.intgBridge(),
Daniel Park75e3d7f2018-05-29 14:43:53 +0900539 selector,
daniel parkeeb8e042018-02-21 14:06:58 +0900540 tBuilder.setOutput(gNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900541 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900542 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900543 install);
Daniel Park75e3d7f2018-05-29 14:43:53 +0900544 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900545 log.trace("Succeeded to set flow rules for upstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900546 }
547
daniel park32b42202018-03-14 16:53:44 +0900548 private ExternalPeerRouter externalPeerRouter(Network network) {
daniel parkeeb8e042018-02-21 14:06:58 +0900549 if (network == null) {
550 return null;
551 }
552
553 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
554
555 if (subnet == null) {
556 return null;
557 }
558
559 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
560 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
561 .findAny().orElse(null);
562 if (osRouterIface == null) {
563 return null;
564 }
565
566 Router osRouter = osRouterService.router(osRouterIface.getId());
567 if (osRouter == null) {
568 return null;
569 }
570 if (osRouter.getExternalGatewayInfo() == null) {
571 return null;
572 }
573
574 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
daniel park65e1c202018-04-03 13:15:28 +0900575 return osNetworkService.externalPeerRouter(exGatewayInfo);
daniel parkeeb8e042018-02-21 14:06:58 +0900576 }
daniel park65e1c202018-04-03 13:15:28 +0900577
Jian Li99892e92018-04-13 14:59:39 +0900578
579 private void associateFloatingIp(NetFloatingIP osFip) {
580 Port osPort = osNetworkService.port(osFip.getPortId());
581 if (osPort == null) {
582 final String errorFormat = ERR_FLOW + "port(%s) not found";
583 final String error = String.format(errorFormat,
584 osFip.getFloatingIpAddress(), osFip.getPortId());
585 throw new IllegalStateException(error);
586 }
587 // set floating IP rules only if the port is associated to a VM
588 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900589 setFloatingIpRules(osFip, osPort, null, true);
Jian Li99892e92018-04-13 14:59:39 +0900590 }
591 }
592
593 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
594 Port osPort = osNetworkService.port(portId);
595 if (osPort == null) {
596 // FIXME when a port with floating IP removed without
597 // disassociation step, it can reach here
598 return;
599 }
600 // set floating IP rules only if the port is associated to a VM
601 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900602 setFloatingIpRules(osFip, osPort, null, false);
Jian Li99892e92018-04-13 14:59:39 +0900603 }
604 }
605
Hyunsun Moon0d457362017-06-27 17:19:41 +0900606 private class InternalFloatingIpListener implements OpenstackRouterListener {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900607
608 @Override
609 public boolean isRelevant(OpenstackRouterEvent event) {
610 // do not allow to proceed without leadership
611 NodeId leader = leadershipService.getLeader(appId.name());
612 if (!Objects.equals(localNodeId, leader)) {
613 return false;
614 }
615 return event.floatingIp() != null;
616 }
617
618 @Override
619 public void event(OpenstackRouterEvent event) {
620 switch (event.type()) {
621 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900622 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900623 NetFloatingIP osFip = event.floatingIp();
624 associateFloatingIp(osFip);
625 log.info("Associated floating IP {}:{}",
626 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900627 });
628 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900629 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
630 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900631 NetFloatingIP osFip = event.floatingIp();
632 disassociateFloatingIp(osFip, event.portId());
633 log.info("Disassociated floating IP {}:{}",
634 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900635 });
636 break;
637 case OPENSTACK_FLOATING_IP_REMOVED:
638 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900639 NetFloatingIP osFip = event.floatingIp();
640 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
641 disassociateFloatingIp(osFip, osFip.getPortId());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900642 }
Hyunsun Moonb720e632017-05-16 15:41:36 +0900643 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900644 });
645 break;
646 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900647 eventExecutor.execute(() -> {
648 NetFloatingIP osFip = event.floatingIp();
649 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
650 associateFloatingIp(event.floatingIp());
651 }
652 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
653 });
654 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900655 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900656 case OPENSTACK_ROUTER_CREATED:
657 case OPENSTACK_ROUTER_UPDATED:
658 case OPENSTACK_ROUTER_REMOVED:
659 case OPENSTACK_ROUTER_INTERFACE_ADDED:
660 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
661 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
662 default:
663 // do nothing for the other events
664 break;
665 }
666 }
667 }
668
669 private class InternalNodeListener implements OpenstackNodeListener {
670
671 @Override
672 public boolean isRelevant(OpenstackNodeEvent event) {
673 // do not allow to proceed without leadership
674 NodeId leader = leadershipService.getLeader(appId.name());
675 if (!Objects.equals(localNodeId, leader)) {
676 return false;
677 }
678 return event.subject().type() == GATEWAY;
679 }
680
681 @Override
682 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900683
684 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900685 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900686 eventExecutor.execute(() -> {
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900687 for (NetFloatingIP fip : osRouterService.floatingIps()) {
688 if (Strings.isNullOrEmpty(fip.getPortId())) {
689 continue;
690 }
691 Port osPort = osNetworkService.port(fip.getPortId());
692 if (osPort == null) {
693 log.warn("Failed to set floating IP {}", fip.getId());
694 continue;
695 }
Jian Li1064e4f2018-05-29 16:16:53 +0900696 setFloatingIpRules(fip, osPort, event.subject(), true);
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900697 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900698 });
699 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900700 case OPENSTACK_NODE_INCOMPLETE:
Jian Li1064e4f2018-05-29 16:16:53 +0900701
702 // we only purge the routing related rules stored in each
703 // compute node when gateway node becomes unavailable
704 if (!event.subject().type().equals(GATEWAY)) {
705 return;
706 }
707
708 eventExecutor.execute(() -> {
709 for (NetFloatingIP fip : osRouterService.floatingIps()) {
710 if (Strings.isNullOrEmpty(fip.getPortId())) {
711 continue;
712 }
713 Port osPort = osNetworkService.port(fip.getPortId());
714 if (osPort == null) {
715 log.warn("Failed to set floating IP {}", fip.getId());
716 continue;
717 }
718 Network osNet = osNetworkService.network(osPort.getNetworkId());
719 if (osNet == null) {
720 final String errorFormat = ERR_FLOW + "no network(%s) exists";
721 final String error = String.format(errorFormat,
722 fip.getFloatingIpAddress(),
723 osPort.getNetworkId());
724 throw new IllegalStateException(error);
725 }
726 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
727 log.trace("Mac address of openstack port: {}", srcMac);
728 InstancePort instPort = instancePortService.instancePort(srcMac);
729
730 if (instPort == null) {
731 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
732 final String error = String.format(errorFormat,
733 fip.getFloatingIpAddress(), srcMac);
734 throw new IllegalStateException(error);
735 }
736
Jian Lide679782018-06-05 01:41:29 +0900737 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
738 if (externalPeerRouter == null) {
739 final String errorFormat = ERR_FLOW + "no external peer router found";
740 throw new IllegalStateException(errorFormat);
741 }
742
743 updateComputeNodeRules(instPort, osNet, event.subject(), false);
744 updateGatewayNodeRules(fip, instPort, osNet,
745 externalPeerRouter, event.subject(), false);
Jian Li1064e4f2018-05-29 16:16:53 +0900746 }
747 });
748 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900749 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900750 // do nothing
Hyunsun Moon44aac662017-02-18 02:07:01 +0900751 break;
752 }
753 }
754 }
Jian Li99892e92018-04-13 14:59:39 +0900755
756 private class InternalHostListener implements HostListener {
757
758 @Override
759 public boolean isRelevant(HostEvent event) {
760 Host host = event.subject();
761 if (!isValidHost(host)) {
762 log.debug("Invalid host detected, ignore it {}", host);
763 return false;
764 }
765 return true;
766 }
767
768 @Override
769 public void event(HostEvent event) {
770 InstancePort instPort = HostBasedInstancePort.of(event.subject());
771 switch (event.type()) {
772 case HOST_REMOVED:
773 storeTempInstPort(instPort);
774 break;
775 case HOST_UPDATED:
776 case HOST_ADDED:
777 default:
778 break;
779 }
780 }
781
782 private void storeTempInstPort(InstancePort port) {
783 Set<NetFloatingIP> ips = osRouterService.floatingIps();
784 for (NetFloatingIP fip : ips) {
785 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
786 continue;
787 }
788 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
789 continue;
790 }
791 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
792 removedPorts.put(port.macAddress(), port);
793 eventExecutor.execute(() -> {
794 disassociateFloatingIp(fip, port.portId());
795 log.info("Disassociated floating IP {}:{}",
796 fip.getFloatingIpAddress(), fip.getFixedIpAddress());
797 });
798 }
799 }
800 }
801
802 private boolean isValidHost(Host host) {
803 return !host.ipAddresses().isEmpty() &&
804 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
805 host.annotations().value(ANNOTATION_PORT_ID) != null;
806 }
807 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900808}