blob: 5b5e8a7670b26d5515415d86716c2bb075bdc0f6 [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 Li1064e4f2018-05-29 16:16:53 +090020import com.google.common.collect.Sets;
Jian Li4d1b1f32020-05-11 23:02:38 +090021import org.onlab.packet.ARP;
22import org.onlab.packet.EthType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090023import org.onlab.packet.Ethernet;
Jian Li4d1b1f32020-05-11 23:02:38 +090024import org.onlab.packet.Ip4Address;
Hyunsun Moon44aac662017-02-18 02:07:01 +090025import org.onlab.packet.IpAddress;
26import org.onlab.packet.MacAddress;
daniel parkee8700b2017-05-11 15:50:03 +090027import org.onlab.packet.VlanId;
Jian Li4d1b1f32020-05-11 23:02:38 +090028import org.onosproject.cfg.ComponentConfigService;
29import org.onosproject.cfg.ConfigProperty;
Hyunsun Moon44aac662017-02-18 02:07:01 +090030import org.onosproject.cluster.ClusterService;
31import org.onosproject.cluster.LeadershipService;
32import org.onosproject.cluster.NodeId;
33import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
Jian Li4d1b1f32020-05-11 23:02:38 +090035import org.onosproject.net.Device;
Jian Li24ec59f2018-05-23 19:01:25 +090036import org.onosproject.net.DeviceId;
Jian Li2d68c192018-12-13 15:52:59 +090037import org.onosproject.net.PortNumber;
Hyunsun Moon44aac662017-02-18 02:07:01 +090038import 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;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090043import org.onosproject.net.packet.PacketService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090044import org.onosproject.openstacknetworking.api.Constants;
daniel park32b42202018-03-14 16:53:44 +090045import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Hyunsun Moon44aac662017-02-18 02:07:01 +090046import org.onosproject.openstacknetworking.api.InstancePort;
Jian Liec5c32b2018-07-13 14:28:58 +090047import org.onosproject.openstacknetworking.api.InstancePortAdminService;
Jian Li24ec59f2018-05-23 19:01:25 +090048import org.onosproject.openstacknetworking.api.InstancePortEvent;
49import org.onosproject.openstacknetworking.api.InstancePortListener;
sanghodc375372017-06-08 10:41:30 +090050import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
SONA Project6bc5c4a2018-12-14 23:49:52 +090051import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
Jian Lie1a39032018-06-19 21:49:36 +090052import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
53import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
sanghodc375372017-06-08 10:41:30 +090054import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lif3a28b02018-06-11 21:29:13 +090055import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090056import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
57import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
Jian Li8f64feb2018-07-24 13:20:16 +090058import org.onosproject.openstacknetworking.api.PreCommitPortService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090059import org.onosproject.openstacknode.api.OpenstackNode;
60import org.onosproject.openstacknode.api.OpenstackNodeEvent;
61import org.onosproject.openstacknode.api.OpenstackNodeListener;
62import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090063import org.openstack4j.model.network.NetFloatingIP;
64import org.openstack4j.model.network.Network;
65import org.openstack4j.model.network.Port;
Jian Lif3a28b02018-06-11 21:29:13 +090066import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
Jian Li34220ea2018-11-14 01:30:24 +090067import org.osgi.service.component.annotations.Activate;
68import org.osgi.service.component.annotations.Component;
69import org.osgi.service.component.annotations.Deactivate;
70import org.osgi.service.component.annotations.Reference;
71import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon44aac662017-02-18 02:07:01 +090072import org.slf4j.Logger;
73import org.slf4j.LoggerFactory;
74
75import java.util.Objects;
Jian Li99892e92018-04-13 14:59:39 +090076import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090077import java.util.concurrent.ExecutorService;
78
79import static java.util.concurrent.Executors.newSingleThreadExecutor;
80import static org.onlab.util.Tools.groupedThreads;
Jian Li4d1b1f32020-05-11 23:02:38 +090081import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
sanghodc375372017-06-08 10:41:30 +090082import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
83import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Jian Li4d1b1f32020-05-11 23:02:38 +090084import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
daniel parkeeb8e042018-02-21 14:06:58 +090085import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE;
sanghodc375372017-06-08 10:41:30 +090086import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
daniel parkeeb8e042018-02-21 14:06:58 +090087import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
Jian Li8a5fb642018-09-14 15:50:04 +090088import static org.onosproject.openstacknetworking.api.InstancePort.State.REMOVE_PENDING;
Jian Lie1a39032018-06-19 21:49:36 +090089import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_ENDED;
90import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_STARTED;
Jian Lie6e609f2019-05-14 17:45:54 +090091import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.GENEVE;
SONA Project6bc5c4a2018-12-14 23:49:52 +090092import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.GRE;
93import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VLAN;
94import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VXLAN;
Jian Li8f64feb2018-07-24 13:20:16 +090095import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
Jian Li4d1b1f32020-05-11 23:02:38 +090096import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE;
Jian Li24ec59f2018-05-23 19:01:25 +090097import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
Daniel Park4fa1f5e2018-10-17 12:41:52 +090098import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalPeerRouterForNetwork;
Jian Li1064e4f2018-05-29 16:16:53 +090099import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900100import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
Jian Li4d1b1f32020-05-11 23:02:38 +0900101import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Li24ec59f2018-05-23 19:01:25 +0900102import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
Jian Li32b03622018-11-06 17:54:24 +0900103import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.processGarpPacketForFloatingIp;
Jian Liec5c32b2018-07-13 14:28:58 +0900104import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Li2d68c192018-12-13 15:52:59 +0900105import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.tunnelPortNumByNetId;
Jian Li26949762018-03-30 15:46:37 +0900106import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Jian Li4d1b1f32020-05-11 23:02:38 +0900107import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
108import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
109import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900110import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900111
112/**
113 * Handles OpenStack floating IP events.
114 */
115@Component(immediate = true)
116public class OpenstackRoutingFloatingIpHandler {
117
118 private final Logger log = LoggerFactory.getLogger(getClass());
119
120 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800121 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type %s";
Hyunsun Moon44aac662017-02-18 02:07:01 +0900122
Jian Li4d138702018-11-27 17:25:28 +0900123 private static final String NO_EXT_PEER_ROUTER_MSG = "no external peer router found";
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900126 protected CoreService coreService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900129 protected DeviceService deviceService;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900132 protected LeadershipService leadershipService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900135 protected ClusterService clusterService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900138 protected OpenstackNodeService osNodeService;
139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Liec5c32b2018-07-13 14:28:58 +0900141 protected InstancePortAdminService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900142
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lif3a28b02018-06-11 21:29:13 +0900144 protected OpenstackRouterAdminService osRouterAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900145
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900147 protected OpenstackNetworkService osNetworkService;
148
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700149 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sanghodc375372017-06-08 10:41:30 +0900150 protected OpenstackFlowRuleService osFlowRuleService;
151
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li8f64feb2018-07-24 13:20:16 +0900153 protected PreCommitPortService preCommitPortService;
154
Ray Milkeyd5425682018-10-23 10:21:33 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900156 protected PacketService packetService;
157
Jian Li4d1b1f32020-05-11 23:02:38 +0900158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
159 protected ComponentConfigService configService;
160
Hyunsun Moon44aac662017-02-18 02:07:01 +0900161 private final ExecutorService eventExecutor = newSingleThreadExecutor(
162 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Jian Li5ecfd1a2018-12-10 11:41:03 +0900163 private final OpenstackRouterListener
164 floatingIpListener = new InternalFloatingIpListener();
165 private final InstancePortListener
166 instancePortListener = new InternalInstancePortListener();
167 private final OpenstackNodeListener
168 osNodeListener = new InternalNodeListener();
169 private final OpenstackNetworkListener
170 osNetworkListener = new InternalOpenstackNetworkListener();
171 private final InstancePortListener
172 instPortListener = new InternalInstancePortListener();
Jian Lie1a39032018-06-19 21:49:36 +0900173
Hyunsun Moon44aac662017-02-18 02:07:01 +0900174 private ApplicationId appId;
175 private NodeId localNodeId;
176
177 @Activate
178 protected void activate() {
179 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
180 localNodeId = clusterService.getLocalNode().id();
181 leadershipService.runForLeadership(appId.name());
Jian Li24ec59f2018-05-23 19:01:25 +0900182 osRouterAdminService.addListener(floatingIpListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900183 osNodeService.addListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900184 instancePortService.addListener(instancePortListener);
Jian Lie1a39032018-06-19 21:49:36 +0900185 osNodeService.addListener(osNodeListener);
186 osNetworkService.addListener(osNetworkListener);
187 instancePortService.addListener(instPortListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900188
189 log.info("Started");
190 }
191
192 @Deactivate
193 protected void deactivate() {
Jian Li24ec59f2018-05-23 19:01:25 +0900194 instancePortService.removeListener(instancePortListener);
Jian Lie1a39032018-06-19 21:49:36 +0900195 instancePortService.removeListener(instPortListener);
196 osNetworkService.removeListener(osNetworkListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900197 osNodeService.removeListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900198 osRouterAdminService.removeListener(floatingIpListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900199 leadershipService.withdraw(appId.name());
200 eventExecutor.shutdown();
201
202 log.info("Stopped");
203 }
204
Jian Li4d1b1f32020-05-11 23:02:38 +0900205 private String getArpMode() {
206 Set<ConfigProperty> properties =
207 configService.getProperties(OpenstackRoutingArpHandler.class.getName());
208 return getPropertyValue(properties, ARP_MODE);
209 }
210
Jian Lie6e609f2019-05-14 17:45:54 +0900211 private void setFloatingIpRules(NetFloatingIP floatingIp,
212 InstancePort instPort,
213 OpenstackNode gateway,
214 ExternalPeerRouter peerRouter,
215 boolean install) {
Jian Li628a7cb2018-08-11 23:04:24 +0900216
217 if (instPort == null) {
218 log.debug("No instance port found");
219 return;
220 }
221
222 Network osNet = osNetworkService.network(instPort.networkId());
Jian Li8f64feb2018-07-24 13:20:16 +0900223
Jian Lie6e609f2019-05-14 17:45:54 +0900224 ExternalPeerRouter externalPeerRouter = peerRouter != null ? peerRouter :
225 externalPeerRouterForNetwork(osNet, osNetworkService, osRouterAdminService);
226
daniel park32b42202018-03-14 16:53:44 +0900227 if (externalPeerRouter == null) {
Jian Lie6e609f2019-05-14 17:45:54 +0900228 log.warn("External peer router is not ready for now, " +
229 "floating IP rules will be installed/uninstalled " +
230 "when external peer router is available...");
231 return;
daniel park32b42202018-03-14 16:53:44 +0900232 }
233
Jian Li8f64feb2018-07-24 13:20:16 +0900234 if (install) {
Jian Li628a7cb2018-08-11 23:04:24 +0900235 preCommitPortService.subscribePreCommit(instPort.portId(),
Jian Li8f64feb2018-07-24 13:20:16 +0900236 OPENSTACK_PORT_PRE_REMOVE, this.getClass().getName());
Jian Li628a7cb2018-08-11 23:04:24 +0900237 log.info("Subscribed the port {} on listening pre-remove event", instPort.portId());
Jian Li8f64feb2018-07-24 13:20:16 +0900238 } else {
Jian Li628a7cb2018-08-11 23:04:24 +0900239 preCommitPortService.unsubscribePreCommit(instPort.portId(),
240 OPENSTACK_PORT_PRE_REMOVE, instancePortService, this.getClass().getName());
241 log.info("Unsubscribed the port {} on listening pre-remove event", instPort.portId());
Jian Li8f64feb2018-07-24 13:20:16 +0900242 }
243
Jian Lide679782018-06-05 01:41:29 +0900244 updateComputeNodeRules(instPort, osNet, gateway, install);
245 updateGatewayNodeRules(floatingIp, instPort, osNet, externalPeerRouter, gateway, install);
246
Jian Lide679782018-06-05 01:41:29 +0900247 // TODO: need to refactor setUpstreamRules if possible
daniel park32b42202018-03-14 16:53:44 +0900248 setUpstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
Jian Li8f64feb2018-07-24 13:20:16 +0900249
daniel parkc2a2ed62018-04-10 15:17:42 +0900250 log.trace("Succeeded to set flow rules for floating ip {}:{} and install: {}",
251 floatingIp.getFloatingIpAddress(),
252 floatingIp.getFixedIpAddress(),
253 install);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900254 }
255
Jian Lide679782018-06-05 01:41:29 +0900256 private synchronized void updateGatewayNodeRules(NetFloatingIP fip,
257 InstancePort instPort,
258 Network osNet,
259 ExternalPeerRouter router,
260 OpenstackNode gateway,
261 boolean install) {
262
263 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
264 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
265 finalGws.addAll(ImmutableSet.copyOf(completedGws));
266
Jian Lia171a432018-06-11 11:52:11 +0900267
268 if (gateway == null) {
269 // these are floating IP related cases...
270 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
271 ImmutableSet.copyOf(finalGws), install);
272
Jian Lide679782018-06-05 01:41:29 +0900273 } else {
Jian Lia171a432018-06-11 11:52:11 +0900274 // these are openstack node related cases...
275 if (install) {
276 if (completedGws.contains(gateway)) {
277 if (completedGws.size() > 1) {
278 finalGws.remove(gateway);
279 if (fip.getPortId() != null) {
280 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
281 ImmutableSet.copyOf(finalGws), false);
282 finalGws.add(gateway);
283 }
284 }
Jian Lide679782018-06-05 01:41:29 +0900285 if (fip.getPortId() != null) {
286 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
287 ImmutableSet.copyOf(finalGws), true);
288 }
Jian Lia171a432018-06-11 11:52:11 +0900289 } else {
290 log.warn("Detected node should be included in completed gateway set");
Jian Lide679782018-06-05 01:41:29 +0900291 }
292 } else {
Jian Lia171a432018-06-11 11:52:11 +0900293 if (!completedGws.contains(gateway)) {
Jian Li4d138702018-11-27 17:25:28 +0900294 if (!completedGws.isEmpty() && fip.getPortId() != null) {
295 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
Jian Lia171a432018-06-11 11:52:11 +0900296 ImmutableSet.copyOf(finalGws), true);
Jian Lia171a432018-06-11 11:52:11 +0900297 }
298 } else {
299 log.warn("Detected node should NOT be included in completed gateway set");
300 }
Jian Lide679782018-06-05 01:41:29 +0900301 }
302 }
303 }
304
305 private synchronized void updateComputeNodeRules(InstancePort instPort,
306 Network osNet,
307 OpenstackNode gateway,
308 boolean install) {
Jian Li1064e4f2018-05-29 16:16:53 +0900309
310 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
311 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
312 finalGws.addAll(ImmutableSet.copyOf(completedGws));
313
314 if (gateway == null) {
315 // these are floating IP related cases...
316 setComputeNodeToGatewayHelper(instPort, osNet,
317 ImmutableSet.copyOf(finalGws), install);
Jian Lide679782018-06-05 01:41:29 +0900318
Jian Li1064e4f2018-05-29 16:16:53 +0900319 } else {
320 // these are openstack node related cases...
321 if (install) {
322 if (completedGws.contains(gateway)) {
323 if (completedGws.size() > 1) {
324 finalGws.remove(gateway);
325 setComputeNodeToGatewayHelper(instPort, osNet,
326 ImmutableSet.copyOf(finalGws), false);
327 finalGws.add(gateway);
328 }
329
330 setComputeNodeToGatewayHelper(instPort, osNet,
331 ImmutableSet.copyOf(finalGws), true);
332 } else {
333 log.warn("Detected node should be included in completed gateway set");
334 }
335 } else {
336 if (!completedGws.contains(gateway)) {
337 finalGws.add(gateway);
338 setComputeNodeToGatewayHelper(instPort, osNet,
339 ImmutableSet.copyOf(finalGws), false);
340 finalGws.remove(gateway);
Jian Li4d138702018-11-27 17:25:28 +0900341 if (!completedGws.isEmpty()) {
Jian Li1064e4f2018-05-29 16:16:53 +0900342 setComputeNodeToGatewayHelper(instPort, osNet,
343 ImmutableSet.copyOf(finalGws), true);
344 }
345 } else {
346 log.warn("Detected node should NOT be included in completed gateway set");
347 }
348 }
349 }
350 }
351
352 // a helper method
353 private void setComputeNodeToGatewayHelper(InstancePort instPort,
354 Network osNet,
355 Set<OpenstackNode> gateways,
356 boolean install) {
daniel parkeeb8e042018-02-21 14:06:58 +0900357
358 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
359 .matchEthType(Ethernet.TYPE_IPV4)
360 .matchIPSrc(instPort.ipAddress().toIpPrefix())
361 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
362
Daniel Park3f26e792018-11-26 18:34:53 +0900363 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
daniel parkeeb8e042018-02-21 14:06:58 +0900364
Jian Li5ecfd1a2018-12-10 11:41:03 +0900365 OpenstackNode selectedGatewayNode =
366 getGwByComputeDevId(gateways, instPort.deviceId());
Jian Li1064e4f2018-05-29 16:16:53 +0900367
daniel parkeeb8e042018-02-21 14:06:58 +0900368 if (selectedGatewayNode == null) {
Daniel Park3f26e792018-11-26 18:34:53 +0900369 log.warn(ERR_FLOW + "no gateway node selected");
Jian Lifb64d882018-11-27 10:57:40 +0900370 return;
daniel parkeeb8e042018-02-21 14:06:58 +0900371 }
Daniel Park3f26e792018-11-26 18:34:53 +0900372
Jian Li621f73c2018-12-15 01:49:22 +0900373 Type netType = osNetworkService.networkType(osNet.getId());
374
375 switch (netType) {
Daniel Park3f26e792018-11-26 18:34:53 +0900376 case VXLAN:
Jian Li2d68c192018-12-13 15:52:59 +0900377 case GRE:
Jian Li621f73c2018-12-15 01:49:22 +0900378 case GENEVE:
Jian Li2d68c192018-12-13 15:52:59 +0900379 PortNumber portNum = tunnelPortNumByNetId(instPort.networkId(),
380 osNetworkService, osNodeService.node(instPort.deviceId()));
381
382 if (portNum == null) {
Daniel Park3f26e792018-11-26 18:34:53 +0900383 log.warn(ERR_FLOW + "no tunnel port");
384 return;
385 }
Jian Li2d68c192018-12-13 15:52:59 +0900386
Daniel Park3f26e792018-11-26 18:34:53 +0900387 sBuilder.matchTunnelId(Long.parseLong(osNet.getProviderSegID()));
Jian Li2d68c192018-12-13 15:52:59 +0900388
Daniel Park3f26e792018-11-26 18:34:53 +0900389 tBuilder.extension(buildExtension(
daniel parkeeb8e042018-02-21 14:06:58 +0900390 deviceService,
391 instPort.deviceId(),
392 selectedGatewayNode.dataIp().getIp4Address()),
393 instPort.deviceId())
Jian Li2d68c192018-12-13 15:52:59 +0900394 .setOutput(portNum);
Daniel Park3f26e792018-11-26 18:34:53 +0900395 break;
396 case VLAN:
397 if (osNodeService.node(instPort.deviceId()).vlanPortNum() == null) {
398 log.warn(ERR_FLOW + "no vlan port");
399 return;
400 }
401 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
402 tBuilder.setOutput(osNodeService.node(instPort.deviceId()).vlanPortNum());
403 break;
404 default:
405 log.warn(ERR_FLOW + "no supported network type");
406 }
daniel parkeeb8e042018-02-21 14:06:58 +0900407
408 osFlowRuleService.setRule(
409 appId,
410 instPort.deviceId(),
411 sBuilder.build(),
Daniel Park3f26e792018-11-26 18:34:53 +0900412 tBuilder.build(),
daniel parkeeb8e042018-02-21 14:06:58 +0900413 PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE,
414 ROUTING_TABLE,
415 install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900416 log.trace("Succeeded to set flow rules from compute node to gateway on compute node");
daniel parkeeb8e042018-02-21 14:06:58 +0900417 }
418
Jian Lide679782018-06-05 01:41:29 +0900419 private void setDownstreamExternalRulesHelper(NetFloatingIP floatingIp,
420 Network osNet,
421 InstancePort instPort,
422 ExternalPeerRouter externalPeerRouter,
423 Set<OpenstackNode> gateways, boolean install) {
424 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
SONA Project6bc5c4a2018-12-14 23:49:52 +0900425 Type netType = osNetworkService.networkType(osNet.getId());
Jian Lide679782018-06-05 01:41:29 +0900426 if (cNode == null) {
427 final String error = String.format("Cannot find openstack node for device %s",
428 instPort.deviceId());
429 throw new IllegalStateException(error);
430 }
SONA Project6bc5c4a2018-12-14 23:49:52 +0900431 if (netType == VXLAN && cNode.dataIp() == null) {
Jian Lide679782018-06-05 01:41:29 +0900432 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
433 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
434 throw new IllegalStateException(error);
435 }
SONA Project6bc5c4a2018-12-14 23:49:52 +0900436 if (netType == GRE && cNode.dataIp() == null) {
Jian Li2d68c192018-12-13 15:52:59 +0900437 final String errorFormat = ERR_FLOW + "GRE mode is not ready for %s";
438 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
439 throw new IllegalStateException(error);
440 }
Jian Li621f73c2018-12-15 01:49:22 +0900441 if (netType == GENEVE && cNode.dataIp() == null) {
442 final String errorFormat = ERR_FLOW + "GENEVE mode is not ready for %s";
443 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
444 throw new IllegalStateException(error);
445 }
SONA Project6bc5c4a2018-12-14 23:49:52 +0900446 if (netType == VLAN && cNode.vlanIntf() == null) {
Jian Lide679782018-06-05 01:41:29 +0900447 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
448 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
449 throw new IllegalStateException(error);
450 }
451
452 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
453
454 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
455
456 if (selectedGatewayNode == null) {
457 final String errorFormat = ERR_FLOW + "no gateway node selected";
458 throw new IllegalStateException(errorFormat);
459 }
460
461 TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
462 .matchEthType(Ethernet.TYPE_IPV4)
Daniel Park319b9ab2018-09-14 11:27:04 +0900463 .matchInPort(selectedGatewayNode.uplinkPortNum())
Jian Lide679782018-06-05 01:41:29 +0900464 .matchIPDst(floating.toIpPrefix());
465
466 TrafficTreatment.Builder externalTreatmentBuilder = DefaultTrafficTreatment.builder()
467 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
468 .setEthDst(instPort.macAddress())
469 .setIpDst(instPort.ipAddress().getIp4Address());
470
Jian Li5e2ad4a2018-07-16 13:40:53 +0900471 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
472 externalSelectorBuilder.matchVlanId(externalPeerRouter.vlanId()).build();
Jian Lide679782018-06-05 01:41:29 +0900473 externalTreatmentBuilder.popVlan();
474 }
475
Jian Li621f73c2018-12-15 01:49:22 +0900476 switch (netType) {
Jian Lide679782018-06-05 01:41:29 +0900477 case VXLAN:
Jian Li2d68c192018-12-13 15:52:59 +0900478 case GRE:
Jian Li621f73c2018-12-15 01:49:22 +0900479 case GENEVE:
Jian Li2d68c192018-12-13 15:52:59 +0900480 PortNumber portNum = tunnelPortNumByNetId(instPort.networkId(),
481 osNetworkService, selectedGatewayNode);
Jian Lide679782018-06-05 01:41:29 +0900482 externalTreatmentBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
483 .extension(buildExtension(
484 deviceService,
485 selectedGatewayNode.intgBridge(),
486 cNode.dataIp().getIp4Address()),
487 selectedGatewayNode.intgBridge())
Jian Li2d68c192018-12-13 15:52:59 +0900488 .setOutput(portNum);
Jian Lide679782018-06-05 01:41:29 +0900489 break;
490 case VLAN:
491 externalTreatmentBuilder.pushVlan()
492 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
493 .setOutput(selectedGatewayNode.vlanPortNum());
494 break;
495 default:
496 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
497 osNet.getNetworkType());
498 throw new IllegalStateException(error);
499 }
500
501 osFlowRuleService.setRule(
502 appId,
503 selectedGatewayNode.intgBridge(),
504 externalSelectorBuilder.build(),
505 externalTreatmentBuilder.build(),
506 PRIORITY_FLOATING_EXTERNAL,
507 GW_COMMON_TABLE,
508 install);
Jian Li4d1b1f32020-05-11 23:02:38 +0900509
Jian Li4ac9f9b2020-05-18 17:48:48 +0900510 setArpRule(floatingIp, instPort.macAddress(), externalPeerRouter, selectedGatewayNode, install);
Jian Li4d1b1f32020-05-11 23:02:38 +0900511 }
512
513 private void setArpRule(NetFloatingIP floatingIp, MacAddress targetMac,
Jian Li4ac9f9b2020-05-18 17:48:48 +0900514 ExternalPeerRouter externalPeerRouter,
Jian Li4d1b1f32020-05-11 23:02:38 +0900515 OpenstackNode gateway, boolean install) {
Jian Li4ac9f9b2020-05-18 17:48:48 +0900516 if (ARP_BROADCAST_MODE.equals(getArpMode()) && externalPeerRouter.ipAddress() != null) {
Jian Li4d1b1f32020-05-11 23:02:38 +0900517 TrafficSelector selector = DefaultTrafficSelector.builder()
518 .matchInPort(gateway.uplinkPortNum())
519 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
Jian Li4ac9f9b2020-05-18 17:48:48 +0900520 .matchArpSpa(Ip4Address.valueOf(externalPeerRouter.ipAddress().toString()))
Jian Li4d1b1f32020-05-11 23:02:38 +0900521 .matchArpOp(ARP.OP_REQUEST)
522 .matchArpTpa(Ip4Address.valueOf(floatingIp.getFloatingIpAddress()))
523 .build();
524
525 Device device = deviceService.getDevice(gateway.intgBridge());
526
527 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
528 .extension(buildMoveEthSrcToDstExtension(device), device.id())
529 .extension(buildMoveArpShaToThaExtension(device), device.id())
530 .extension(buildMoveArpSpaToTpaExtension(device), device.id())
531 .setArpOp(ARP.OP_REPLY)
532 .setEthSrc(targetMac)
533 .setArpSha(targetMac)
534 .setArpSpa(Ip4Address.valueOf(floatingIp.getFloatingIpAddress()))
535 .setOutput(PortNumber.IN_PORT)
536 .build();
537
538 osFlowRuleService.setRule(
539 appId,
540 gateway.intgBridge(),
541 selector,
542 treatment,
543 PRIORITY_ARP_GATEWAY_RULE,
544 GW_COMMON_TABLE,
545 install
546 );
547
548 if (install) {
549 log.info("Install ARP Rule for Floating IP {}",
550 floatingIp.getFloatingIpAddress());
551 } else {
552 log.info("Uninstall ARP Rule for Floating IP {}",
553 floatingIp.getFloatingIpAddress());
554 }
555 }
Jian Lide679782018-06-05 01:41:29 +0900556 }
557
Hyunsun Moon44aac662017-02-18 02:07:01 +0900558 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
Jian Li32b03622018-11-06 17:54:24 +0900559 InstancePort instPort,
560 ExternalPeerRouter externalPeerRouter,
daniel park32b42202018-03-14 16:53:44 +0900561 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900562 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900563 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900564 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900565 .matchIPSrc(instPort.ipAddress().toIpPrefix());
566
Jian Li621f73c2018-12-15 01:49:22 +0900567 Type netType = osNetworkService.networkType(osNet.getId());
568
569 switch (netType) {
daniel parkee8700b2017-05-11 15:50:03 +0900570 case VXLAN:
Jian Li2d68c192018-12-13 15:52:59 +0900571 case GRE:
Jian Li621f73c2018-12-15 01:49:22 +0900572 case GENEVE:
daniel parkee8700b2017-05-11 15:50:03 +0900573 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
574 break;
575 case VLAN:
576 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
577 break;
578 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800579 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900580 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900581 throw new IllegalStateException(error);
582 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900583
Daniel Park75e3d7f2018-05-29 14:43:53 +0900584 TrafficSelector selector = sBuilder.build();
daniel parkeeb8e042018-02-21 14:06:58 +0900585
Hyunsun Moon0d457362017-06-27 17:19:41 +0900586 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900587 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
588 .setIpSrc(floating.getIp4Address())
589 .setEthSrc(instPort.macAddress())
Jian Li5e2ad4a2018-07-16 13:40:53 +0900590 .setEthDst(externalPeerRouter.macAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900591
SONA Project6bc5c4a2018-12-14 23:49:52 +0900592 if (netType == VLAN) {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900593 tBuilder.popVlan();
594 }
595
Jian Li5e2ad4a2018-07-16 13:40:53 +0900596 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
597 tBuilder.pushVlan().setVlanId(externalPeerRouter.vlanId());
Daniel Park75e3d7f2018-05-29 14:43:53 +0900598 }
sanghodc375372017-06-08 10:41:30 +0900599 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900600 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900601 gNode.intgBridge(),
Daniel Park75e3d7f2018-05-29 14:43:53 +0900602 selector,
daniel parkeeb8e042018-02-21 14:06:58 +0900603 tBuilder.setOutput(gNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900604 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900605 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900606 install);
Daniel Park75e3d7f2018-05-29 14:43:53 +0900607 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900608 log.trace("Succeeded to set flow rules for upstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900609 }
610
Jian Li99892e92018-04-13 14:59:39 +0900611 private void associateFloatingIp(NetFloatingIP osFip) {
Jian Li628a7cb2018-08-11 23:04:24 +0900612 InstancePort instPort = instancePortService.instancePort(osFip.getPortId());
613
614 if (instPort == null) {
615 log.warn("Failed to insert floating IP rule for {} due to missing of port info.",
616 osFip.getFloatingIpAddress());
617 return;
Jian Li99892e92018-04-13 14:59:39 +0900618 }
Jian Li628a7cb2018-08-11 23:04:24 +0900619
Jian Li99892e92018-04-13 14:59:39 +0900620 // set floating IP rules only if the port is associated to a VM
Jian Li628a7cb2018-08-11 23:04:24 +0900621 if (!Strings.isNullOrEmpty(instPort.deviceId().toString())) {
Jian Lie6e609f2019-05-14 17:45:54 +0900622 setFloatingIpRules(osFip, instPort, null, null, true);
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900623 processGratuitousArpPacket(osFip, instPort);
624
Jian Li99892e92018-04-13 14:59:39 +0900625 }
626 }
627
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900628 private void processGratuitousArpPacket(NetFloatingIP floatingIP,
629 InstancePort instancePort) {
630 Set<OpenstackNode> gws = ImmutableSet.copyOf(osNodeService.completeNodes(GATEWAY));
631
632 Network osNet = osNetworkService.network(instancePort.networkId());
633
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900634 OpenstackNode selectedGw = getGwByInstancePort(gws, instancePort);
635 ExternalPeerRouter externalPeerRouter =
636 externalPeerRouterForNetwork(osNet, osNetworkService, osRouterAdminService);
637 if (externalPeerRouter == null) {
Jian Lie6e609f2019-05-14 17:45:54 +0900638 log.error("Failed to process GARP packet for floating ip {}, because ",
Jian Li4d138702018-11-27 17:25:28 +0900639 NO_EXT_PEER_ROUTER_MSG);
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900640 return;
641 }
642
Jian Li32b03622018-11-06 17:54:24 +0900643 processGarpPacketForFloatingIp(floatingIP, instancePort,
644 externalPeerRouter.vlanId(), selectedGw, packetService);
Daniel Park4fa1f5e2018-10-17 12:41:52 +0900645
646 }
647
Jian Li99892e92018-04-13 14:59:39 +0900648 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
Jian Li628a7cb2018-08-11 23:04:24 +0900649 InstancePort instPort = instancePortService.instancePort(portId);
Jian Lie1a39032018-06-19 21:49:36 +0900650
Jian Li628a7cb2018-08-11 23:04:24 +0900651 if (instPort == null) {
652 log.warn("Failed to remove floating IP rule for {} due to missing of port info.",
653 osFip.getFloatingIpAddress());
654 return;
Jian Lie1a39032018-06-19 21:49:36 +0900655 }
656
Jian Li99892e92018-04-13 14:59:39 +0900657 // set floating IP rules only if the port is associated to a VM
Jian Li628a7cb2018-08-11 23:04:24 +0900658 if (!Strings.isNullOrEmpty(instPort.deviceId().toString())) {
Jian Lie6e609f2019-05-14 17:45:54 +0900659 setFloatingIpRules(osFip, instPort, null, null, false);
Jian Li99892e92018-04-13 14:59:39 +0900660 }
661 }
662
Hyunsun Moon0d457362017-06-27 17:19:41 +0900663 private class InternalFloatingIpListener implements OpenstackRouterListener {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900664
665 @Override
666 public boolean isRelevant(OpenstackRouterEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900667 return event.floatingIp() != null;
668 }
669
Jian Li34220ea2018-11-14 01:30:24 +0900670 private boolean isRelevantHelper() {
671 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
672 }
673
Hyunsun Moon44aac662017-02-18 02:07:01 +0900674 @Override
675 public void event(OpenstackRouterEvent event) {
676 switch (event.type()) {
677 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Jian Li4d138702018-11-27 17:25:28 +0900678 eventExecutor.execute(() -> processFloatingIpAssociation(event));
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900679 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900680 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
Jian Li4d138702018-11-27 17:25:28 +0900681 eventExecutor.execute(() -> processFloatingIpDisassociation(event));
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900682 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900683 case OPENSTACK_FLOATING_IP_CREATED:
Jian Li4d138702018-11-27 17:25:28 +0900684 eventExecutor.execute(() -> processFloatingIpCreation(event));
Hyunsun Moonb720e632017-05-16 15:41:36 +0900685 break;
Jian Lia171a432018-06-11 11:52:11 +0900686 case OPENSTACK_FLOATING_IP_REMOVED:
Jian Li4d138702018-11-27 17:25:28 +0900687 eventExecutor.execute(() -> processFloatingIpRemoval(event));
Jian Lia171a432018-06-11 11:52:11 +0900688 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900689 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900690 default:
691 // do nothing for the other events
692 break;
693 }
694 }
Jian Li4d138702018-11-27 17:25:28 +0900695
696 private void processFloatingIpAssociation(OpenstackRouterEvent event) {
697 if (!isRelevantHelper()) {
698 return;
699 }
700
701 NetFloatingIP osFip = event.floatingIp();
702 if (instancePortService.instancePort(osFip.getPortId()) != null) {
703 associateFloatingIp(osFip);
704 log.info("Associated floating IP {}:{}",
705 osFip.getFloatingIpAddress(),
706 osFip.getFixedIpAddress());
707 }
708 }
709
710 private void processFloatingIpDisassociation(OpenstackRouterEvent event) {
711 if (!isRelevantHelper()) {
712 return;
713 }
714
715 NetFloatingIP osFip = event.floatingIp();
716 if (instancePortService.instancePort(event.portId()) != null) {
717 disassociateFloatingIp(osFip, event.portId());
718 log.info("Disassociated floating IP {}:{}",
719 osFip.getFloatingIpAddress(),
720 osFip.getFixedIpAddress());
721 }
722 }
723
724 private void processFloatingIpCreation(OpenstackRouterEvent event) {
725 if (!isRelevantHelper()) {
726 return;
727 }
728
729 NetFloatingIP osFip = event.floatingIp();
730 String portId = osFip.getPortId();
731 if (!Strings.isNullOrEmpty(portId) &&
732 instancePortService.instancePort(portId) != null) {
733 associateFloatingIp(event.floatingIp());
734 }
735 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
736 }
737
738 private void processFloatingIpRemoval(OpenstackRouterEvent event) {
739 if (!isRelevantHelper()) {
740 return;
741 }
742
743 NetFloatingIP osFip = event.floatingIp();
744 String portId = osFip.getPortId();
745 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
746 // in case the floating IP is not associated with any port due to
747 // port removal, we simply do not execute floating IP disassociation
748 if (osNetworkService.port(portId) != null &&
749 instancePortService.instancePort(portId) != null) {
750 disassociateFloatingIp(osFip, portId);
751 }
752
753 // since we skip floating IP disassociation, we need to
754 // manually unsubscribe the port pre-remove event
755 preCommitPortService.unsubscribePreCommit(osFip.getPortId(),
756 OPENSTACK_PORT_PRE_REMOVE, instancePortService,
757 this.getClass().getName());
758 log.info("Unsubscribed the port {} on listening pre-remove event",
759 osFip.getPortId());
760 }
761 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
762 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900763 }
764
765 private class InternalNodeListener implements OpenstackNodeListener {
766
767 @Override
768 public boolean isRelevant(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900769 return event.subject().type() == GATEWAY;
770 }
771
Jian Li34220ea2018-11-14 01:30:24 +0900772 private boolean isRelevantHelper() {
773 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
774 }
775
Hyunsun Moon44aac662017-02-18 02:07:01 +0900776 @Override
777 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900778
779 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900780 case OPENSTACK_NODE_COMPLETE:
Jian Li4d138702018-11-27 17:25:28 +0900781 eventExecutor.execute(() -> processNodeCompletion(event));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900782 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900783 case OPENSTACK_NODE_INCOMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900784 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900785 // do nothing
Hyunsun Moon44aac662017-02-18 02:07:01 +0900786 break;
787 }
788 }
Jian Li4d138702018-11-27 17:25:28 +0900789
790 private void processNodeCompletion(OpenstackNodeEvent event) {
791 if (!isRelevantHelper()) {
792 return;
793 }
794
795 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
796
797 if (Strings.isNullOrEmpty(fip.getPortId())) {
798 continue;
799 }
800
801 Port osPort = osNetworkService.port(fip.getPortId());
802 InstancePort instPort = instancePortService.instancePort(fip.getPortId());
803
804 // we check both Openstack Port and Instance Port
805 if (osPort == null || instPort == null) {
806 continue;
807 }
808
Jian Lie6e609f2019-05-14 17:45:54 +0900809 setFloatingIpRules(fip, instPort, event.subject(), null, true);
Jian Li4d138702018-11-27 17:25:28 +0900810 }
811 }
812
813 private void processNodeIncompletion(OpenstackNodeEvent event) {
814 if (!isRelevantHelper()) {
815 return;
816 }
817
818 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
819 if (Strings.isNullOrEmpty(fip.getPortId())) {
820 continue;
821 }
822 Port osPort = osNetworkService.port(fip.getPortId());
823 if (osPort == null) {
824 log.warn("Failed to set floating IP {}", fip.getId());
825 continue;
826 }
827 Network osNet = osNetworkService.network(osPort.getNetworkId());
828 if (osNet == null) {
829 final String errorFormat = ERR_FLOW + "no network(%s) exists";
830 final String error = String.format(errorFormat,
831 fip.getFloatingIpAddress(),
832 osPort.getNetworkId());
833 throw new IllegalStateException(error);
834 }
835 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
836 log.trace("Mac address of openstack port: {}", srcMac);
837 InstancePort instPort = instancePortService.instancePort(srcMac);
838
839 if (instPort == null) {
840 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
841 final String error = String.format(errorFormat,
842 fip.getFloatingIpAddress(), srcMac);
843 throw new IllegalStateException(error);
844 }
845
846 ExternalPeerRouter externalPeerRouter = externalPeerRouterForNetwork(osNet,
847 osNetworkService, osRouterAdminService);
848 if (externalPeerRouter == null) {
849 final String errorFormat = ERR_FLOW + NO_EXT_PEER_ROUTER_MSG;
850 throw new IllegalStateException(errorFormat);
851 }
852
853 updateComputeNodeRules(instPort, osNet, event.subject(), false);
854 updateGatewayNodeRules(fip, instPort, osNet,
855 externalPeerRouter, event.subject(), false);
856 }
857 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900858 }
Jian Li99892e92018-04-13 14:59:39 +0900859
Jian Li24ec59f2018-05-23 19:01:25 +0900860 private class InternalInstancePortListener implements InstancePortListener {
861
Jian Li34220ea2018-11-14 01:30:24 +0900862 private boolean isRelevantHelper(InstancePortEvent event) {
Jian Li24ec59f2018-05-23 19:01:25 +0900863
Jian Lie1a39032018-06-19 21:49:36 +0900864 if (event.type() == OPENSTACK_INSTANCE_MIGRATION_ENDED ||
865 event.type() == OPENSTACK_INSTANCE_MIGRATION_STARTED) {
866 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
867 NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
868
869 // we check the possible NPE to avoid duplicated null check
870 // for OPENSTACK_INSTANCE_MIGRATION_ENDED and
871 // OPENSTACK_INSTANCE_MIGRATION_STARTED cases
872 if (fip == null || !isAssociatedWithVM(osNetworkService, fip)) {
873 return false;
874 }
875 }
876
Jian Li34220ea2018-11-14 01:30:24 +0900877 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
Jian Li24ec59f2018-05-23 19:01:25 +0900878 }
879
880 @Override
881 public void event(InstancePortEvent event) {
Jian Li24ec59f2018-05-23 19:01:25 +0900882 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900883 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Li4d138702018-11-27 17:25:28 +0900884 eventExecutor.execute(() -> processInstancePortDetection(event));
Jian Lie1a39032018-06-19 21:49:36 +0900885 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900886 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Li4d138702018-11-27 17:25:28 +0900887 eventExecutor.execute(() -> processInstanceMigrationStart(event));
Jian Li24ec59f2018-05-23 19:01:25 +0900888 break;
889 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Li4d138702018-11-27 17:25:28 +0900890 eventExecutor.execute(() -> processInstanceMigrationEnd(event));
Jian Li24ec59f2018-05-23 19:01:25 +0900891 break;
892 default:
893 break;
894 }
895 }
Jian Li4d138702018-11-27 17:25:28 +0900896
897 private void processInstancePortDetection(InstancePortEvent event) {
898 if (!isRelevantHelper(event)) {
899 return;
900 }
901
902 InstancePort instPort = event.subject();
903
904 if (instPort != null && instPort.portId() != null) {
905 osRouterAdminService.floatingIps().stream()
906 .filter(f -> f.getPortId() != null)
907 .filter(f -> f.getPortId().equals(instPort.portId()))
908 .forEach(f -> setFloatingIpRules(f,
Jian Lie6e609f2019-05-14 17:45:54 +0900909 instPort, null, null, true));
Jian Li4d138702018-11-27 17:25:28 +0900910 }
911 }
912
913 private void processInstanceMigrationStart(InstancePortEvent event) {
914 if (!isRelevantHelper(event)) {
915 return;
916 }
917
918 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
919 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
920 NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
921
922 if (fip == null) {
923 return;
924 }
925
926 Port osPort = osNetworkService.port(fip.getPortId());
927 Network osNet = osNetworkService.network(osPort.getNetworkId());
928 ExternalPeerRouter externalPeerRouter = externalPeerRouterForNetwork(osNet,
929 osNetworkService, osRouterAdminService);
930
931 if (externalPeerRouter == null) {
932 final String errorFormat = ERR_FLOW + NO_EXT_PEER_ROUTER_MSG;
933 throw new IllegalStateException(errorFormat);
934 }
935
936 // since DownstreamExternal rules should only be placed in
937 // corresponding gateway node, we need to install new rule to
938 // the corresponding gateway node
939 setDownstreamExternalRulesHelper(fip, osNet,
940 event.subject(), externalPeerRouter, gateways, true);
941
942 // since ComputeNodeToGateway rules should only be placed in
943 // corresponding compute node, we need to install new rule to
944 // the target compute node, and remove rules from original node
945 setComputeNodeToGatewayHelper(event.subject(), osNet, gateways, true);
946 }
947
948 private void processInstanceMigrationEnd(InstancePortEvent event) {
949 if (!isRelevantHelper(event)) {
950 return;
951 }
952
953 InstancePort oldInstPort = swapStaleLocation(event.subject());
954
955 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
956 NetFloatingIP fip = associatedFloatingIp(oldInstPort, ips);
957
958 if (fip == null) {
959 return;
960 }
961
962 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
963 Port osPort = osNetworkService.port(fip.getPortId());
964 Network osNet = osNetworkService.network(osPort.getNetworkId());
965 ExternalPeerRouter externalPeerRouter = externalPeerRouterForNetwork(osNet,
966 osNetworkService, osRouterAdminService);
967
968 if (externalPeerRouter == null) {
969 final String errorFormat = ERR_FLOW + NO_EXT_PEER_ROUTER_MSG;
970 throw new IllegalStateException(errorFormat);
971 }
972
973 // We need to remove the old ComputeNodeToGateway rules from
974 // original compute node
975 setComputeNodeToGatewayHelper(oldInstPort, osNet, gateways, false);
976
977 // If we only have one gateway, we simply do not remove any
978 // flow rules from either gateway or compute node
979 if (gateways.size() == 1) {
980 return;
981 }
982
983 // Checks whether the destination compute node's device id
984 // has identical gateway hash or not
985 // if it is true, we simply do not remove the rules, as
986 // it has been overwritten at port detention event
987 // if it is false, we will remove the rules
988 DeviceId newDeviceId = event.subject().deviceId();
989 DeviceId oldDeviceId = oldInstPort.deviceId();
990
991 OpenstackNode oldGateway = getGwByComputeDevId(gateways, oldDeviceId);
992 OpenstackNode newGateway = getGwByComputeDevId(gateways, newDeviceId);
993
994 if (oldGateway != null && oldGateway.equals(newGateway)) {
995 return;
996 }
997
998 // Since DownstreamExternal rules should only be placed in
999 // corresponding gateway node, we need to remove old rule from
1000 // the corresponding gateway node
1001 setDownstreamExternalRulesHelper(fip, osNet, oldInstPort,
1002 externalPeerRouter, gateways, false);
1003 }
Jian Li24ec59f2018-05-23 19:01:25 +09001004 }
Jian Lie1a39032018-06-19 21:49:36 +09001005
1006 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
1007
Jian Li34220ea2018-11-14 01:30:24 +09001008 private boolean isRelevantHelper() {
1009 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
Jian Lie1a39032018-06-19 21:49:36 +09001010 }
1011
1012 @Override
1013 public void event(OpenstackNetworkEvent event) {
Jian Li34220ea2018-11-14 01:30:24 +09001014
Jian Lie6e609f2019-05-14 17:45:54 +09001015 switch (event.type()) {
1016 case OPENSTACK_PORT_PRE_REMOVE:
1017 eventExecutor.execute(() -> processPortPreRemoval(event));
1018 break;
1019 case EXTERNAL_PEER_ROUTER_MAC_UPDATED:
1020 eventExecutor.execute(() -> processExternalPeerRouterMacUpdate(event));
1021 break;
1022 default:
1023 break;
Jian Lie1a39032018-06-19 21:49:36 +09001024 }
1025 }
1026
Jian Lie6e609f2019-05-14 17:45:54 +09001027 private void processExternalPeerRouterMacUpdate(OpenstackNetworkEvent event) {
1028 if (!isRelevantHelper()) {
1029 return;
1030 }
1031
1032 instancePortService.instancePorts().forEach(instPort ->
1033 osRouterAdminService.floatingIps().stream()
1034 .filter(f -> f.getPortId() != null)
1035 .filter(f -> f.getPortId().equals(instPort.portId()))
1036 .forEach(f -> setFloatingIpRules(f,
1037 instPort, null, event.peerRouter(), true)));
1038 }
1039
Jian Li4d138702018-11-27 17:25:28 +09001040 private void processPortPreRemoval(OpenstackNetworkEvent event) {
Jian Lie6e609f2019-05-14 17:45:54 +09001041 if (!isRelevantHelper()) {
1042 return;
1043 }
1044
Jian Li32b03622018-11-06 17:54:24 +09001045 InstancePort instPort = instancePortService.instancePort(
1046 event.port().getId());
1047 if (instPort == null) {
1048 return;
1049 }
1050 NetFloatingIP fip = associatedFloatingIp(instPort,
1051 osRouterAdminService.floatingIps());
1052
1053 if (fip != null) {
1054 instancePortService.updateInstancePort(
1055 instPort.updateState(REMOVE_PENDING));
1056 updateFipStore(event.port().getId());
1057 } else {
1058 instancePortService.removeInstancePort(instPort.portId());
1059 }
1060 }
1061
Jian Li2360acb2018-10-17 00:46:31 +09001062 private void updateFipStore(String portId) {
Jian Lie1a39032018-06-19 21:49:36 +09001063
Jian Li2360acb2018-10-17 00:46:31 +09001064 if (portId == null) {
Jian Lie1a39032018-06-19 21:49:36 +09001065 return;
1066 }
1067
1068 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
1069 for (NetFloatingIP fip : ips) {
1070 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
1071 continue;
1072 }
1073 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
1074 continue;
1075 }
Jian Li2360acb2018-10-17 00:46:31 +09001076 if (fip.getPortId().equals(portId)) {
Jian Lie1a39032018-06-19 21:49:36 +09001077 NeutronFloatingIP neutronFip = (NeutronFloatingIP) fip;
1078 // invalidate bound fixed IP and port
1079 neutronFip.setFixedIpAddress(null);
1080 neutronFip.setPortId(null);
Jian Lie1a39032018-06-19 21:49:36 +09001081
1082 // Following update will in turn trigger
1083 // OPENSTACK_FLOATING_IP_DISASSOCIATED event
1084 osRouterAdminService.updateFloatingIp(neutronFip);
1085 log.info("Updated floating IP {}, due to host removal",
1086 neutronFip.getFloatingIpAddress());
1087 }
1088 }
1089 }
1090 }
Hyunsun Moon44aac662017-02-18 02:07:01 +09001091}