blob: f20eb5194cabcfc1551ff3394a6f4708c7aa161c [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;
Hyunsun Moon44aac662017-02-18 02:07:01 +090021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.MacAddress;
daniel parkee8700b2017-05-11 15:50:03 +090029import org.onlab.packet.VlanId;
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 Li24ec59f2018-05-23 19:01:25 +090035import org.onosproject.net.DeviceId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090036import org.onosproject.net.PortNumber;
37import org.onosproject.net.device.DeviceService;
38import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.TrafficSelector;
41import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090042import org.onosproject.openstacknetworking.api.Constants;
daniel park32b42202018-03-14 16:53:44 +090043import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Hyunsun Moon44aac662017-02-18 02:07:01 +090044import org.onosproject.openstacknetworking.api.InstancePort;
Jian Liec5c32b2018-07-13 14:28:58 +090045import org.onosproject.openstacknetworking.api.InstancePortAdminService;
Jian Li24ec59f2018-05-23 19:01:25 +090046import org.onosproject.openstacknetworking.api.InstancePortEvent;
47import org.onosproject.openstacknetworking.api.InstancePortListener;
sanghodc375372017-06-08 10:41:30 +090048import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Jian Lie1a39032018-06-19 21:49:36 +090049import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
50import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
sanghodc375372017-06-08 10:41:30 +090051import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lif3a28b02018-06-11 21:29:13 +090052import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090053import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
54import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
Jian Li8f64feb2018-07-24 13:20:16 +090055import org.onosproject.openstacknetworking.api.PreCommitPortService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090056import org.onosproject.openstacknode.api.OpenstackNode;
57import org.onosproject.openstacknode.api.OpenstackNodeEvent;
58import org.onosproject.openstacknode.api.OpenstackNodeListener;
59import org.onosproject.openstacknode.api.OpenstackNodeService;
daniel parkeeb8e042018-02-21 14:06:58 +090060import org.openstack4j.model.network.ExternalGateway;
Hyunsun Moon44aac662017-02-18 02:07:01 +090061import org.openstack4j.model.network.NetFloatingIP;
62import org.openstack4j.model.network.Network;
daniel parkee8700b2017-05-11 15:50:03 +090063import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090064import org.openstack4j.model.network.Port;
daniel parkeeb8e042018-02-21 14:06:58 +090065import org.openstack4j.model.network.Router;
66import org.openstack4j.model.network.RouterInterface;
67import org.openstack4j.model.network.Subnet;
Jian Lif3a28b02018-06-11 21:29:13 +090068import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090069import org.slf4j.Logger;
70import org.slf4j.LoggerFactory;
71
72import java.util.Objects;
Jian Li99892e92018-04-13 14:59:39 +090073import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090074import java.util.concurrent.ExecutorService;
75
76import static java.util.concurrent.Executors.newSingleThreadExecutor;
77import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090078import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
79import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
daniel parkeeb8e042018-02-21 14:06:58 +090080import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE;
sanghodc375372017-06-08 10:41:30 +090081import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
82import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_INTERNAL;
daniel parkeeb8e042018-02-21 14:06:58 +090083import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
Jian Lie1a39032018-06-19 21:49:36 +090084import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_ENDED;
85import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_STARTED;
Jian Li8f64feb2018-07-24 13:20:16 +090086import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
Jian Li24ec59f2018-05-23 19:01:25 +090087import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
Jian Li1064e4f2018-05-29 16:16:53 +090088import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Jian Li24ec59f2018-05-23 19:01:25 +090089import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
Jian Liec5c32b2018-07-13 14:28:58 +090090import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Li26949762018-03-30 15:46:37 +090091import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090092import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090093
94/**
95 * Handles OpenStack floating IP events.
96 */
97@Component(immediate = true)
98public class OpenstackRoutingFloatingIpHandler {
99
100 private final Logger log = LoggerFactory.getLogger(getClass());
101
102 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800103 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type %s";
Hyunsun Moon44aac662017-02-18 02:07:01 +0900104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected CoreService coreService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DeviceService deviceService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900112 protected LeadershipService leadershipService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected ClusterService clusterService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected OpenstackNodeService osNodeService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liec5c32b2018-07-13 14:28:58 +0900121 protected InstancePortAdminService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900124 protected OpenstackRouterAdminService osRouterAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected OpenstackNetworkService osNetworkService;
128
sanghodc375372017-06-08 10:41:30 +0900129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected OpenstackFlowRuleService osFlowRuleService;
131
Jian Li8f64feb2018-07-24 13:20:16 +0900132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected PreCommitPortService preCommitPortService;
134
Hyunsun Moon44aac662017-02-18 02:07:01 +0900135 private final ExecutorService eventExecutor = newSingleThreadExecutor(
136 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Jian Li24ec59f2018-05-23 19:01:25 +0900137 private final OpenstackRouterListener floatingIpListener = new InternalFloatingIpListener();
138 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900139 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
Jian Lie1a39032018-06-19 21:49:36 +0900140 private final OpenstackNetworkListener osNetworkListener = new InternalOpenstackNetworkListener();
141 private final InstancePortListener instPortListener = new InternalInstancePortListener();
142
Hyunsun Moon44aac662017-02-18 02:07:01 +0900143 private ApplicationId appId;
144 private NodeId localNodeId;
145
146 @Activate
147 protected void activate() {
148 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
149 localNodeId = clusterService.getLocalNode().id();
150 leadershipService.runForLeadership(appId.name());
Jian Li24ec59f2018-05-23 19:01:25 +0900151 osRouterAdminService.addListener(floatingIpListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900152 osNodeService.addListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900153 instancePortService.addListener(instancePortListener);
Jian Lie1a39032018-06-19 21:49:36 +0900154 osNodeService.addListener(osNodeListener);
155 osNetworkService.addListener(osNetworkListener);
156 instancePortService.addListener(instPortListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900157
158 log.info("Started");
159 }
160
161 @Deactivate
162 protected void deactivate() {
Jian Li24ec59f2018-05-23 19:01:25 +0900163 instancePortService.removeListener(instancePortListener);
Jian Lie1a39032018-06-19 21:49:36 +0900164 instancePortService.removeListener(instPortListener);
165 osNetworkService.removeListener(osNetworkListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900166 osNodeService.removeListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900167 osRouterAdminService.removeListener(floatingIpListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900168 leadershipService.withdraw(appId.name());
169 eventExecutor.shutdown();
170
171 log.info("Stopped");
172 }
173
Hyunsun Moon44aac662017-02-18 02:07:01 +0900174 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
Jian Li1064e4f2018-05-29 16:16:53 +0900175 OpenstackNode gateway, boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900176 Network osNet = osNetworkService.network(osPort.getNetworkId());
Jian Li8f64feb2018-07-24 13:20:16 +0900177
Hyunsun Moon44aac662017-02-18 02:07:01 +0900178 if (osNet == null) {
Jian Li71670d12018-03-02 21:31:07 +0900179 final String errorFormat = ERR_FLOW + "no network(%s) exists";
180 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900181 floatingIp.getFloatingIpAddress(),
182 osPort.getNetworkId());
183 throw new IllegalStateException(error);
184 }
185
186 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
daniel parkc2a2ed62018-04-10 15:17:42 +0900187 log.trace("Mac address of openstack port: {}", srcMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900188 InstancePort instPort = instancePortService.instancePort(srcMac);
Jian Li99892e92018-04-13 14:59:39 +0900189
Jian Li99892e92018-04-13 14:59:39 +0900190 if (instPort == null) {
Jian Li71670d12018-03-02 21:31:07 +0900191 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
192 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900193 floatingIp.getFloatingIpAddress(), srcMac);
194 throw new IllegalStateException(error);
195 }
196
daniel park32b42202018-03-14 16:53:44 +0900197 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
198 if (externalPeerRouter == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900199 final String errorFormat = ERR_FLOW + "no external peer router found";
200 throw new IllegalStateException(errorFormat);
daniel park32b42202018-03-14 16:53:44 +0900201 }
202
Jian Li8f64feb2018-07-24 13:20:16 +0900203 if (install) {
204 preCommitPortService.subscribePreCommit(osPort.getId(),
205 OPENSTACK_PORT_PRE_REMOVE, this.getClass().getName());
Jian Lida03ce92018-07-24 21:41:53 +0900206 log.info("Subscribed the port {} on listening pre-remove event", osPort.getId());
Jian Li8f64feb2018-07-24 13:20:16 +0900207 } else {
208 preCommitPortService.unsubscribePreCommit(osPort.getId(),
209 OPENSTACK_PORT_PRE_REMOVE, this.getClass().getName());
Jian Lida03ce92018-07-24 21:41:53 +0900210 log.info("Unsubscribed the port {} on listening pre-remove event", osPort.getId());
Jian Li8f64feb2018-07-24 13:20:16 +0900211 }
212
Jian Lide679782018-06-05 01:41:29 +0900213 updateComputeNodeRules(instPort, osNet, gateway, install);
214 updateGatewayNodeRules(floatingIp, instPort, osNet, externalPeerRouter, gateway, install);
215
216 // FIXME: downstream internal rules are still duplicated in all gateway nodes
217 // need to make the internal rules de-duplicated sooner or later
218 setDownstreamInternalRules(floatingIp, osNet, instPort, install);
219
220 // TODO: need to refactor setUpstreamRules if possible
daniel park32b42202018-03-14 16:53:44 +0900221 setUpstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
Jian Li8f64feb2018-07-24 13:20:16 +0900222
daniel parkc2a2ed62018-04-10 15:17:42 +0900223 log.trace("Succeeded to set flow rules for floating ip {}:{} and install: {}",
224 floatingIp.getFloatingIpAddress(),
225 floatingIp.getFixedIpAddress(),
226 install);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900227 }
228
Jian Lide679782018-06-05 01:41:29 +0900229 private synchronized void updateGatewayNodeRules(NetFloatingIP fip,
230 InstancePort instPort,
231 Network osNet,
232 ExternalPeerRouter router,
233 OpenstackNode gateway,
234 boolean install) {
235
236 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
237 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
238 finalGws.addAll(ImmutableSet.copyOf(completedGws));
239
Jian Lia171a432018-06-11 11:52:11 +0900240
241 if (gateway == null) {
242 // these are floating IP related cases...
243 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
244 ImmutableSet.copyOf(finalGws), install);
245
Jian Lide679782018-06-05 01:41:29 +0900246 } else {
Jian Lia171a432018-06-11 11:52:11 +0900247 // these are openstack node related cases...
248 if (install) {
249 if (completedGws.contains(gateway)) {
250 if (completedGws.size() > 1) {
251 finalGws.remove(gateway);
252 if (fip.getPortId() != null) {
253 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
254 ImmutableSet.copyOf(finalGws), false);
255 finalGws.add(gateway);
256 }
257 }
Jian Lide679782018-06-05 01:41:29 +0900258 if (fip.getPortId() != null) {
259 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
260 ImmutableSet.copyOf(finalGws), true);
261 }
Jian Lia171a432018-06-11 11:52:11 +0900262 } else {
263 log.warn("Detected node should be included in completed gateway set");
Jian Lide679782018-06-05 01:41:29 +0900264 }
265 } else {
Jian Lia171a432018-06-11 11:52:11 +0900266 if (!completedGws.contains(gateway)) {
267 if (completedGws.size() >= 1) {
268 if (fip.getPortId() != null) {
269 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
270 ImmutableSet.copyOf(finalGws), true);
271 }
272 }
273 } else {
274 log.warn("Detected node should NOT be included in completed gateway set");
275 }
Jian Lide679782018-06-05 01:41:29 +0900276 }
277 }
278 }
279
280 private synchronized void updateComputeNodeRules(InstancePort instPort,
281 Network osNet,
282 OpenstackNode gateway,
283 boolean install) {
Jian Li1064e4f2018-05-29 16:16:53 +0900284
285 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
286 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
287 finalGws.addAll(ImmutableSet.copyOf(completedGws));
288
289 if (gateway == null) {
290 // these are floating IP related cases...
291 setComputeNodeToGatewayHelper(instPort, osNet,
292 ImmutableSet.copyOf(finalGws), install);
Jian Lide679782018-06-05 01:41:29 +0900293
Jian Li1064e4f2018-05-29 16:16:53 +0900294 } else {
295 // these are openstack node related cases...
296 if (install) {
297 if (completedGws.contains(gateway)) {
298 if (completedGws.size() > 1) {
299 finalGws.remove(gateway);
300 setComputeNodeToGatewayHelper(instPort, osNet,
301 ImmutableSet.copyOf(finalGws), false);
302 finalGws.add(gateway);
303 }
304
305 setComputeNodeToGatewayHelper(instPort, osNet,
306 ImmutableSet.copyOf(finalGws), true);
307 } else {
308 log.warn("Detected node should be included in completed gateway set");
309 }
310 } else {
311 if (!completedGws.contains(gateway)) {
312 finalGws.add(gateway);
313 setComputeNodeToGatewayHelper(instPort, osNet,
314 ImmutableSet.copyOf(finalGws), false);
315 finalGws.remove(gateway);
316 if (completedGws.size() >= 1) {
317 setComputeNodeToGatewayHelper(instPort, osNet,
318 ImmutableSet.copyOf(finalGws), true);
319 }
320 } else {
321 log.warn("Detected node should NOT be included in completed gateway set");
322 }
323 }
324 }
325 }
326
327 // a helper method
328 private void setComputeNodeToGatewayHelper(InstancePort instPort,
329 Network osNet,
330 Set<OpenstackNode> gateways,
331 boolean install) {
daniel parkeeb8e042018-02-21 14:06:58 +0900332 TrafficTreatment treatment;
333
334 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
335 .matchEthType(Ethernet.TYPE_IPV4)
336 .matchIPSrc(instPort.ipAddress().toIpPrefix())
337 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
338
339 switch (osNet.getNetworkType()) {
340 case VXLAN:
341 sBuilder.matchTunnelId(Long.parseLong(osNet.getProviderSegID()));
342 break;
343 case VLAN:
344 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
345 break;
346 default:
347 final String error = String.format(
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800348 ERR_UNSUPPORTED_NET_TYPE,
daniel parkeeb8e042018-02-21 14:06:58 +0900349 osNet.getNetworkType().toString());
350 throw new IllegalStateException(error);
351 }
352
Jian Li1064e4f2018-05-29 16:16:53 +0900353 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
354
daniel parkeeb8e042018-02-21 14:06:58 +0900355 if (selectedGatewayNode == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900356 final String errorFormat = ERR_FLOW + "no gateway node selected";
357 throw new IllegalStateException(errorFormat);
daniel parkeeb8e042018-02-21 14:06:58 +0900358 }
359 treatment = DefaultTrafficTreatment.builder()
360 .extension(buildExtension(
361 deviceService,
362 instPort.deviceId(),
363 selectedGatewayNode.dataIp().getIp4Address()),
364 instPort.deviceId())
365 .setOutput(osNodeService.node(instPort.deviceId()).tunnelPortNum())
366 .build();
367
368 osFlowRuleService.setRule(
369 appId,
370 instPort.deviceId(),
371 sBuilder.build(),
372 treatment,
373 PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE,
374 ROUTING_TABLE,
375 install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900376 log.trace("Succeeded to set flow rules from compute node to gateway on compute node");
daniel parkeeb8e042018-02-21 14:06:58 +0900377 }
378
Jian Lide679782018-06-05 01:41:29 +0900379 private void setDownstreamInternalRules(NetFloatingIP floatingIp,
380 Network osNet,
381 InstancePort instPort,
382 boolean install) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900383 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
384 if (cNode == null) {
385 final String error = String.format("Cannot find openstack node for device %s",
386 instPort.deviceId());
387 throw new IllegalStateException(error);
388 }
389 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900390 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
391 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900392 throw new IllegalStateException(error);
393 }
394 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900395 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
396 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900397 throw new IllegalStateException(error);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900398 }
399
400 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900401
Jian Lide679782018-06-05 01:41:29 +0900402 // TODO: following code snippet will be refactored sooner or later
Hyunsun Moon0d457362017-06-27 17:19:41 +0900403 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900404 // access from one VM to the others via floating IP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900405 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
406 .matchEthType(Ethernet.TYPE_IPV4)
407 .matchIPDst(floating.toIpPrefix())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900408 .matchInPort(gNode.tunnelPortNum())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900409 .build();
410
daniel parkee8700b2017-05-11 15:50:03 +0900411 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900412 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
413 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900414 .setIpDst(instPort.ipAddress().getIp4Address());
415
416 switch (osNet.getNetworkType()) {
417 case VXLAN:
418 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
419 .extension(buildExtension(
420 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900421 gNode.intgBridge(),
422 cNode.dataIp().getIp4Address()),
423 gNode.intgBridge())
daniel parkee8700b2017-05-11 15:50:03 +0900424 .setOutput(PortNumber.IN_PORT);
425 break;
426 case VLAN:
427 internalBuilder.pushVlan()
428 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
429 .setOutput(PortNumber.IN_PORT);
430 break;
431 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800432 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900433 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900434 throw new IllegalStateException(error);
435 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900436
sanghodc375372017-06-08 10:41:30 +0900437 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900438 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900439 gNode.intgBridge(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900440 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900441 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900442 PRIORITY_FLOATING_INTERNAL,
sanghodc375372017-06-08 10:41:30 +0900443 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900444 install);
445 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900446 log.trace("Succeeded to set flow rules for downstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900447 }
448
Jian Lide679782018-06-05 01:41:29 +0900449 private void setDownstreamExternalRulesHelper(NetFloatingIP floatingIp,
450 Network osNet,
451 InstancePort instPort,
452 ExternalPeerRouter externalPeerRouter,
453 Set<OpenstackNode> gateways, boolean install) {
454 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
455 if (cNode == null) {
456 final String error = String.format("Cannot find openstack node for device %s",
457 instPort.deviceId());
458 throw new IllegalStateException(error);
459 }
460 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
461 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
462 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
463 throw new IllegalStateException(error);
464 }
465 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
466 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
467 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
468 throw new IllegalStateException(error);
469 }
470
471 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
472
473 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
474
475 if (selectedGatewayNode == null) {
476 final String errorFormat = ERR_FLOW + "no gateway node selected";
477 throw new IllegalStateException(errorFormat);
478 }
479
480 TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
481 .matchEthType(Ethernet.TYPE_IPV4)
482 .matchIPDst(floating.toIpPrefix());
483
484 TrafficTreatment.Builder externalTreatmentBuilder = DefaultTrafficTreatment.builder()
485 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
486 .setEthDst(instPort.macAddress())
487 .setIpDst(instPort.ipAddress().getIp4Address());
488
Jian Li5e2ad4a2018-07-16 13:40:53 +0900489 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
490 externalSelectorBuilder.matchVlanId(externalPeerRouter.vlanId()).build();
Jian Lide679782018-06-05 01:41:29 +0900491 externalTreatmentBuilder.popVlan();
492 }
493
494 switch (osNet.getNetworkType()) {
495 case VXLAN:
496 externalTreatmentBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
497 .extension(buildExtension(
498 deviceService,
499 selectedGatewayNode.intgBridge(),
500 cNode.dataIp().getIp4Address()),
501 selectedGatewayNode.intgBridge())
502 .setOutput(selectedGatewayNode.tunnelPortNum());
503 break;
504 case VLAN:
505 externalTreatmentBuilder.pushVlan()
506 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
507 .setOutput(selectedGatewayNode.vlanPortNum());
508 break;
509 default:
510 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
511 osNet.getNetworkType());
512 throw new IllegalStateException(error);
513 }
514
515 osFlowRuleService.setRule(
516 appId,
517 selectedGatewayNode.intgBridge(),
518 externalSelectorBuilder.build(),
519 externalTreatmentBuilder.build(),
520 PRIORITY_FLOATING_EXTERNAL,
521 GW_COMMON_TABLE,
522 install);
523 }
524
Hyunsun Moon44aac662017-02-18 02:07:01 +0900525 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900526 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
527 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900528 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900529 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900530 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900531 .matchIPSrc(instPort.ipAddress().toIpPrefix());
532
533 switch (osNet.getNetworkType()) {
534 case VXLAN:
535 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
536 break;
537 case VLAN:
538 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
539 break;
540 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800541 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900542 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900543 throw new IllegalStateException(error);
544 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900545
Daniel Park75e3d7f2018-05-29 14:43:53 +0900546 TrafficSelector selector = sBuilder.build();
daniel parkeeb8e042018-02-21 14:06:58 +0900547
Hyunsun Moon0d457362017-06-27 17:19:41 +0900548 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900549 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
550 .setIpSrc(floating.getIp4Address())
551 .setEthSrc(instPort.macAddress())
Jian Li5e2ad4a2018-07-16 13:40:53 +0900552 .setEthDst(externalPeerRouter.macAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900553
Daniel Park75e3d7f2018-05-29 14:43:53 +0900554 if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
555 tBuilder.popVlan();
556 }
557
Jian Li5e2ad4a2018-07-16 13:40:53 +0900558 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
559 tBuilder.pushVlan().setVlanId(externalPeerRouter.vlanId());
Daniel Park75e3d7f2018-05-29 14:43:53 +0900560 }
sanghodc375372017-06-08 10:41:30 +0900561 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900562 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900563 gNode.intgBridge(),
Daniel Park75e3d7f2018-05-29 14:43:53 +0900564 selector,
daniel parkeeb8e042018-02-21 14:06:58 +0900565 tBuilder.setOutput(gNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900566 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900567 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900568 install);
Daniel Park75e3d7f2018-05-29 14:43:53 +0900569 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900570 log.trace("Succeeded to set flow rules for upstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900571 }
572
daniel park32b42202018-03-14 16:53:44 +0900573 private ExternalPeerRouter externalPeerRouter(Network network) {
daniel parkeeb8e042018-02-21 14:06:58 +0900574 if (network == null) {
575 return null;
576 }
577
578 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
579
580 if (subnet == null) {
581 return null;
582 }
583
Jian Lif3a28b02018-06-11 21:29:13 +0900584 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
daniel parkeeb8e042018-02-21 14:06:58 +0900585 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
586 .findAny().orElse(null);
587 if (osRouterIface == null) {
588 return null;
589 }
590
Jian Lif3a28b02018-06-11 21:29:13 +0900591 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
daniel parkeeb8e042018-02-21 14:06:58 +0900592 if (osRouter == null) {
593 return null;
594 }
595 if (osRouter.getExternalGatewayInfo() == null) {
596 return null;
597 }
598
599 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
daniel park65e1c202018-04-03 13:15:28 +0900600 return osNetworkService.externalPeerRouter(exGatewayInfo);
daniel parkeeb8e042018-02-21 14:06:58 +0900601 }
daniel park65e1c202018-04-03 13:15:28 +0900602
Jian Li99892e92018-04-13 14:59:39 +0900603 private void associateFloatingIp(NetFloatingIP osFip) {
604 Port osPort = osNetworkService.port(osFip.getPortId());
605 if (osPort == null) {
606 final String errorFormat = ERR_FLOW + "port(%s) not found";
607 final String error = String.format(errorFormat,
608 osFip.getFloatingIpAddress(), osFip.getPortId());
609 throw new IllegalStateException(error);
610 }
611 // set floating IP rules only if the port is associated to a VM
612 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Lie1a39032018-06-19 21:49:36 +0900613
614 if (instancePortService.instancePort(osPort.getId()) == null) {
Jian Lie1a39032018-06-19 21:49:36 +0900615 return;
616 }
617
Jian Li1064e4f2018-05-29 16:16:53 +0900618 setFloatingIpRules(osFip, osPort, null, true);
Jian Li99892e92018-04-13 14:59:39 +0900619 }
620 }
621
622 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
623 Port osPort = osNetworkService.port(portId);
Jian Lie1a39032018-06-19 21:49:36 +0900624
625 if (osPort == null) {
626 final String errorFormat = ERR_FLOW + "port(%s) not found";
627 final String error = String.format(errorFormat,
628 osFip.getFloatingIpAddress(), osFip.getPortId());
629 throw new IllegalStateException(error);
630 }
631
Jian Li99892e92018-04-13 14:59:39 +0900632 // set floating IP rules only if the port is associated to a VM
633 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Lie1a39032018-06-19 21:49:36 +0900634
Jian Lie1a39032018-06-19 21:49:36 +0900635 if (instancePortService.instancePort(osPort.getId()) == null) {
Jian Li46b74002018-07-15 18:39:08 +0900636 return;
Jian Lie1a39032018-06-19 21:49:36 +0900637 }
638
Jian Li1064e4f2018-05-29 16:16:53 +0900639 setFloatingIpRules(osFip, osPort, null, false);
Jian Li99892e92018-04-13 14:59:39 +0900640 }
641 }
642
Hyunsun Moon0d457362017-06-27 17:19:41 +0900643 private class InternalFloatingIpListener implements OpenstackRouterListener {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900644
645 @Override
646 public boolean isRelevant(OpenstackRouterEvent event) {
647 // do not allow to proceed without leadership
648 NodeId leader = leadershipService.getLeader(appId.name());
649 if (!Objects.equals(localNodeId, leader)) {
650 return false;
651 }
652 return event.floatingIp() != null;
653 }
654
655 @Override
656 public void event(OpenstackRouterEvent event) {
657 switch (event.type()) {
658 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900659 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900660 NetFloatingIP osFip = event.floatingIp();
661 associateFloatingIp(osFip);
662 log.info("Associated floating IP {}:{}",
663 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900664 });
665 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900666 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
667 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900668 NetFloatingIP osFip = event.floatingIp();
669 disassociateFloatingIp(osFip, event.portId());
670 log.info("Disassociated floating IP {}:{}",
671 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900672 });
673 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900674 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900675 eventExecutor.execute(() -> {
676 NetFloatingIP osFip = event.floatingIp();
677 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
678 associateFloatingIp(event.floatingIp());
679 }
680 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
681 });
682 break;
Jian Lia171a432018-06-11 11:52:11 +0900683 case OPENSTACK_FLOATING_IP_REMOVED:
684 eventExecutor.execute(() -> {
685 NetFloatingIP osFip = event.floatingIp();
686 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Lic2403592018-07-18 12:56:45 +0900687 // in case the floating IP is not associated with any port due to
688 // port removal, we simply do not execute floating IP disassociation
689 if (osNetworkService.port(osFip.getPortId()) != null) {
690 disassociateFloatingIp(osFip, osFip.getPortId());
691 }
Jian Lida03ce92018-07-24 21:41:53 +0900692
693 // since we skip floating IP disassociation, we need to
694 // manually unsubscribe the port pre-remove event
695 preCommitPortService.unsubscribePreCommit(osFip.getPortId(),
696 OPENSTACK_PORT_PRE_REMOVE, this.getClass().getName());
697 log.info("Unsubscribed the port {} on listening pre-remove event", osFip.getPortId());
Jian Lia171a432018-06-11 11:52:11 +0900698 }
699 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
700 });
701 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900702 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900703 case OPENSTACK_ROUTER_CREATED:
704 case OPENSTACK_ROUTER_UPDATED:
705 case OPENSTACK_ROUTER_REMOVED:
706 case OPENSTACK_ROUTER_INTERFACE_ADDED:
707 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
708 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
709 default:
710 // do nothing for the other events
711 break;
712 }
713 }
714 }
715
716 private class InternalNodeListener implements OpenstackNodeListener {
717
718 @Override
719 public boolean isRelevant(OpenstackNodeEvent event) {
720 // do not allow to proceed without leadership
721 NodeId leader = leadershipService.getLeader(appId.name());
722 if (!Objects.equals(localNodeId, leader)) {
723 return false;
724 }
725 return event.subject().type() == GATEWAY;
726 }
727
728 @Override
729 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900730
731 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900732 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900733 eventExecutor.execute(() -> {
Jian Lif3a28b02018-06-11 21:29:13 +0900734 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
Jian Lie1a39032018-06-19 21:49:36 +0900735
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900736 if (Strings.isNullOrEmpty(fip.getPortId())) {
737 continue;
738 }
Jian Lie1a39032018-06-19 21:49:36 +0900739
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900740 Port osPort = osNetworkService.port(fip.getPortId());
741 if (osPort == null) {
742 log.warn("Failed to set floating IP {}", fip.getId());
743 continue;
744 }
Jian Lie1a39032018-06-19 21:49:36 +0900745
Jian Li46b74002018-07-15 18:39:08 +0900746 if (instancePortService.instancePort(fip.getPortId()) == null) {
Jian Lie1a39032018-06-19 21:49:36 +0900747 continue;
748 }
Jian Liec5c32b2018-07-13 14:28:58 +0900749
Jian Li1064e4f2018-05-29 16:16:53 +0900750 setFloatingIpRules(fip, osPort, event.subject(), true);
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900751 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900752 });
753 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900754 case OPENSTACK_NODE_INCOMPLETE:
Jian Li1064e4f2018-05-29 16:16:53 +0900755 eventExecutor.execute(() -> {
Jian Lif3a28b02018-06-11 21:29:13 +0900756 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
Jian Li1064e4f2018-05-29 16:16:53 +0900757 if (Strings.isNullOrEmpty(fip.getPortId())) {
758 continue;
759 }
760 Port osPort = osNetworkService.port(fip.getPortId());
761 if (osPort == null) {
762 log.warn("Failed to set floating IP {}", fip.getId());
763 continue;
764 }
765 Network osNet = osNetworkService.network(osPort.getNetworkId());
766 if (osNet == null) {
767 final String errorFormat = ERR_FLOW + "no network(%s) exists";
768 final String error = String.format(errorFormat,
769 fip.getFloatingIpAddress(),
770 osPort.getNetworkId());
771 throw new IllegalStateException(error);
772 }
773 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
774 log.trace("Mac address of openstack port: {}", srcMac);
775 InstancePort instPort = instancePortService.instancePort(srcMac);
776
777 if (instPort == null) {
778 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
779 final String error = String.format(errorFormat,
780 fip.getFloatingIpAddress(), srcMac);
781 throw new IllegalStateException(error);
782 }
783
Jian Lide679782018-06-05 01:41:29 +0900784 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
785 if (externalPeerRouter == null) {
786 final String errorFormat = ERR_FLOW + "no external peer router found";
787 throw new IllegalStateException(errorFormat);
788 }
789
790 updateComputeNodeRules(instPort, osNet, event.subject(), false);
791 updateGatewayNodeRules(fip, instPort, osNet,
792 externalPeerRouter, event.subject(), false);
Jian Li1064e4f2018-05-29 16:16:53 +0900793 }
794 });
795 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900796 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900797 // do nothing
Hyunsun Moon44aac662017-02-18 02:07:01 +0900798 break;
799 }
800 }
801 }
Jian Li99892e92018-04-13 14:59:39 +0900802
Jian Li24ec59f2018-05-23 19:01:25 +0900803 private class InternalInstancePortListener implements InstancePortListener {
804
805 @Override
806 public boolean isRelevant(InstancePortEvent event) {
Jian Li24ec59f2018-05-23 19:01:25 +0900807
Jian Lie1a39032018-06-19 21:49:36 +0900808 if (event.type() == OPENSTACK_INSTANCE_MIGRATION_ENDED ||
809 event.type() == OPENSTACK_INSTANCE_MIGRATION_STARTED) {
810 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
811 NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
812
813 // we check the possible NPE to avoid duplicated null check
814 // for OPENSTACK_INSTANCE_MIGRATION_ENDED and
815 // OPENSTACK_INSTANCE_MIGRATION_STARTED cases
816 if (fip == null || !isAssociatedWithVM(osNetworkService, fip)) {
817 return false;
818 }
819 }
820
821 // do not allow to proceed without leadership
822 NodeId leader = leadershipService.getLeader(appId.name());
823
824 return Objects.equals(localNodeId, leader);
Jian Li24ec59f2018-05-23 19:01:25 +0900825 }
826
827 @Override
828 public void event(InstancePortEvent event) {
Jian Lie1a39032018-06-19 21:49:36 +0900829 InstancePort instPort = event.subject();
Jian Li24ec59f2018-05-23 19:01:25 +0900830 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
831
Jian Lie1a39032018-06-19 21:49:36 +0900832 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
833 NetFloatingIP fip;
834 Port osPort;
835 Network osNet;
836 ExternalPeerRouter externalPeerRouter;
Jian Li24ec59f2018-05-23 19:01:25 +0900837
838 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900839 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Li77323c52018-06-24 01:26:18 +0900840 if (instPort != null && instPort.portId() != null) {
841 String portId = instPort.portId();
Jian Lie1a39032018-06-19 21:49:36 +0900842
Jian Li77323c52018-06-24 01:26:18 +0900843 Port port = osNetworkService.port(portId);
844
Jian Li46b74002018-07-15 18:39:08 +0900845 osRouterAdminService.floatingIps().stream()
846 .filter(f -> f.getPortId() != null)
847 .filter(f -> f.getPortId().equals(instPort.portId()))
848 .forEach(f -> setFloatingIpRules(f, port, null, true));
Jian Lie1a39032018-06-19 21:49:36 +0900849 }
850
851 break;
852
Jian Li24ec59f2018-05-23 19:01:25 +0900853 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Lie1a39032018-06-19 21:49:36 +0900854
855 fip = associatedFloatingIp(event.subject(), ips);
856
857 if (fip == null) {
858 return;
859 }
860
861 osPort = osNetworkService.port(fip.getPortId());
862 osNet = osNetworkService.network(osPort.getNetworkId());
863 externalPeerRouter = externalPeerRouter(osNet);
864
865 if (externalPeerRouter == null) {
866 final String errorFormat = ERR_FLOW + "no external peer router found";
867 throw new IllegalStateException(errorFormat);
868 }
869
Jian Li24ec59f2018-05-23 19:01:25 +0900870 eventExecutor.execute(() -> {
871
872 // since downstream internal rules are located in all gateway
873 // nodes, therefore, we simply update the rules with new compute node info
874 setDownstreamInternalRules(fip, osNet, event.subject(), true);
875
876 // since DownstreamExternal rules should only be placed in
877 // corresponding gateway node, we need to install new rule to
878 // the corresponding gateway node
879 setDownstreamExternalRulesHelper(fip, osNet,
880 event.subject(), externalPeerRouter, gateways, true);
881
882 // since ComputeNodeToGateway rules should only be placed in
883 // corresponding compute node, we need to install new rule to
884 // the target compute node, and remove rules from original node
885 setComputeNodeToGatewayHelper(event.subject(), osNet, gateways, true);
Jian Li24ec59f2018-05-23 19:01:25 +0900886 });
887 break;
888 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
889
Jian Liec5c32b2018-07-13 14:28:58 +0900890 InstancePort revisedInstPort = swapStaleLocation(event.subject());
891
892 fip = associatedFloatingIp(revisedInstPort, ips);
Jian Lie1a39032018-06-19 21:49:36 +0900893
894 if (fip == null) {
895 return;
896 }
897
898 osPort = osNetworkService.port(fip.getPortId());
899 osNet = osNetworkService.network(osPort.getNetworkId());
900 externalPeerRouter = externalPeerRouter(osNet);
901
902 if (externalPeerRouter == null) {
903 final String errorFormat = ERR_FLOW + "no external peer router found";
904 throw new IllegalStateException(errorFormat);
905 }
906
907 // If we only have one gateway, we simply do not remove any
Jian Li24ec59f2018-05-23 19:01:25 +0900908 // flow rules from either gateway or compute node
909 if (gateways.size() == 1) {
910 return;
911 }
912
Jian Lie1a39032018-06-19 21:49:36 +0900913 // Checks whether the destination compute node's device id
Jian Li24ec59f2018-05-23 19:01:25 +0900914 // has identical gateway hash or not
915 // if it is true, we simply do not remove the rules, as
916 // it has been overwritten at port detention event
917 // if it is false, we will remove the rules
Jian Liec5c32b2018-07-13 14:28:58 +0900918 DeviceId newDeviceId = event.subject().deviceId();
919 DeviceId oldDeviceId = revisedInstPort.deviceId();
Jian Li24ec59f2018-05-23 19:01:25 +0900920
921 OpenstackNode oldGateway = getGwByComputeDevId(gateways, oldDeviceId);
922 OpenstackNode newGateway = getGwByComputeDevId(gateways, newDeviceId);
923
924 if (oldGateway != null && oldGateway.equals(newGateway)) {
925 return;
926 }
927
928 eventExecutor.execute(() -> {
929
Jian Lie1a39032018-06-19 21:49:36 +0900930 // We need to remove the old ComputeNodeToGateway rules from
Jian Li24ec59f2018-05-23 19:01:25 +0900931 // original compute node
Jian Liec5c32b2018-07-13 14:28:58 +0900932 setComputeNodeToGatewayHelper(revisedInstPort, osNet, gateways, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900933
Jian Lie1a39032018-06-19 21:49:36 +0900934 // Since DownstreamExternal rules should only be placed in
Jian Li24ec59f2018-05-23 19:01:25 +0900935 // corresponding gateway node, we need to remove old rule from
936 // the corresponding gateway node
Jian Liec5c32b2018-07-13 14:28:58 +0900937 setDownstreamExternalRulesHelper(fip, osNet, revisedInstPort,
938 externalPeerRouter, gateways, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900939 });
940 break;
941 default:
942 break;
943 }
944 }
945 }
Jian Lie1a39032018-06-19 21:49:36 +0900946
947 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
948
949 @Override
950 public boolean isRelevant(OpenstackNetworkEvent event) {
951 // do not allow to proceed without leadership
952 NodeId leader = leadershipService.getLeader(appId.name());
Jian Lida03ce92018-07-24 21:41:53 +0900953 return Objects.equals(localNodeId, leader);
Jian Lie1a39032018-06-19 21:49:36 +0900954 }
955
956 @Override
957 public void event(OpenstackNetworkEvent event) {
Jian Li8f64feb2018-07-24 13:20:16 +0900958 String portId;
959
Jian Lie1a39032018-06-19 21:49:36 +0900960 switch (event.type()) {
Jian Li8f64feb2018-07-24 13:20:16 +0900961 case OPENSTACK_PORT_PRE_REMOVE:
962 portId = event.port().getId();
Jian Liec5c32b2018-07-13 14:28:58 +0900963
964 InstancePort instPort = instancePortService.instancePort(portId);
Jian Liec5c32b2018-07-13 14:28:58 +0900965 updateFipStore(instPort);
966
Jian Li8f64feb2018-07-24 13:20:16 +0900967 break;
968 case OPENSTACK_PORT_REMOVED:
969 portId = event.port().getId();
970
971 instancePortService.removeInstancePort(portId);
972
Jian Lie1a39032018-06-19 21:49:36 +0900973 break;
974 default:
975 break;
976 }
977 }
978
979 private void updateFipStore(InstancePort port) {
980
981 if (port == null) {
982 return;
983 }
984
985 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
986 for (NetFloatingIP fip : ips) {
987 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
988 continue;
989 }
990 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
991 continue;
992 }
993 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
994 NeutronFloatingIP neutronFip = (NeutronFloatingIP) fip;
995 // invalidate bound fixed IP and port
996 neutronFip.setFixedIpAddress(null);
997 neutronFip.setPortId(null);
Jian Lie1a39032018-06-19 21:49:36 +0900998
999 // Following update will in turn trigger
1000 // OPENSTACK_FLOATING_IP_DISASSOCIATED event
1001 osRouterAdminService.updateFloatingIp(neutronFip);
1002 log.info("Updated floating IP {}, due to host removal",
1003 neutronFip.getFloatingIpAddress());
1004 }
1005 }
1006 }
1007 }
Hyunsun Moon44aac662017-02-18 02:07:01 +09001008}