blob: 172dd789163c48310eaa1fe955c1c885079bc760 [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 Liec5c32b2018-07-13 14:28:58 +090046import org.onosproject.openstacknetworking.api.InstancePortAdminService;
Jian Li24ec59f2018-05-23 19:01:25 +090047import org.onosproject.openstacknetworking.api.InstancePortEvent;
48import org.onosproject.openstacknetworking.api.InstancePortListener;
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 Liec5c32b2018-07-13 14:28:58 +090085import static org.onosproject.openstacknetworking.api.InstancePort.State.PENDING_REMOVAL;
86import static org.onosproject.openstacknetworking.api.InstancePort.State.REMOVED;
Jian Lie1a39032018-06-19 21:49:36 +090087import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_ENDED;
88import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_STARTED;
Jian Li24ec59f2018-05-23 19:01:25 +090089import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
Jian Li1064e4f2018-05-29 16:16:53 +090090import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Jian Li24ec59f2018-05-23 19:01:25 +090091import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
Jian Liec5c32b2018-07-13 14:28:58 +090092import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Li26949762018-03-30 15:46:37 +090093import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090094import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Jian Lic2403592018-07-18 12:56:45 +090095import static org.openstack4j.model.network.NetworkType.FLAT;
Hyunsun Moon44aac662017-02-18 02:07:01 +090096
97/**
98 * Handles OpenStack floating IP events.
99 */
100@Component(immediate = true)
101public class OpenstackRoutingFloatingIpHandler {
102
103 private final Logger log = LoggerFactory.getLogger(getClass());
104
105 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800106 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type %s";
Hyunsun Moon44aac662017-02-18 02:07:01 +0900107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected CoreService coreService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected DeviceService deviceService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900115 protected LeadershipService leadershipService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected ClusterService clusterService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected OpenstackNodeService osNodeService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liec5c32b2018-07-13 14:28:58 +0900124 protected InstancePortAdminService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Lif3a28b02018-06-11 21:29:13 +0900127 protected OpenstackRouterAdminService osRouterAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected OpenstackNetworkService osNetworkService;
131
sanghodc375372017-06-08 10:41:30 +0900132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected OpenstackFlowRuleService osFlowRuleService;
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
143 private Map<String, Port> terminatedOsPorts = 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 Li71670d12018-03-02 21:31:07 +0900192 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
193 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900194 floatingIp.getFloatingIpAddress(), srcMac);
195 throw new IllegalStateException(error);
196 }
197
Jian Liec5c32b2018-07-13 14:28:58 +0900198 if (instPort.state() == PENDING_REMOVAL) {
199 instancePortService.updateInstancePort(instPort.updateState(REMOVED));
200 }
201
daniel park32b42202018-03-14 16:53:44 +0900202 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
203 if (externalPeerRouter == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900204 final String errorFormat = ERR_FLOW + "no external peer router found";
205 throw new IllegalStateException(errorFormat);
daniel park32b42202018-03-14 16:53:44 +0900206 }
207
Jian Lide679782018-06-05 01:41:29 +0900208 updateComputeNodeRules(instPort, osNet, gateway, install);
209 updateGatewayNodeRules(floatingIp, instPort, osNet, externalPeerRouter, gateway, install);
210
211 // FIXME: downstream internal rules are still duplicated in all gateway nodes
212 // need to make the internal rules de-duplicated sooner or later
213 setDownstreamInternalRules(floatingIp, osNet, instPort, install);
214
215 // TODO: need to refactor setUpstreamRules if possible
daniel park32b42202018-03-14 16:53:44 +0900216 setUpstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900217 log.trace("Succeeded to set flow rules for floating ip {}:{} and install: {}",
218 floatingIp.getFloatingIpAddress(),
219 floatingIp.getFixedIpAddress(),
220 install);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900221 }
222
Jian Lide679782018-06-05 01:41:29 +0900223 private synchronized void updateGatewayNodeRules(NetFloatingIP fip,
224 InstancePort instPort,
225 Network osNet,
226 ExternalPeerRouter router,
227 OpenstackNode gateway,
228 boolean install) {
229
230 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
231 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
232 finalGws.addAll(ImmutableSet.copyOf(completedGws));
233
Jian Lia171a432018-06-11 11:52:11 +0900234
235 if (gateway == null) {
236 // these are floating IP related cases...
237 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
238 ImmutableSet.copyOf(finalGws), install);
239
Jian Lide679782018-06-05 01:41:29 +0900240 } else {
Jian Lia171a432018-06-11 11:52:11 +0900241 // these are openstack node related cases...
242 if (install) {
243 if (completedGws.contains(gateway)) {
244 if (completedGws.size() > 1) {
245 finalGws.remove(gateway);
246 if (fip.getPortId() != null) {
247 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
248 ImmutableSet.copyOf(finalGws), false);
249 finalGws.add(gateway);
250 }
251 }
Jian Lide679782018-06-05 01:41:29 +0900252 if (fip.getPortId() != null) {
253 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
254 ImmutableSet.copyOf(finalGws), true);
255 }
Jian Lia171a432018-06-11 11:52:11 +0900256 } else {
257 log.warn("Detected node should be included in completed gateway set");
Jian Lide679782018-06-05 01:41:29 +0900258 }
259 } else {
Jian Lia171a432018-06-11 11:52:11 +0900260 if (!completedGws.contains(gateway)) {
261 if (completedGws.size() >= 1) {
262 if (fip.getPortId() != null) {
263 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
264 ImmutableSet.copyOf(finalGws), true);
265 }
266 }
267 } else {
268 log.warn("Detected node should NOT be included in completed gateway set");
269 }
Jian Lide679782018-06-05 01:41:29 +0900270 }
271 }
272 }
273
274 private synchronized void updateComputeNodeRules(InstancePort instPort,
275 Network osNet,
276 OpenstackNode gateway,
277 boolean install) {
Jian Li1064e4f2018-05-29 16:16:53 +0900278
279 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
280 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
281 finalGws.addAll(ImmutableSet.copyOf(completedGws));
282
283 if (gateway == null) {
284 // these are floating IP related cases...
285 setComputeNodeToGatewayHelper(instPort, osNet,
286 ImmutableSet.copyOf(finalGws), install);
Jian Lide679782018-06-05 01:41:29 +0900287
Jian Li1064e4f2018-05-29 16:16:53 +0900288 } else {
289 // these are openstack node related cases...
290 if (install) {
291 if (completedGws.contains(gateway)) {
292 if (completedGws.size() > 1) {
293 finalGws.remove(gateway);
294 setComputeNodeToGatewayHelper(instPort, osNet,
295 ImmutableSet.copyOf(finalGws), false);
296 finalGws.add(gateway);
297 }
298
299 setComputeNodeToGatewayHelper(instPort, osNet,
300 ImmutableSet.copyOf(finalGws), true);
301 } else {
302 log.warn("Detected node should be included in completed gateway set");
303 }
304 } else {
305 if (!completedGws.contains(gateway)) {
306 finalGws.add(gateway);
307 setComputeNodeToGatewayHelper(instPort, osNet,
308 ImmutableSet.copyOf(finalGws), false);
309 finalGws.remove(gateway);
310 if (completedGws.size() >= 1) {
311 setComputeNodeToGatewayHelper(instPort, osNet,
312 ImmutableSet.copyOf(finalGws), true);
313 }
314 } else {
315 log.warn("Detected node should NOT be included in completed gateway set");
316 }
317 }
318 }
319 }
320
321 // a helper method
322 private void setComputeNodeToGatewayHelper(InstancePort instPort,
323 Network osNet,
324 Set<OpenstackNode> gateways,
325 boolean install) {
daniel parkeeb8e042018-02-21 14:06:58 +0900326 TrafficTreatment treatment;
327
328 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
329 .matchEthType(Ethernet.TYPE_IPV4)
330 .matchIPSrc(instPort.ipAddress().toIpPrefix())
331 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
332
333 switch (osNet.getNetworkType()) {
334 case VXLAN:
335 sBuilder.matchTunnelId(Long.parseLong(osNet.getProviderSegID()));
336 break;
337 case VLAN:
338 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
339 break;
340 default:
341 final String error = String.format(
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800342 ERR_UNSUPPORTED_NET_TYPE,
daniel parkeeb8e042018-02-21 14:06:58 +0900343 osNet.getNetworkType().toString());
344 throw new IllegalStateException(error);
345 }
346
Jian Li1064e4f2018-05-29 16:16:53 +0900347 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
348
daniel parkeeb8e042018-02-21 14:06:58 +0900349 if (selectedGatewayNode == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900350 final String errorFormat = ERR_FLOW + "no gateway node selected";
351 throw new IllegalStateException(errorFormat);
daniel parkeeb8e042018-02-21 14:06:58 +0900352 }
353 treatment = DefaultTrafficTreatment.builder()
354 .extension(buildExtension(
355 deviceService,
356 instPort.deviceId(),
357 selectedGatewayNode.dataIp().getIp4Address()),
358 instPort.deviceId())
359 .setOutput(osNodeService.node(instPort.deviceId()).tunnelPortNum())
360 .build();
361
362 osFlowRuleService.setRule(
363 appId,
364 instPort.deviceId(),
365 sBuilder.build(),
366 treatment,
367 PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE,
368 ROUTING_TABLE,
369 install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900370 log.trace("Succeeded to set flow rules from compute node to gateway on compute node");
daniel parkeeb8e042018-02-21 14:06:58 +0900371 }
372
Jian Lide679782018-06-05 01:41:29 +0900373 private void setDownstreamInternalRules(NetFloatingIP floatingIp,
374 Network osNet,
375 InstancePort instPort,
376 boolean install) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900377 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
378 if (cNode == null) {
379 final String error = String.format("Cannot find openstack node for device %s",
380 instPort.deviceId());
381 throw new IllegalStateException(error);
382 }
383 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900384 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
385 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900386 throw new IllegalStateException(error);
387 }
388 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900389 final String errorFormat = ERR_FLOW + "VLAN 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);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900392 }
393
394 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900395
Jian Lide679782018-06-05 01:41:29 +0900396 // TODO: following code snippet will be refactored sooner or later
Hyunsun Moon0d457362017-06-27 17:19:41 +0900397 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900398 // access from one VM to the others via floating IP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900399 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
400 .matchEthType(Ethernet.TYPE_IPV4)
401 .matchIPDst(floating.toIpPrefix())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900402 .matchInPort(gNode.tunnelPortNum())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900403 .build();
404
daniel parkee8700b2017-05-11 15:50:03 +0900405 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900406 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
407 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900408 .setIpDst(instPort.ipAddress().getIp4Address());
409
410 switch (osNet.getNetworkType()) {
411 case VXLAN:
412 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
413 .extension(buildExtension(
414 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900415 gNode.intgBridge(),
416 cNode.dataIp().getIp4Address()),
417 gNode.intgBridge())
daniel parkee8700b2017-05-11 15:50:03 +0900418 .setOutput(PortNumber.IN_PORT);
419 break;
420 case VLAN:
421 internalBuilder.pushVlan()
422 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
423 .setOutput(PortNumber.IN_PORT);
424 break;
425 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800426 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900427 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900428 throw new IllegalStateException(error);
429 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900430
sanghodc375372017-06-08 10:41:30 +0900431 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900432 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900433 gNode.intgBridge(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900434 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900435 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900436 PRIORITY_FLOATING_INTERNAL,
sanghodc375372017-06-08 10:41:30 +0900437 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900438 install);
439 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900440 log.trace("Succeeded to set flow rules for downstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900441 }
442
Jian Lide679782018-06-05 01:41:29 +0900443 private void setDownstreamExternalRulesHelper(NetFloatingIP floatingIp,
444 Network osNet,
445 InstancePort instPort,
446 ExternalPeerRouter externalPeerRouter,
447 Set<OpenstackNode> gateways, boolean install) {
448 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
449 if (cNode == null) {
450 final String error = String.format("Cannot find openstack node for device %s",
451 instPort.deviceId());
452 throw new IllegalStateException(error);
453 }
454 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
455 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
456 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
457 throw new IllegalStateException(error);
458 }
459 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
460 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
461 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
462 throw new IllegalStateException(error);
463 }
464
465 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
466
467 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
468
469 if (selectedGatewayNode == null) {
470 final String errorFormat = ERR_FLOW + "no gateway node selected";
471 throw new IllegalStateException(errorFormat);
472 }
473
474 TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
475 .matchEthType(Ethernet.TYPE_IPV4)
476 .matchIPDst(floating.toIpPrefix());
477
478 TrafficTreatment.Builder externalTreatmentBuilder = DefaultTrafficTreatment.builder()
479 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
480 .setEthDst(instPort.macAddress())
481 .setIpDst(instPort.ipAddress().getIp4Address());
482
Jian Li5e2ad4a2018-07-16 13:40:53 +0900483 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
484 externalSelectorBuilder.matchVlanId(externalPeerRouter.vlanId()).build();
Jian Lide679782018-06-05 01:41:29 +0900485 externalTreatmentBuilder.popVlan();
486 }
487
488 switch (osNet.getNetworkType()) {
489 case VXLAN:
490 externalTreatmentBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
491 .extension(buildExtension(
492 deviceService,
493 selectedGatewayNode.intgBridge(),
494 cNode.dataIp().getIp4Address()),
495 selectedGatewayNode.intgBridge())
496 .setOutput(selectedGatewayNode.tunnelPortNum());
497 break;
498 case VLAN:
499 externalTreatmentBuilder.pushVlan()
500 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
501 .setOutput(selectedGatewayNode.vlanPortNum());
502 break;
503 default:
504 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
505 osNet.getNetworkType());
506 throw new IllegalStateException(error);
507 }
508
509 osFlowRuleService.setRule(
510 appId,
511 selectedGatewayNode.intgBridge(),
512 externalSelectorBuilder.build(),
513 externalTreatmentBuilder.build(),
514 PRIORITY_FLOATING_EXTERNAL,
515 GW_COMMON_TABLE,
516 install);
517 }
518
Hyunsun Moon44aac662017-02-18 02:07:01 +0900519 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900520 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
521 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900522 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900523 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900524 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900525 .matchIPSrc(instPort.ipAddress().toIpPrefix());
526
527 switch (osNet.getNetworkType()) {
528 case VXLAN:
529 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
530 break;
531 case VLAN:
532 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
533 break;
534 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800535 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900536 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900537 throw new IllegalStateException(error);
538 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900539
Daniel Park75e3d7f2018-05-29 14:43:53 +0900540 TrafficSelector selector = sBuilder.build();
daniel parkeeb8e042018-02-21 14:06:58 +0900541
Hyunsun Moon0d457362017-06-27 17:19:41 +0900542 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900543 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
544 .setIpSrc(floating.getIp4Address())
545 .setEthSrc(instPort.macAddress())
Jian Li5e2ad4a2018-07-16 13:40:53 +0900546 .setEthDst(externalPeerRouter.macAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900547
Daniel Park75e3d7f2018-05-29 14:43:53 +0900548 if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
549 tBuilder.popVlan();
550 }
551
Jian Li5e2ad4a2018-07-16 13:40:53 +0900552 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
553 tBuilder.pushVlan().setVlanId(externalPeerRouter.vlanId());
Daniel Park75e3d7f2018-05-29 14:43:53 +0900554 }
sanghodc375372017-06-08 10:41:30 +0900555 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900556 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900557 gNode.intgBridge(),
Daniel Park75e3d7f2018-05-29 14:43:53 +0900558 selector,
daniel parkeeb8e042018-02-21 14:06:58 +0900559 tBuilder.setOutput(gNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900560 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900561 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900562 install);
Daniel Park75e3d7f2018-05-29 14:43:53 +0900563 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900564 log.trace("Succeeded to set flow rules for upstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900565 }
566
daniel park32b42202018-03-14 16:53:44 +0900567 private ExternalPeerRouter externalPeerRouter(Network network) {
daniel parkeeb8e042018-02-21 14:06:58 +0900568 if (network == null) {
569 return null;
570 }
571
572 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
573
574 if (subnet == null) {
575 return null;
576 }
577
Jian Lif3a28b02018-06-11 21:29:13 +0900578 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
daniel parkeeb8e042018-02-21 14:06:58 +0900579 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
580 .findAny().orElse(null);
581 if (osRouterIface == null) {
582 return null;
583 }
584
Jian Lif3a28b02018-06-11 21:29:13 +0900585 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
daniel parkeeb8e042018-02-21 14:06:58 +0900586 if (osRouter == null) {
587 return null;
588 }
589 if (osRouter.getExternalGatewayInfo() == null) {
590 return null;
591 }
592
593 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
daniel park65e1c202018-04-03 13:15:28 +0900594 return osNetworkService.externalPeerRouter(exGatewayInfo);
daniel parkeeb8e042018-02-21 14:06:58 +0900595 }
daniel park65e1c202018-04-03 13:15:28 +0900596
Jian Li99892e92018-04-13 14:59:39 +0900597 private void associateFloatingIp(NetFloatingIP osFip) {
598 Port osPort = osNetworkService.port(osFip.getPortId());
599 if (osPort == null) {
600 final String errorFormat = ERR_FLOW + "port(%s) not found";
601 final String error = String.format(errorFormat,
602 osFip.getFloatingIpAddress(), osFip.getPortId());
603 throw new IllegalStateException(error);
604 }
605 // set floating IP rules only if the port is associated to a VM
606 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Lie1a39032018-06-19 21:49:36 +0900607
608 if (instancePortService.instancePort(osPort.getId()) == null) {
Jian Lie1a39032018-06-19 21:49:36 +0900609 return;
610 }
611
Jian Li1064e4f2018-05-29 16:16:53 +0900612 setFloatingIpRules(osFip, osPort, null, true);
Jian Li99892e92018-04-13 14:59:39 +0900613 }
614 }
615
616 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
617 Port osPort = osNetworkService.port(portId);
618 if (osPort == null) {
Jian Lie1a39032018-06-19 21:49:36 +0900619 osPort = terminatedOsPorts.get(portId);
620 terminatedOsPorts.remove(portId);
Jian Li99892e92018-04-13 14:59:39 +0900621 }
Jian Lie1a39032018-06-19 21:49:36 +0900622
623 if (osPort == null) {
624 final String errorFormat = ERR_FLOW + "port(%s) not found";
625 final String error = String.format(errorFormat,
626 osFip.getFloatingIpAddress(), osFip.getPortId());
627 throw new IllegalStateException(error);
628 }
629
Jian Li99892e92018-04-13 14:59:39 +0900630 // set floating IP rules only if the port is associated to a VM
631 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Lie1a39032018-06-19 21:49:36 +0900632
Jian Lie1a39032018-06-19 21:49:36 +0900633 if (instancePortService.instancePort(osPort.getId()) == null) {
Jian Li46b74002018-07-15 18:39:08 +0900634 return;
Jian Lie1a39032018-06-19 21:49:36 +0900635 }
636
Jian Li1064e4f2018-05-29 16:16:53 +0900637 setFloatingIpRules(osFip, osPort, null, false);
Jian Li99892e92018-04-13 14:59:39 +0900638 }
639 }
640
Hyunsun Moon0d457362017-06-27 17:19:41 +0900641 private class InternalFloatingIpListener implements OpenstackRouterListener {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900642
643 @Override
644 public boolean isRelevant(OpenstackRouterEvent event) {
645 // do not allow to proceed without leadership
646 NodeId leader = leadershipService.getLeader(appId.name());
647 if (!Objects.equals(localNodeId, leader)) {
648 return false;
649 }
650 return event.floatingIp() != null;
651 }
652
653 @Override
654 public void event(OpenstackRouterEvent event) {
655 switch (event.type()) {
656 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900657 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900658 NetFloatingIP osFip = event.floatingIp();
659 associateFloatingIp(osFip);
660 log.info("Associated floating IP {}:{}",
661 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900662 });
663 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900664 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
665 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900666 NetFloatingIP osFip = event.floatingIp();
667 disassociateFloatingIp(osFip, event.portId());
668 log.info("Disassociated floating IP {}:{}",
669 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900670 });
671 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900672 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900673 eventExecutor.execute(() -> {
674 NetFloatingIP osFip = event.floatingIp();
675 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
676 associateFloatingIp(event.floatingIp());
677 }
678 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
679 });
680 break;
Jian Lia171a432018-06-11 11:52:11 +0900681 case OPENSTACK_FLOATING_IP_REMOVED:
682 eventExecutor.execute(() -> {
683 NetFloatingIP osFip = event.floatingIp();
684 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
Jian Lic2403592018-07-18 12:56:45 +0900685 // in case the floating IP is not associated with any port due to
686 // port removal, we simply do not execute floating IP disassociation
687 if (osNetworkService.port(osFip.getPortId()) != null) {
688 disassociateFloatingIp(osFip, osFip.getPortId());
689 }
Jian Lia171a432018-06-11 11:52:11 +0900690 }
691 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
692 });
693 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900694 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900695 case OPENSTACK_ROUTER_CREATED:
696 case OPENSTACK_ROUTER_UPDATED:
697 case OPENSTACK_ROUTER_REMOVED:
698 case OPENSTACK_ROUTER_INTERFACE_ADDED:
699 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
700 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
701 default:
702 // do nothing for the other events
703 break;
704 }
705 }
706 }
707
708 private class InternalNodeListener implements OpenstackNodeListener {
709
710 @Override
711 public boolean isRelevant(OpenstackNodeEvent event) {
712 // do not allow to proceed without leadership
713 NodeId leader = leadershipService.getLeader(appId.name());
714 if (!Objects.equals(localNodeId, leader)) {
715 return false;
716 }
717 return event.subject().type() == GATEWAY;
718 }
719
720 @Override
721 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900722
723 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900724 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900725 eventExecutor.execute(() -> {
Jian Lif3a28b02018-06-11 21:29:13 +0900726 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
Jian Lie1a39032018-06-19 21:49:36 +0900727
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900728 if (Strings.isNullOrEmpty(fip.getPortId())) {
729 continue;
730 }
Jian Lie1a39032018-06-19 21:49:36 +0900731
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900732 Port osPort = osNetworkService.port(fip.getPortId());
733 if (osPort == null) {
734 log.warn("Failed to set floating IP {}", fip.getId());
735 continue;
736 }
Jian Lie1a39032018-06-19 21:49:36 +0900737
Jian Li46b74002018-07-15 18:39:08 +0900738 if (instancePortService.instancePort(fip.getPortId()) == null) {
Jian Lie1a39032018-06-19 21:49:36 +0900739 continue;
740 }
Jian Liec5c32b2018-07-13 14:28:58 +0900741
Jian Li1064e4f2018-05-29 16:16:53 +0900742 setFloatingIpRules(fip, osPort, event.subject(), true);
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900743 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900744 });
745 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900746 case OPENSTACK_NODE_INCOMPLETE:
Jian Li1064e4f2018-05-29 16:16:53 +0900747 eventExecutor.execute(() -> {
Jian Lif3a28b02018-06-11 21:29:13 +0900748 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
Jian Li1064e4f2018-05-29 16:16:53 +0900749 if (Strings.isNullOrEmpty(fip.getPortId())) {
750 continue;
751 }
752 Port osPort = osNetworkService.port(fip.getPortId());
753 if (osPort == null) {
754 log.warn("Failed to set floating IP {}", fip.getId());
755 continue;
756 }
757 Network osNet = osNetworkService.network(osPort.getNetworkId());
758 if (osNet == null) {
759 final String errorFormat = ERR_FLOW + "no network(%s) exists";
760 final String error = String.format(errorFormat,
761 fip.getFloatingIpAddress(),
762 osPort.getNetworkId());
763 throw new IllegalStateException(error);
764 }
765 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
766 log.trace("Mac address of openstack port: {}", srcMac);
767 InstancePort instPort = instancePortService.instancePort(srcMac);
768
769 if (instPort == null) {
770 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
771 final String error = String.format(errorFormat,
772 fip.getFloatingIpAddress(), srcMac);
773 throw new IllegalStateException(error);
774 }
775
Jian Lide679782018-06-05 01:41:29 +0900776 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
777 if (externalPeerRouter == null) {
778 final String errorFormat = ERR_FLOW + "no external peer router found";
779 throw new IllegalStateException(errorFormat);
780 }
781
782 updateComputeNodeRules(instPort, osNet, event.subject(), false);
783 updateGatewayNodeRules(fip, instPort, osNet,
784 externalPeerRouter, event.subject(), false);
Jian Li1064e4f2018-05-29 16:16:53 +0900785 }
786 });
787 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900788 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900789 // do nothing
Hyunsun Moon44aac662017-02-18 02:07:01 +0900790 break;
791 }
792 }
793 }
Jian Li99892e92018-04-13 14:59:39 +0900794
Jian Li24ec59f2018-05-23 19:01:25 +0900795 private class InternalInstancePortListener implements InstancePortListener {
796
797 @Override
798 public boolean isRelevant(InstancePortEvent event) {
Jian Li24ec59f2018-05-23 19:01:25 +0900799
Jian Lie1a39032018-06-19 21:49:36 +0900800 if (event.type() == OPENSTACK_INSTANCE_MIGRATION_ENDED ||
801 event.type() == OPENSTACK_INSTANCE_MIGRATION_STARTED) {
802 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
803 NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
804
805 // we check the possible NPE to avoid duplicated null check
806 // for OPENSTACK_INSTANCE_MIGRATION_ENDED and
807 // OPENSTACK_INSTANCE_MIGRATION_STARTED cases
808 if (fip == null || !isAssociatedWithVM(osNetworkService, fip)) {
809 return false;
810 }
811 }
812
813 // do not allow to proceed without leadership
814 NodeId leader = leadershipService.getLeader(appId.name());
815
816 return Objects.equals(localNodeId, leader);
Jian Li24ec59f2018-05-23 19:01:25 +0900817 }
818
819 @Override
820 public void event(InstancePortEvent event) {
Jian Lie1a39032018-06-19 21:49:36 +0900821 InstancePort instPort = event.subject();
Jian Li24ec59f2018-05-23 19:01:25 +0900822 Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
823
Jian Lie1a39032018-06-19 21:49:36 +0900824 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
825 NetFloatingIP fip;
826 Port osPort;
827 Network osNet;
828 ExternalPeerRouter externalPeerRouter;
Jian Li24ec59f2018-05-23 19:01:25 +0900829
830 switch (event.type()) {
Jian Lie1a39032018-06-19 21:49:36 +0900831 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Li77323c52018-06-24 01:26:18 +0900832 if (instPort != null && instPort.portId() != null) {
833 String portId = instPort.portId();
Jian Lie1a39032018-06-19 21:49:36 +0900834
Jian Li77323c52018-06-24 01:26:18 +0900835 terminatedOsPorts.remove(portId);
836
837 Port port = osNetworkService.port(portId);
838
Jian Li46b74002018-07-15 18:39:08 +0900839 osRouterAdminService.floatingIps().stream()
840 .filter(f -> f.getPortId() != null)
841 .filter(f -> f.getPortId().equals(instPort.portId()))
842 .forEach(f -> setFloatingIpRules(f, port, null, true));
Jian Lie1a39032018-06-19 21:49:36 +0900843 }
844
845 break;
846
Jian Li24ec59f2018-05-23 19:01:25 +0900847 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Lie1a39032018-06-19 21:49:36 +0900848
849 fip = associatedFloatingIp(event.subject(), ips);
850
851 if (fip == null) {
852 return;
853 }
854
855 osPort = osNetworkService.port(fip.getPortId());
856 osNet = osNetworkService.network(osPort.getNetworkId());
857 externalPeerRouter = externalPeerRouter(osNet);
858
859 if (externalPeerRouter == null) {
860 final String errorFormat = ERR_FLOW + "no external peer router found";
861 throw new IllegalStateException(errorFormat);
862 }
863
Jian Li24ec59f2018-05-23 19:01:25 +0900864 eventExecutor.execute(() -> {
865
866 // since downstream internal rules are located in all gateway
867 // nodes, therefore, we simply update the rules with new compute node info
868 setDownstreamInternalRules(fip, osNet, event.subject(), true);
869
870 // since DownstreamExternal rules should only be placed in
871 // corresponding gateway node, we need to install new rule to
872 // the corresponding gateway node
873 setDownstreamExternalRulesHelper(fip, osNet,
874 event.subject(), externalPeerRouter, gateways, true);
875
876 // since ComputeNodeToGateway rules should only be placed in
877 // corresponding compute node, we need to install new rule to
878 // the target compute node, and remove rules from original node
879 setComputeNodeToGatewayHelper(event.subject(), osNet, gateways, true);
Jian Li24ec59f2018-05-23 19:01:25 +0900880 });
881 break;
882 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
883
Jian Liec5c32b2018-07-13 14:28:58 +0900884 InstancePort revisedInstPort = swapStaleLocation(event.subject());
885
886 fip = associatedFloatingIp(revisedInstPort, ips);
Jian Lie1a39032018-06-19 21:49:36 +0900887
888 if (fip == null) {
889 return;
890 }
891
892 osPort = osNetworkService.port(fip.getPortId());
893 osNet = osNetworkService.network(osPort.getNetworkId());
894 externalPeerRouter = externalPeerRouter(osNet);
895
896 if (externalPeerRouter == null) {
897 final String errorFormat = ERR_FLOW + "no external peer router found";
898 throw new IllegalStateException(errorFormat);
899 }
900
901 // If we only have one gateway, we simply do not remove any
Jian Li24ec59f2018-05-23 19:01:25 +0900902 // flow rules from either gateway or compute node
903 if (gateways.size() == 1) {
904 return;
905 }
906
Jian Lie1a39032018-06-19 21:49:36 +0900907 // Checks whether the destination compute node's device id
Jian Li24ec59f2018-05-23 19:01:25 +0900908 // has identical gateway hash or not
909 // if it is true, we simply do not remove the rules, as
910 // it has been overwritten at port detention event
911 // if it is false, we will remove the rules
Jian Liec5c32b2018-07-13 14:28:58 +0900912 DeviceId newDeviceId = event.subject().deviceId();
913 DeviceId oldDeviceId = revisedInstPort.deviceId();
Jian Li24ec59f2018-05-23 19:01:25 +0900914
915 OpenstackNode oldGateway = getGwByComputeDevId(gateways, oldDeviceId);
916 OpenstackNode newGateway = getGwByComputeDevId(gateways, newDeviceId);
917
918 if (oldGateway != null && oldGateway.equals(newGateway)) {
919 return;
920 }
921
922 eventExecutor.execute(() -> {
923
Jian Lie1a39032018-06-19 21:49:36 +0900924 // We need to remove the old ComputeNodeToGateway rules from
Jian Li24ec59f2018-05-23 19:01:25 +0900925 // original compute node
Jian Liec5c32b2018-07-13 14:28:58 +0900926 setComputeNodeToGatewayHelper(revisedInstPort, osNet, gateways, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900927
Jian Lie1a39032018-06-19 21:49:36 +0900928 // Since DownstreamExternal rules should only be placed in
Jian Li24ec59f2018-05-23 19:01:25 +0900929 // corresponding gateway node, we need to remove old rule from
930 // the corresponding gateway node
Jian Liec5c32b2018-07-13 14:28:58 +0900931 setDownstreamExternalRulesHelper(fip, osNet, revisedInstPort,
932 externalPeerRouter, gateways, false);
Jian Li24ec59f2018-05-23 19:01:25 +0900933 });
934 break;
935 default:
936 break;
937 }
938 }
939 }
Jian Lie1a39032018-06-19 21:49:36 +0900940
941 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
942
943 @Override
944 public boolean isRelevant(OpenstackNetworkEvent event) {
945 // do not allow to proceed without leadership
946 NodeId leader = leadershipService.getLeader(appId.name());
Jian Lic2403592018-07-18 12:56:45 +0900947 return Objects.equals(localNodeId, leader) &&
948 event.subject().getNetworkType() != FLAT;
Jian Lie1a39032018-06-19 21:49:36 +0900949 }
950
951 @Override
952 public void event(OpenstackNetworkEvent event) {
953 switch (event.type()) {
954 case OPENSTACK_PORT_REMOVED:
Jian Liec5c32b2018-07-13 14:28:58 +0900955 String portId = event.port().getId();
956 terminatedOsPorts.put(portId, event.port());
957
958 InstancePort instPort = instancePortService.instancePort(portId);
959 InstancePort updated = instPort.updateState(PENDING_REMOVAL);
960 instancePortService.updateInstancePort(updated);
961
962 updateFipStore(instPort);
963
964 // we will hold the instance port in its store, until its
965 // state is changed to REMOVED
966 while (true) {
967 if (instancePortService.instancePort(portId).state() ==
968 REMOVED) {
969 instancePortService.removeInstancePort(portId);
970 break;
971 }
Jian Lie1a39032018-06-19 21:49:36 +0900972 }
973 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}