blob: 593fda86771f00d1469e252833787c59f7e894ea [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Hyunsun Moon44aac662017-02-18 02:07:01 +09003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.openstacknetworking.impl;
17
18import com.google.common.base.Strings;
Jian Li1064e4f2018-05-29 16:16:53 +090019import com.google.common.collect.ImmutableSet;
Jian Li99892e92018-04-13 14:59:39 +090020import com.google.common.collect.Maps;
Jian Li1064e4f2018-05-29 16:16:53 +090021import com.google.common.collect.Sets;
Hyunsun Moon44aac662017-02-18 02:07:01 +090022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.Ethernet;
28import org.onlab.packet.IpAddress;
29import org.onlab.packet.MacAddress;
daniel parkee8700b2017-05-11 15:50:03 +090030import org.onlab.packet.VlanId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090031import org.onosproject.cluster.ClusterService;
32import org.onosproject.cluster.LeadershipService;
33import org.onosproject.cluster.NodeId;
34import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Jian Li24ec59f2018-05-23 19:01:25 +090036import org.onosproject.net.DeviceId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090037import org.onosproject.net.PortNumber;
38import org.onosproject.net.device.DeviceService;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090043import org.onosproject.openstacknetworking.api.Constants;
daniel park32b42202018-03-14 16:53:44 +090044import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Hyunsun Moon44aac662017-02-18 02:07:01 +090045import org.onosproject.openstacknetworking.api.InstancePort;
Jian Li24ec59f2018-05-23 19:01:25 +090046import org.onosproject.openstacknetworking.api.InstancePortEvent;
47import org.onosproject.openstacknetworking.api.InstancePortListener;
Hyunsun Moon44aac662017-02-18 02:07:01 +090048import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090049import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Jian Lie1a39032018-06-19 21:49:36 +090050import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
51import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
sanghodc375372017-06-08 10:41:30 +090052import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lif3a28b02018-06-11 21:29:13 +090053import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090054import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
55import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
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
Jian Li99892e92018-04-13 14:59:39 +090072import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090073import java.util.Objects;
Jian Li99892e92018-04-13 14:59:39 +090074import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090075import java.util.concurrent.ExecutorService;
76
77import static java.util.concurrent.Executors.newSingleThreadExecutor;
78import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090079import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
80import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
daniel parkeeb8e042018-02-21 14:06:58 +090081import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE;
sanghodc375372017-06-08 10:41:30 +090082import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
83import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_INTERNAL;
daniel parkeeb8e042018-02-21 14:06:58 +090084import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
Jian Lie1a39032018-06-19 21:49:36 +090085import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_ENDED;
86import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_STARTED;
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 Li26949762018-03-30 15:46:37 +090090import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090091import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090092
93/**
94 * Handles OpenStack floating IP events.
95 */
96@Component(immediate = true)
97public class OpenstackRoutingFloatingIpHandler {
98
99 private final Logger log = LoggerFactory.getLogger(getClass());
100
101 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800102 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type %s";
Hyunsun Moon44aac662017-02-18 02:07:01 +0900103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected CoreService coreService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected DeviceService deviceService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900111 protected LeadershipService leadershipService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected ClusterService clusterService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected OpenstackNodeService osNodeService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected InstancePortService instancePortService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900123 protected OpenstackRouterAdminService osRouterAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected OpenstackNetworkService osNetworkService;
127
sanghodc375372017-06-08 10:41:30 +0900128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected OpenstackFlowRuleService osFlowRuleService;
130
Hyunsun Moon44aac662017-02-18 02:07:01 +0900131 private final ExecutorService eventExecutor = newSingleThreadExecutor(
132 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Jian Li24ec59f2018-05-23 19:01:25 +0900133 private final OpenstackRouterListener floatingIpListener = new InternalFloatingIpListener();
134 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900135 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
Jian Lie1a39032018-06-19 21:49:36 +0900136 private final Map<String, DeviceId> migrationPool = Maps.newConcurrentMap();
137 private final OpenstackNetworkListener osNetworkListener = new InternalOpenstackNetworkListener();
138 private final InstancePortListener instPortListener = new InternalInstancePortListener();
139
140 private Map<String, Port> terminatedOsPorts = Maps.newConcurrentMap();
141 private Map<String, InstancePort> terminatedInstPorts = Maps.newConcurrentMap();
142 private Map<String, InstancePort> tobeRemovedInstPorts = Maps.newConcurrentMap();
143 private Map<String, NetFloatingIP> pendingInstPortIds = Maps.newConcurrentMap();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900144
145 private ApplicationId appId;
146 private NodeId localNodeId;
147
148 @Activate
149 protected void activate() {
150 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
151 localNodeId = clusterService.getLocalNode().id();
152 leadershipService.runForLeadership(appId.name());
Jian Li24ec59f2018-05-23 19:01:25 +0900153 osRouterAdminService.addListener(floatingIpListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900154 osNodeService.addListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900155 instancePortService.addListener(instancePortListener);
Jian Lie1a39032018-06-19 21:49:36 +0900156 osNodeService.addListener(osNodeListener);
157 osNetworkService.addListener(osNetworkListener);
158 instancePortService.addListener(instPortListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900159
160 log.info("Started");
161 }
162
163 @Deactivate
164 protected void deactivate() {
Jian Li24ec59f2018-05-23 19:01:25 +0900165 instancePortService.removeListener(instancePortListener);
Jian Lie1a39032018-06-19 21:49:36 +0900166 instancePortService.removeListener(instPortListener);
167 osNetworkService.removeListener(osNetworkListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900168 osNodeService.removeListener(osNodeListener);
Jian Li24ec59f2018-05-23 19:01:25 +0900169 osRouterAdminService.removeListener(floatingIpListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900170 leadershipService.withdraw(appId.name());
171 eventExecutor.shutdown();
172
173 log.info("Stopped");
174 }
175
Hyunsun Moon44aac662017-02-18 02:07:01 +0900176 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
Jian Li1064e4f2018-05-29 16:16:53 +0900177 OpenstackNode gateway, boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900178 Network osNet = osNetworkService.network(osPort.getNetworkId());
179 if (osNet == null) {
Jian Li71670d12018-03-02 21:31:07 +0900180 final String errorFormat = ERR_FLOW + "no network(%s) exists";
181 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900182 floatingIp.getFloatingIpAddress(),
183 osPort.getNetworkId());
184 throw new IllegalStateException(error);
185 }
186
187 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
daniel parkc2a2ed62018-04-10 15:17:42 +0900188 log.trace("Mac address of openstack port: {}", srcMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900189 InstancePort instPort = instancePortService.instancePort(srcMac);
Jian Li99892e92018-04-13 14:59:39 +0900190
Jian Li99892e92018-04-13 14:59:39 +0900191 if (instPort == null) {
Jian Lie1a39032018-06-19 21:49:36 +0900192 instPort = tobeRemovedInstPorts.get(osPort.getId());
193 tobeRemovedInstPorts.remove(osPort.getId());
194 }
195
196 if (instPort == null) {
197 instPort = terminatedInstPorts.get(osPort.getId());
Jian Li99892e92018-04-13 14:59:39 +0900198 }
199
Hyunsun Moon44aac662017-02-18 02:07:01 +0900200 if (instPort == null) {
Jian Li71670d12018-03-02 21:31:07 +0900201 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
202 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900203 floatingIp.getFloatingIpAddress(), srcMac);
204 throw new IllegalStateException(error);
205 }
206
daniel park32b42202018-03-14 16:53:44 +0900207 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
208 if (externalPeerRouter == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900209 final String errorFormat = ERR_FLOW + "no external peer router found";
210 throw new IllegalStateException(errorFormat);
daniel park32b42202018-03-14 16:53:44 +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);
daniel parkc2a2ed62018-04-10 15:17:42 +0900222 log.trace("Succeeded to set flow rules for floating ip {}:{} and install: {}",
223 floatingIp.getFloatingIpAddress(),
224 floatingIp.getFixedIpAddress(),
225 install);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900226 }
227
Jian Lide679782018-06-05 01:41:29 +0900228 private synchronized void updateGatewayNodeRules(NetFloatingIP fip,
229 InstancePort instPort,
230 Network osNet,
231 ExternalPeerRouter router,
232 OpenstackNode gateway,
233 boolean install) {
234
235 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
236 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
237 finalGws.addAll(ImmutableSet.copyOf(completedGws));
238
Jian Lia171a432018-06-11 11:52:11 +0900239
240 if (gateway == null) {
241 // these are floating IP related cases...
242 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
243 ImmutableSet.copyOf(finalGws), install);
244
Jian Lide679782018-06-05 01:41:29 +0900245 } else {
Jian Lia171a432018-06-11 11:52:11 +0900246 // these are openstack node related cases...
247 if (install) {
248 if (completedGws.contains(gateway)) {
249 if (completedGws.size() > 1) {
250 finalGws.remove(gateway);
251 if (fip.getPortId() != null) {
252 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
253 ImmutableSet.copyOf(finalGws), false);
254 finalGws.add(gateway);
255 }
256 }
Jian Lide679782018-06-05 01:41:29 +0900257 if (fip.getPortId() != null) {
258 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
259 ImmutableSet.copyOf(finalGws), true);
260 }
Jian Lia171a432018-06-11 11:52:11 +0900261 } else {
262 log.warn("Detected node should be included in completed gateway set");
Jian Lide679782018-06-05 01:41:29 +0900263 }
264 } else {
Jian Lia171a432018-06-11 11:52:11 +0900265 if (!completedGws.contains(gateway)) {
266 if (completedGws.size() >= 1) {
267 if (fip.getPortId() != null) {
268 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
269 ImmutableSet.copyOf(finalGws), true);
270 }
271 }
272 } else {
273 log.warn("Detected node should NOT be included in completed gateway set");
274 }
Jian Lide679782018-06-05 01:41:29 +0900275 }
276 }
277 }
278
279 private synchronized void updateComputeNodeRules(InstancePort instPort,
280 Network osNet,
281 OpenstackNode gateway,
282 boolean install) {
Jian Li1064e4f2018-05-29 16:16:53 +0900283
284 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
285 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
286 finalGws.addAll(ImmutableSet.copyOf(completedGws));
287
288 if (gateway == null) {
289 // these are floating IP related cases...
290 setComputeNodeToGatewayHelper(instPort, osNet,
291 ImmutableSet.copyOf(finalGws), install);
Jian Lide679782018-06-05 01:41:29 +0900292
Jian Li1064e4f2018-05-29 16:16:53 +0900293 } else {
294 // these are openstack node related cases...
295 if (install) {
296 if (completedGws.contains(gateway)) {
297 if (completedGws.size() > 1) {
298 finalGws.remove(gateway);
299 setComputeNodeToGatewayHelper(instPort, osNet,
300 ImmutableSet.copyOf(finalGws), false);
301 finalGws.add(gateway);
302 }
303
304 setComputeNodeToGatewayHelper(instPort, osNet,
305 ImmutableSet.copyOf(finalGws), true);
306 } else {
307 log.warn("Detected node should be included in completed gateway set");
308 }
309 } else {
310 if (!completedGws.contains(gateway)) {
311 finalGws.add(gateway);
312 setComputeNodeToGatewayHelper(instPort, osNet,
313 ImmutableSet.copyOf(finalGws), false);
314 finalGws.remove(gateway);
315 if (completedGws.size() >= 1) {
316 setComputeNodeToGatewayHelper(instPort, osNet,
317 ImmutableSet.copyOf(finalGws), true);
318 }
319 } else {
320 log.warn("Detected node should NOT be included in completed gateway set");
321 }
322 }
323 }
324 }
325
326 // a helper method
327 private void setComputeNodeToGatewayHelper(InstancePort instPort,
328 Network osNet,
329 Set<OpenstackNode> gateways,
330 boolean install) {
daniel parkeeb8e042018-02-21 14:06:58 +0900331 TrafficTreatment treatment;
332
333 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
334 .matchEthType(Ethernet.TYPE_IPV4)
335 .matchIPSrc(instPort.ipAddress().toIpPrefix())
336 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
337
338 switch (osNet.getNetworkType()) {
339 case VXLAN:
340 sBuilder.matchTunnelId(Long.parseLong(osNet.getProviderSegID()));
341 break;
342 case VLAN:
343 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
344 break;
345 default:
346 final String error = String.format(
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800347 ERR_UNSUPPORTED_NET_TYPE,
daniel parkeeb8e042018-02-21 14:06:58 +0900348 osNet.getNetworkType().toString());
349 throw new IllegalStateException(error);
350 }
351
Jian Li1064e4f2018-05-29 16:16:53 +0900352 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
353
daniel parkeeb8e042018-02-21 14:06:58 +0900354 if (selectedGatewayNode == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900355 final String errorFormat = ERR_FLOW + "no gateway node selected";
356 throw new IllegalStateException(errorFormat);
daniel parkeeb8e042018-02-21 14:06:58 +0900357 }
358 treatment = DefaultTrafficTreatment.builder()
359 .extension(buildExtension(
360 deviceService,
361 instPort.deviceId(),
362 selectedGatewayNode.dataIp().getIp4Address()),
363 instPort.deviceId())
364 .setOutput(osNodeService.node(instPort.deviceId()).tunnelPortNum())
365 .build();
366
367 osFlowRuleService.setRule(
368 appId,
369 instPort.deviceId(),
370 sBuilder.build(),
371 treatment,
372 PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE,
373 ROUTING_TABLE,
374 install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900375 log.trace("Succeeded to set flow rules from compute node to gateway on compute node");
daniel parkeeb8e042018-02-21 14:06:58 +0900376 }
377
Jian Lide679782018-06-05 01:41:29 +0900378 private void setDownstreamInternalRules(NetFloatingIP floatingIp,
379 Network osNet,
380 InstancePort instPort,
381 boolean install) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900382 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
383 if (cNode == null) {
384 final String error = String.format("Cannot find openstack node for device %s",
385 instPort.deviceId());
386 throw new IllegalStateException(error);
387 }
388 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900389 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
390 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900391 throw new IllegalStateException(error);
392 }
393 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900394 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
395 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900396 throw new IllegalStateException(error);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900397 }
398
399 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900400
Jian Lide679782018-06-05 01:41:29 +0900401 // TODO: following code snippet will be refactored sooner or later
Hyunsun Moon0d457362017-06-27 17:19:41 +0900402 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900403 // access from one VM to the others via floating IP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900404 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
405 .matchEthType(Ethernet.TYPE_IPV4)
406 .matchIPDst(floating.toIpPrefix())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900407 .matchInPort(gNode.tunnelPortNum())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900408 .build();
409
daniel parkee8700b2017-05-11 15:50:03 +0900410 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900411 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
412 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900413 .setIpDst(instPort.ipAddress().getIp4Address());
414
415 switch (osNet.getNetworkType()) {
416 case VXLAN:
417 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
418 .extension(buildExtension(
419 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900420 gNode.intgBridge(),
421 cNode.dataIp().getIp4Address()),
422 gNode.intgBridge())
daniel parkee8700b2017-05-11 15:50:03 +0900423 .setOutput(PortNumber.IN_PORT);
424 break;
425 case VLAN:
426 internalBuilder.pushVlan()
427 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
428 .setOutput(PortNumber.IN_PORT);
429 break;
430 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800431 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900432 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900433 throw new IllegalStateException(error);
434 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900435
sanghodc375372017-06-08 10:41:30 +0900436 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900437 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900438 gNode.intgBridge(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900439 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900440 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900441 PRIORITY_FLOATING_INTERNAL,
sanghodc375372017-06-08 10:41:30 +0900442 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900443 install);
444 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900445 log.trace("Succeeded to set flow rules for downstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900446 }
447
Jian Lide679782018-06-05 01:41:29 +0900448 private void setDownstreamExternalRulesHelper(NetFloatingIP floatingIp,
449 Network osNet,
450 InstancePort instPort,
451 ExternalPeerRouter externalPeerRouter,
452 Set<OpenstackNode> gateways, boolean install) {
453 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
454 if (cNode == null) {
455 final String error = String.format("Cannot find openstack node for device %s",
456 instPort.deviceId());
457 throw new IllegalStateException(error);
458 }
459 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
460 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
461 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
462 throw new IllegalStateException(error);
463 }
464 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
465 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
466 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
467 throw new IllegalStateException(error);
468 }
469
470 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
471
472 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
473
474 if (selectedGatewayNode == null) {
475 final String errorFormat = ERR_FLOW + "no gateway node selected";
476 throw new IllegalStateException(errorFormat);
477 }
478
479 TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
480 .matchEthType(Ethernet.TYPE_IPV4)
481 .matchIPDst(floating.toIpPrefix());
482
483 TrafficTreatment.Builder externalTreatmentBuilder = DefaultTrafficTreatment.builder()
484 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
485 .setEthDst(instPort.macAddress())
486 .setIpDst(instPort.ipAddress().getIp4Address());
487
488 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
489 externalSelectorBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId()).build();
490 externalTreatmentBuilder.popVlan();
491 }
492
493 switch (osNet.getNetworkType()) {
494 case VXLAN:
495 externalTreatmentBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
496 .extension(buildExtension(
497 deviceService,
498 selectedGatewayNode.intgBridge(),
499 cNode.dataIp().getIp4Address()),
500 selectedGatewayNode.intgBridge())
501 .setOutput(selectedGatewayNode.tunnelPortNum());
502 break;
503 case VLAN:
504 externalTreatmentBuilder.pushVlan()
505 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
506 .setOutput(selectedGatewayNode.vlanPortNum());
507 break;
508 default:
509 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
510 osNet.getNetworkType());
511 throw new IllegalStateException(error);
512 }
513
514 osFlowRuleService.setRule(
515 appId,
516 selectedGatewayNode.intgBridge(),
517 externalSelectorBuilder.build(),
518 externalTreatmentBuilder.build(),
519 PRIORITY_FLOATING_EXTERNAL,
520 GW_COMMON_TABLE,
521 install);
522 }
523
Hyunsun Moon44aac662017-02-18 02:07:01 +0900524 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900525 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
526 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900527 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900528 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900529 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900530 .matchIPSrc(instPort.ipAddress().toIpPrefix());
531
532 switch (osNet.getNetworkType()) {
533 case VXLAN:
534 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
535 break;
536 case VLAN:
537 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
538 break;
539 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800540 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900541 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900542 throw new IllegalStateException(error);
543 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900544
Daniel Park75e3d7f2018-05-29 14:43:53 +0900545 TrafficSelector selector = sBuilder.build();
daniel parkeeb8e042018-02-21 14:06:58 +0900546
Hyunsun Moon0d457362017-06-27 17:19:41 +0900547 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900548 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
549 .setIpSrc(floating.getIp4Address())
550 .setEthSrc(instPort.macAddress())
551 .setEthDst(externalPeerRouter.externalPeerRouterMac());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900552
Daniel Park75e3d7f2018-05-29 14:43:53 +0900553 if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
554 tBuilder.popVlan();
555 }
556
557 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
558 tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
559 }
sanghodc375372017-06-08 10:41:30 +0900560 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900561 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900562 gNode.intgBridge(),
Daniel Park75e3d7f2018-05-29 14:43:53 +0900563 selector,
daniel parkeeb8e042018-02-21 14:06:58 +0900564 tBuilder.setOutput(gNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900565 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900566 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900567 install);
Daniel Park75e3d7f2018-05-29 14:43:53 +0900568 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900569 log.trace("Succeeded to set flow rules for upstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900570 }
571
daniel park32b42202018-03-14 16:53:44 +0900572 private ExternalPeerRouter externalPeerRouter(Network network) {
daniel parkeeb8e042018-02-21 14:06:58 +0900573 if (network == null) {
574 return null;
575 }
576
577 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
578
579 if (subnet == null) {
580 return null;
581 }
582
Jian Lif3a28b02018-06-11 21:29:13 +0900583 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
daniel parkeeb8e042018-02-21 14:06:58 +0900584 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
585 .findAny().orElse(null);
586 if (osRouterIface == null) {
587 return null;
588 }
589
Jian Lif3a28b02018-06-11 21:29:13 +0900590 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
daniel parkeeb8e042018-02-21 14:06:58 +0900591 if (osRouter == null) {
592 return null;
593 }
594 if (osRouter.getExternalGatewayInfo() == null) {
595 return null;
596 }
597
598 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
daniel park65e1c202018-04-03 13:15:28 +0900599 return osNetworkService.externalPeerRouter(exGatewayInfo);
daniel parkeeb8e042018-02-21 14:06:58 +0900600 }
daniel park65e1c202018-04-03 13:15:28 +0900601
Jian Li99892e92018-04-13 14:59:39 +0900602 private void associateFloatingIp(NetFloatingIP osFip) {
603 Port osPort = osNetworkService.port(osFip.getPortId());
604 if (osPort == null) {
605 final String errorFormat = ERR_FLOW + "port(%s) not found";
606 final String error = String.format(errorFormat,
607 osFip.getFloatingIpAddress(), osFip.getPortId());
608 throw new IllegalStateException(error);
609 }
610 // set floating IP rules only if the port is associated to a VM
611 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Lie1a39032018-06-19 21:49:36 +0900612
613 if (instancePortService.instancePort(osPort.getId()) == null) {
614 log.info("Try to associate the fip {} with a terminated VM",
615 osFip.getFloatingIpAddress());
616 pendingInstPortIds.put(osPort.getId(), osFip);
617 return;
618 }
619
Jian Li1064e4f2018-05-29 16:16:53 +0900620 setFloatingIpRules(osFip, osPort, null, true);
Jian Li99892e92018-04-13 14:59:39 +0900621 }
622 }
623
624 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
625 Port osPort = osNetworkService.port(portId);
626 if (osPort == null) {
Jian Lie1a39032018-06-19 21:49:36 +0900627 osPort = terminatedOsPorts.get(portId);
628 terminatedOsPorts.remove(portId);
Jian Li99892e92018-04-13 14:59:39 +0900629 }
Jian Lie1a39032018-06-19 21:49:36 +0900630
631 if (osPort == null) {
632 final String errorFormat = ERR_FLOW + "port(%s) not found";
633 final String error = String.format(errorFormat,
634 osFip.getFloatingIpAddress(), osFip.getPortId());
635 throw new IllegalStateException(error);
636 }
637
Jian Li99892e92018-04-13 14:59:39 +0900638 // set floating IP rules only if the port is associated to a VM
639 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Lie1a39032018-06-19 21:49:36 +0900640
641 if (!tobeRemovedInstPorts.containsKey(osPort.getId()) &&
642 terminatedInstPorts.containsKey(osPort.getId())) {
643 tobeRemovedInstPorts.put(osPort.getId(),
644 terminatedInstPorts.get(osPort.getId()));
645 }
646
647 if (instancePortService.instancePort(osPort.getId()) == null) {
648
649 // in case there is pending instance port, we simply remove that
650 // port, otherwise, we directly go with rule removal
651 if (pendingInstPortIds.containsKey(osPort.getId())) {
652 log.info("Try to disassociate the fip {} with a terminated VM",
653 osFip.getFloatingIpAddress());
654 pendingInstPortIds.remove(osPort.getId());
655 return;
656 }
657 }
658
Jian Li1064e4f2018-05-29 16:16:53 +0900659 setFloatingIpRules(osFip, osPort, 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) {
667 // do not allow to proceed without leadership
668 NodeId leader = leadershipService.getLeader(appId.name());
669 if (!Objects.equals(localNodeId, leader)) {
670 return false;
671 }
672 return event.floatingIp() != null;
673 }
674
675 @Override
676 public void event(OpenstackRouterEvent event) {
677 switch (event.type()) {
678 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900679 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900680 NetFloatingIP osFip = event.floatingIp();
681 associateFloatingIp(osFip);
682 log.info("Associated floating IP {}:{}",
683 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900684 });
685 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900686 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
687 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900688 NetFloatingIP osFip = event.floatingIp();
689 disassociateFloatingIp(osFip, event.portId());
690 log.info("Disassociated floating IP {}:{}",
691 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900692 });
693 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900694 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900695 eventExecutor.execute(() -> {
696 NetFloatingIP osFip = event.floatingIp();
697 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
698 associateFloatingIp(event.floatingIp());
699 }
700 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
701 });
702 break;
Jian Lia171a432018-06-11 11:52:11 +0900703 case OPENSTACK_FLOATING_IP_REMOVED:
704 eventExecutor.execute(() -> {
705 NetFloatingIP osFip = event.floatingIp();
706 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
707 disassociateFloatingIp(osFip, osFip.getPortId());
708 }
709 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
710 });
711 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900712 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900713 case OPENSTACK_ROUTER_CREATED:
714 case OPENSTACK_ROUTER_UPDATED:
715 case OPENSTACK_ROUTER_REMOVED:
716 case OPENSTACK_ROUTER_INTERFACE_ADDED:
717 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
718 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
719 default:
720 // do nothing for the other events
721 break;
722 }
723 }
724 }
725
726 private class InternalNodeListener implements OpenstackNodeListener {
727
728 @Override
729 public boolean isRelevant(OpenstackNodeEvent event) {
730 // do not allow to proceed without leadership
731 NodeId leader = leadershipService.getLeader(appId.name());
732 if (!Objects.equals(localNodeId, leader)) {
733 return false;
734 }
735 return event.subject().type() == GATEWAY;
736 }
737
738 @Override
739 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900740
741 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900742 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900743 eventExecutor.execute(() -> {
Jian Lif3a28b02018-06-11 21:29:13 +0900744 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
Jian Lie1a39032018-06-19 21:49:36 +0900745
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900746 if (Strings.isNullOrEmpty(fip.getPortId())) {
747 continue;
748 }
Jian Lie1a39032018-06-19 21:49:36 +0900749
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900750 Port osPort = osNetworkService.port(fip.getPortId());
751 if (osPort == null) {
752 log.warn("Failed to set floating IP {}", fip.getId());
753 continue;
754 }
Jian Lie1a39032018-06-19 21:49:36 +0900755
756 // This is for handling a VM, which is associated
757 // with a floating IP, was terminated case
758 // in this case, we cannot obtain instance port
759 // information from a terminated VM, we simply
760 // construct a pending map where key is terminated
761 // instance port ID while value is floating IP
762 // address which supposed to be associated with the
763 // terminated instance port.
764
765 // Note that, at OPENSTACK_INSTANCE_PORT_DETECTED phase,
766 // we will install floating IP related rules by
767 // referring to the key and value stored in pending map
768 if (!Strings.isNullOrEmpty(osPort.getDeviceId()) &&
769 instancePortService.instancePort(fip.getPortId()) == null) {
770 pendingInstPortIds.put(fip.getPortId(), fip);
771 continue;
772 }
Jian Li1064e4f2018-05-29 16:16:53 +0900773 setFloatingIpRules(fip, osPort, event.subject(), true);
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900774 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900775 });
776 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900777 case OPENSTACK_NODE_INCOMPLETE:
Jian Li1064e4f2018-05-29 16:16:53 +0900778 eventExecutor.execute(() -> {
Jian Lif3a28b02018-06-11 21:29:13 +0900779 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
Jian Li1064e4f2018-05-29 16:16:53 +0900780 if (Strings.isNullOrEmpty(fip.getPortId())) {
781 continue;
782 }
783 Port osPort = osNetworkService.port(fip.getPortId());
784 if (osPort == null) {
785 log.warn("Failed to set floating IP {}", fip.getId());
786 continue;
787 }
788 Network osNet = osNetworkService.network(osPort.getNetworkId());
789 if (osNet == null) {
790 final String errorFormat = ERR_FLOW + "no network(%s) exists";
791 final String error = String.format(errorFormat,
792 fip.getFloatingIpAddress(),
793 osPort.getNetworkId());
794 throw new IllegalStateException(error);
795 }
796 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
797 log.trace("Mac address of openstack port: {}", srcMac);
798 InstancePort instPort = instancePortService.instancePort(srcMac);
799
800 if (instPort == null) {
801 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
802 final String error = String.format(errorFormat,
803 fip.getFloatingIpAddress(), srcMac);
804 throw new IllegalStateException(error);
805 }
806
Jian Lide679782018-06-05 01:41:29 +0900807 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
808 if (externalPeerRouter == null) {
809 final String errorFormat = ERR_FLOW + "no external peer router found";
810 throw new IllegalStateException(errorFormat);
811 }
812
813 updateComputeNodeRules(instPort, osNet, event.subject(), false);
814 updateGatewayNodeRules(fip, instPort, osNet,
815 externalPeerRouter, event.subject(), false);
Jian Li1064e4f2018-05-29 16:16:53 +0900816 }
817 });
818 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900819 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900820 // do nothing
Hyunsun Moon44aac662017-02-18 02:07:01 +0900821 break;
822 }
823 }
824 }
Jian Li99892e92018-04-13 14:59:39 +0900825
Jian Li24ec59f2018-05-23 19:01:25 +0900826 private class InternalInstancePortListener implements InstancePortListener {
827
828 @Override
829 public boolean isRelevant(InstancePortEvent event) {
Jian Li24ec59f2018-05-23 19:01:25 +0900830
Jian Lie1a39032018-06-19 21:49:36 +0900831 if (event.type() == OPENSTACK_INSTANCE_MIGRATION_ENDED ||
832 event.type() == OPENSTACK_INSTANCE_MIGRATION_STARTED) {
833 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
834 NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
835
836 // we check the possible NPE to avoid duplicated null check
837 // for OPENSTACK_INSTANCE_MIGRATION_ENDED and
838 // OPENSTACK_INSTANCE_MIGRATION_STARTED cases
839 if (fip == null || !isAssociatedWithVM(osNetworkService, fip)) {
840 return false;
841 }
842 }
843
844 // do not allow to proceed without leadership
845 NodeId leader = leadershipService.getLeader(appId.name());
846
847 return Objects.equals(localNodeId, leader);
Jian Li24ec59f2018-05-23 19:01:25 +0900848 }
849
850 @Override
851 public void event(InstancePortEvent event) {
Jian Lie1a39032018-06-19 21:49:36 +0900852 InstancePort instPort = event.subject();
Jian Li24ec59f2018-05-23 19:01:25 +0900853 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
854
Jian Lie1a39032018-06-19 21:49:36 +0900855 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
856 NetFloatingIP fip;
857 Port osPort;
858 Network osNet;
859 ExternalPeerRouter externalPeerRouter;
Jian Li24ec59f2018-05-23 19:01:25 +0900860
861 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900862 case OPENSTACK_INSTANCE_PORT_DETECTED:
863 terminatedInstPorts.remove(instPort.portId());
864 terminatedOsPorts.remove(instPort.portId());
865
866 if (pendingInstPortIds.containsKey(instPort.portId())) {
867 setFloatingIpRules(pendingInstPortIds.get(instPort.portId()),
868 osNetworkService.port(instPort.portId()), null, true);
869 pendingInstPortIds.remove(instPort.portId());
870 }
871
872 break;
873
874 case OPENSTACK_INSTANCE_PORT_VANISHED:
875 terminatedInstPorts.put(instPort.portId(), instPort);
876 terminatedOsPorts.put(instPort.portId(),
877 osNetworkService.port(instPort.portId()));
878 break;
879
Jian Li24ec59f2018-05-23 19:01:25 +0900880 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Lie1a39032018-06-19 21:49:36 +0900881
882 fip = associatedFloatingIp(event.subject(), ips);
883
884 if (fip == null) {
885 return;
886 }
887
888 osPort = osNetworkService.port(fip.getPortId());
889 osNet = osNetworkService.network(osPort.getNetworkId());
890 externalPeerRouter = externalPeerRouter(osNet);
891
892 if (externalPeerRouter == null) {
893 final String errorFormat = ERR_FLOW + "no external peer router found";
894 throw new IllegalStateException(errorFormat);
895 }
896
Jian Li24ec59f2018-05-23 19:01:25 +0900897 eventExecutor.execute(() -> {
898
899 // since downstream internal rules are located in all gateway
900 // nodes, therefore, we simply update the rules with new compute node info
901 setDownstreamInternalRules(fip, osNet, event.subject(), true);
902
903 // since DownstreamExternal rules should only be placed in
904 // corresponding gateway node, we need to install new rule to
905 // the corresponding gateway node
906 setDownstreamExternalRulesHelper(fip, osNet,
907 event.subject(), externalPeerRouter, gateways, true);
908
909 // since ComputeNodeToGateway rules should only be placed in
910 // corresponding compute node, we need to install new rule to
911 // the target compute node, and remove rules from original node
912 setComputeNodeToGatewayHelper(event.subject(), osNet, gateways, true);
913
914 migrationPool.put(fip.getFloatingIpAddress(), event.subject().deviceId());
915
916 });
917 break;
918 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
919
Jian Lie1a39032018-06-19 21:49:36 +0900920 fip = associatedFloatingIp(event.subject(), ips);
921
922 if (fip == null) {
923 return;
924 }
925
926 osPort = osNetworkService.port(fip.getPortId());
927 osNet = osNetworkService.network(osPort.getNetworkId());
928 externalPeerRouter = externalPeerRouter(osNet);
929
930 if (externalPeerRouter == null) {
931 final String errorFormat = ERR_FLOW + "no external peer router found";
932 throw new IllegalStateException(errorFormat);
933 }
934
935 // If we only have one gateway, we simply do not remove any
Jian Li24ec59f2018-05-23 19:01:25 +0900936 // flow rules from either gateway or compute node
937 if (gateways.size() == 1) {
938 return;
939 }
940
Jian Lie1a39032018-06-19 21:49:36 +0900941 // Checks whether the destination compute node's device id
Jian Li24ec59f2018-05-23 19:01:25 +0900942 // has identical gateway hash or not
943 // if it is true, we simply do not remove the rules, as
944 // it has been overwritten at port detention event
945 // if it is false, we will remove the rules
946 DeviceId newDeviceId = migrationPool.get(fip.getFloatingIpAddress());
947 DeviceId oldDeviceId = event.subject().deviceId();
948 migrationPool.remove(fip.getFloatingIpAddress());
949
950 OpenstackNode oldGateway = getGwByComputeDevId(gateways, oldDeviceId);
951 OpenstackNode newGateway = getGwByComputeDevId(gateways, newDeviceId);
952
953 if (oldGateway != null && oldGateway.equals(newGateway)) {
954 return;
955 }
956
957 eventExecutor.execute(() -> {
958
Jian Lie1a39032018-06-19 21:49:36 +0900959 // We need to remove the old ComputeNodeToGateway rules from
Jian Li24ec59f2018-05-23 19:01:25 +0900960 // original compute node
961 setComputeNodeToGatewayHelper(event.subject(), osNet, gateways, false);
962
Jian Lie1a39032018-06-19 21:49:36 +0900963 // Since DownstreamExternal rules should only be placed in
Jian Li24ec59f2018-05-23 19:01:25 +0900964 // corresponding gateway node, we need to remove old rule from
965 // the corresponding gateway node
966 setDownstreamExternalRulesHelper(fip, osNet,
967 event.subject(), externalPeerRouter, gateways, false);
968 });
969 break;
970 default:
971 break;
972 }
973 }
974 }
Jian Lie1a39032018-06-19 21:49:36 +0900975
976 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
977
978 @Override
979 public boolean isRelevant(OpenstackNetworkEvent event) {
980 // do not allow to proceed without leadership
981 NodeId leader = leadershipService.getLeader(appId.name());
982 return Objects.equals(localNodeId, leader);
983 }
984
985 @Override
986 public void event(OpenstackNetworkEvent event) {
987 switch (event.type()) {
988 case OPENSTACK_PORT_REMOVED:
989 Port osPort = event.port();
990 if (terminatedInstPorts.containsKey(osPort.getId())) {
991 updateFipStore(terminatedInstPorts.get(osPort.getId()));
992 terminatedInstPorts.remove(osPort.getId());
993 }
994 break;
995 default:
996 break;
997 }
998 }
999
1000 private void updateFipStore(InstancePort port) {
1001
1002 if (port == null) {
1003 return;
1004 }
1005
1006 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
1007 for (NetFloatingIP fip : ips) {
1008 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
1009 continue;
1010 }
1011 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
1012 continue;
1013 }
1014 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
1015 NeutronFloatingIP neutronFip = (NeutronFloatingIP) fip;
1016 // invalidate bound fixed IP and port
1017 neutronFip.setFixedIpAddress(null);
1018 neutronFip.setPortId(null);
1019 tobeRemovedInstPorts.put(port.portId(), port);
1020
1021 // Following update will in turn trigger
1022 // OPENSTACK_FLOATING_IP_DISASSOCIATED event
1023 osRouterAdminService.updateFloatingIp(neutronFip);
1024 log.info("Updated floating IP {}, due to host removal",
1025 neutronFip.getFloatingIpAddress());
1026 }
1027 }
1028 }
1029 }
Hyunsun Moon44aac662017-02-18 02:07:01 +09001030}