blob: 42fd73d96d1a2d321f64b0d718bd4b7d41eaf6bc [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 Li99892e92018-04-13 14:59:39 +090019import com.google.common.collect.Maps;
Hyunsun Moon44aac662017-02-18 02:07:01 +090020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onlab.packet.Ethernet;
26import org.onlab.packet.IpAddress;
27import org.onlab.packet.MacAddress;
daniel parkee8700b2017-05-11 15:50:03 +090028import org.onlab.packet.VlanId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090029import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
Jian Li99892e92018-04-13 14:59:39 +090034import org.onosproject.net.Host;
Hyunsun Moon44aac662017-02-18 02:07:01 +090035import org.onosproject.net.PortNumber;
36import org.onosproject.net.device.DeviceService;
37import org.onosproject.net.flow.DefaultTrafficSelector;
38import org.onosproject.net.flow.DefaultTrafficTreatment;
39import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.flow.TrafficTreatment;
Jian Li99892e92018-04-13 14:59:39 +090041import org.onosproject.net.host.HostEvent;
42import org.onosproject.net.host.HostListener;
43import org.onosproject.net.host.HostService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090044import org.onosproject.openstacknetworking.api.Constants;
daniel park32b42202018-03-14 16:53:44 +090045import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Hyunsun Moon44aac662017-02-18 02:07:01 +090046import org.onosproject.openstacknetworking.api.InstancePort;
47import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090048import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
49import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090050import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
51import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
52import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090053import org.onosproject.openstacknode.api.OpenstackNode;
54import org.onosproject.openstacknode.api.OpenstackNodeEvent;
55import org.onosproject.openstacknode.api.OpenstackNodeListener;
56import org.onosproject.openstacknode.api.OpenstackNodeService;
daniel parkeeb8e042018-02-21 14:06:58 +090057import org.openstack4j.model.network.ExternalGateway;
Hyunsun Moon44aac662017-02-18 02:07:01 +090058import org.openstack4j.model.network.NetFloatingIP;
59import org.openstack4j.model.network.Network;
daniel parkee8700b2017-05-11 15:50:03 +090060import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090061import org.openstack4j.model.network.Port;
daniel parkeeb8e042018-02-21 14:06:58 +090062import org.openstack4j.model.network.Router;
63import org.openstack4j.model.network.RouterInterface;
64import org.openstack4j.model.network.Subnet;
Hyunsun Moon44aac662017-02-18 02:07:01 +090065import org.slf4j.Logger;
66import org.slf4j.LoggerFactory;
67
Jian Li99892e92018-04-13 14:59:39 +090068import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090069import java.util.Objects;
Jian Li99892e92018-04-13 14:59:39 +090070import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090071import java.util.concurrent.ExecutorService;
72
73import static java.util.concurrent.Executors.newSingleThreadExecutor;
74import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090075import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
76import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
daniel parkeeb8e042018-02-21 14:06:58 +090077import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE;
sanghodc375372017-06-08 10:41:30 +090078import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
79import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_INTERNAL;
daniel parkeeb8e042018-02-21 14:06:58 +090080import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
Jian Li99892e92018-04-13 14:59:39 +090081import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
82import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Jian Li26949762018-03-30 15:46:37 +090083import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090084import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090085
86/**
87 * Handles OpenStack floating IP events.
88 */
89@Component(immediate = true)
90public class OpenstackRoutingFloatingIpHandler {
91
92 private final Logger log = LoggerFactory.getLogger(getClass());
93
94 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
Ray Milkeyc6c9b172018-02-26 09:36:31 -080095 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type %s";
Hyunsun Moon44aac662017-02-18 02:07:01 +090096
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected CoreService coreService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected DeviceService deviceService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900104 protected LeadershipService leadershipService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected ClusterService clusterService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li99892e92018-04-13 14:59:39 +0900110 protected HostService hostService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900113 protected OpenstackNodeService osNodeService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected InstancePortService instancePortService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected OpenstackRouterService osRouterService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected OpenstackNetworkService osNetworkService;
123
sanghodc375372017-06-08 10:41:30 +0900124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected OpenstackFlowRuleService osFlowRuleService;
126
Hyunsun Moon44aac662017-02-18 02:07:01 +0900127 private final ExecutorService eventExecutor = newSingleThreadExecutor(
128 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900129 private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900130 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
Jian Li99892e92018-04-13 14:59:39 +0900131 private final HostListener hostListener = new InternalHostListener();
132 private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900133
134 private ApplicationId appId;
135 private NodeId localNodeId;
136
137 @Activate
138 protected void activate() {
139 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
140 localNodeId = clusterService.getLocalNode().id();
141 leadershipService.runForLeadership(appId.name());
Jian Li99892e92018-04-13 14:59:39 +0900142 hostService.addListener(hostListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900143 osRouterService.addListener(floatingIpLisener);
144 osNodeService.addListener(osNodeListener);
145
146 log.info("Started");
147 }
148
149 @Deactivate
150 protected void deactivate() {
Jian Li99892e92018-04-13 14:59:39 +0900151 hostService.removeListener(hostListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900152 osNodeService.removeListener(osNodeListener);
153 osRouterService.removeListener(floatingIpLisener);
154 leadershipService.withdraw(appId.name());
155 eventExecutor.shutdown();
156
157 log.info("Stopped");
158 }
159
Hyunsun Moon44aac662017-02-18 02:07:01 +0900160 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
161 boolean install) {
162 Network osNet = osNetworkService.network(osPort.getNetworkId());
163 if (osNet == null) {
Jian Li71670d12018-03-02 21:31:07 +0900164 final String errorFormat = ERR_FLOW + "no network(%s) exists";
165 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900166 floatingIp.getFloatingIpAddress(),
167 osPort.getNetworkId());
168 throw new IllegalStateException(error);
169 }
170
171 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
daniel parkc2a2ed62018-04-10 15:17:42 +0900172 log.trace("Mac address of openstack port: {}", srcMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900173 InstancePort instPort = instancePortService.instancePort(srcMac);
Jian Li99892e92018-04-13 14:59:39 +0900174
175 // sweep through removed port map
176 if (instPort == null) {
177 instPort = removedPorts.get(srcMac);
178 removedPorts.remove(srcMac);
179 }
180
Hyunsun Moon44aac662017-02-18 02:07:01 +0900181 if (instPort == null) {
Jian Li71670d12018-03-02 21:31:07 +0900182 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
183 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900184 floatingIp.getFloatingIpAddress(), srcMac);
185 throw new IllegalStateException(error);
186 }
187
daniel park32b42202018-03-14 16:53:44 +0900188
189 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
190 if (externalPeerRouter == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900191 final String errorFormat = ERR_FLOW + "no external peer router found";
192 throw new IllegalStateException(errorFormat);
daniel park32b42202018-03-14 16:53:44 +0900193 }
194
daniel parkeeb8e042018-02-21 14:06:58 +0900195 setComputeNodeToGateway(instPort, osNet, install);
daniel park32b42202018-03-14 16:53:44 +0900196 setDownstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
197 setUpstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900198 log.trace("Succeeded to set flow rules for floating ip {}:{} and install: {}",
199 floatingIp.getFloatingIpAddress(),
200 floatingIp.getFixedIpAddress(),
201 install);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900202 }
203
daniel parkeeb8e042018-02-21 14:06:58 +0900204 private void setComputeNodeToGateway(InstancePort instPort, Network osNet, boolean install) {
205 TrafficTreatment treatment;
206
207 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
208 .matchEthType(Ethernet.TYPE_IPV4)
209 .matchIPSrc(instPort.ipAddress().toIpPrefix())
210 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
211
212 switch (osNet.getNetworkType()) {
213 case VXLAN:
214 sBuilder.matchTunnelId(Long.parseLong(osNet.getProviderSegID()));
215 break;
216 case VLAN:
217 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
218 break;
219 default:
220 final String error = String.format(
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800221 ERR_UNSUPPORTED_NET_TYPE,
daniel parkeeb8e042018-02-21 14:06:58 +0900222 osNet.getNetworkType().toString());
223 throw new IllegalStateException(error);
224 }
225
226 OpenstackNode selectedGatewayNode = selectGatewayNode();
227 if (selectedGatewayNode == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900228 final String errorFormat = ERR_FLOW + "no gateway node selected";
229 throw new IllegalStateException(errorFormat);
daniel parkeeb8e042018-02-21 14:06:58 +0900230 }
231 treatment = DefaultTrafficTreatment.builder()
232 .extension(buildExtension(
233 deviceService,
234 instPort.deviceId(),
235 selectedGatewayNode.dataIp().getIp4Address()),
236 instPort.deviceId())
237 .setOutput(osNodeService.node(instPort.deviceId()).tunnelPortNum())
238 .build();
239
240 osFlowRuleService.setRule(
241 appId,
242 instPort.deviceId(),
243 sBuilder.build(),
244 treatment,
245 PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE,
246 ROUTING_TABLE,
247 install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900248 log.trace("Succeeded to set flow rules from compute node to gateway on compute node");
daniel parkeeb8e042018-02-21 14:06:58 +0900249 }
250
251 private OpenstackNode selectGatewayNode() {
252 //TODO support multiple loadbalancing options.
253 return osNodeService.completeNodes(GATEWAY).stream().findAny().orElse(null);
254 }
255
Hyunsun Moon44aac662017-02-18 02:07:01 +0900256 private void setDownstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900257 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
258 boolean install) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900259 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
260 if (cNode == null) {
261 final String error = String.format("Cannot find openstack node for device %s",
262 instPort.deviceId());
263 throw new IllegalStateException(error);
264 }
265 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900266 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
267 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900268 throw new IllegalStateException(error);
269 }
270 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900271 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
272 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900273 throw new IllegalStateException(error);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900274 }
275
276 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900277
Hyunsun Moon0d457362017-06-27 17:19:41 +0900278 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park3ad82132018-05-25 18:47:46 +0900279 TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
280 .matchEthType(Ethernet.TYPE_IPV4)
281 .matchIPDst(floating.toIpPrefix());
daniel park32b42202018-03-14 16:53:44 +0900282
Daniel Park3ad82132018-05-25 18:47:46 +0900283 TrafficTreatment.Builder externalTreatmentBuilder = DefaultTrafficTreatment.builder()
284 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
285 .setEthDst(instPort.macAddress())
286 .setIpDst(instPort.ipAddress().getIp4Address());
287
288 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
289 externalSelectorBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId()).build();
290 externalTreatmentBuilder.popVlan();
291 }
daniel parkee8700b2017-05-11 15:50:03 +0900292
293 switch (osNet.getNetworkType()) {
294 case VXLAN:
Daniel Park3ad82132018-05-25 18:47:46 +0900295 externalTreatmentBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
daniel parkee8700b2017-05-11 15:50:03 +0900296 .extension(buildExtension(
297 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900298 gNode.intgBridge(),
299 cNode.dataIp().getIp4Address()),
300 gNode.intgBridge())
301 .setOutput(gNode.tunnelPortNum());
daniel parkee8700b2017-05-11 15:50:03 +0900302 break;
303 case VLAN:
Daniel Park3ad82132018-05-25 18:47:46 +0900304 externalTreatmentBuilder.pushVlan()
daniel parkee8700b2017-05-11 15:50:03 +0900305 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
Hyunsun Moon0d457362017-06-27 17:19:41 +0900306 .setOutput(gNode.vlanPortNum());
daniel parkee8700b2017-05-11 15:50:03 +0900307 break;
308 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800309 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900310 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900311 throw new IllegalStateException(error);
312 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900313
sanghodc375372017-06-08 10:41:30 +0900314 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900315 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900316 gNode.intgBridge(),
daniel park32b42202018-03-14 16:53:44 +0900317 externalSelectorBuilder.build(),
Daniel Park3ad82132018-05-25 18:47:46 +0900318 externalTreatmentBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900319 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900320 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900321 install);
322
Hyunsun Moon0d457362017-06-27 17:19:41 +0900323 // access from one VM to the others via floating IP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900324 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
325 .matchEthType(Ethernet.TYPE_IPV4)
326 .matchIPDst(floating.toIpPrefix())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900327 .matchInPort(gNode.tunnelPortNum())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900328 .build();
329
daniel parkee8700b2017-05-11 15:50:03 +0900330 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900331 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
332 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900333 .setIpDst(instPort.ipAddress().getIp4Address());
334
335 switch (osNet.getNetworkType()) {
336 case VXLAN:
337 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
338 .extension(buildExtension(
339 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900340 gNode.intgBridge(),
341 cNode.dataIp().getIp4Address()),
342 gNode.intgBridge())
daniel parkee8700b2017-05-11 15:50:03 +0900343 .setOutput(PortNumber.IN_PORT);
344 break;
345 case VLAN:
346 internalBuilder.pushVlan()
347 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
348 .setOutput(PortNumber.IN_PORT);
349 break;
350 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800351 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900352 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900353 throw new IllegalStateException(error);
354 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900355
sanghodc375372017-06-08 10:41:30 +0900356 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900357 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900358 gNode.intgBridge(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900359 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900360 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900361 PRIORITY_FLOATING_INTERNAL,
sanghodc375372017-06-08 10:41:30 +0900362 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900363 install);
364 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900365 log.trace("Succeeded to set flow rules for downstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900366 }
367
368 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900369 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
370 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900371 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900372 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900373 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900374 .matchIPSrc(instPort.ipAddress().toIpPrefix());
375
376 switch (osNet.getNetworkType()) {
377 case VXLAN:
378 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
379 break;
380 case VLAN:
381 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
382 break;
383 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800384 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900385 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900386 throw new IllegalStateException(error);
387 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900388
Daniel Park75e3d7f2018-05-29 14:43:53 +0900389 TrafficSelector selector = sBuilder.build();
daniel parkeeb8e042018-02-21 14:06:58 +0900390
Hyunsun Moon0d457362017-06-27 17:19:41 +0900391 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900392 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
393 .setIpSrc(floating.getIp4Address())
394 .setEthSrc(instPort.macAddress())
395 .setEthDst(externalPeerRouter.externalPeerRouterMac());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900396
Daniel Park75e3d7f2018-05-29 14:43:53 +0900397 if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
398 tBuilder.popVlan();
399 }
400
401 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
402 tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
403 }
sanghodc375372017-06-08 10:41:30 +0900404 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900405 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900406 gNode.intgBridge(),
Daniel Park75e3d7f2018-05-29 14:43:53 +0900407 selector,
daniel parkeeb8e042018-02-21 14:06:58 +0900408 tBuilder.setOutput(gNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900409 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900410 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900411 install);
Daniel Park75e3d7f2018-05-29 14:43:53 +0900412 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900413 log.trace("Succeeded to set flow rules for upstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900414 }
415
daniel park32b42202018-03-14 16:53:44 +0900416 private ExternalPeerRouter externalPeerRouter(Network network) {
daniel parkeeb8e042018-02-21 14:06:58 +0900417 if (network == null) {
418 return null;
419 }
420
421 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
422
423 if (subnet == null) {
424 return null;
425 }
426
427 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
428 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
429 .findAny().orElse(null);
430 if (osRouterIface == null) {
431 return null;
432 }
433
434 Router osRouter = osRouterService.router(osRouterIface.getId());
435 if (osRouter == null) {
436 return null;
437 }
438 if (osRouter.getExternalGatewayInfo() == null) {
439 return null;
440 }
441
442 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
daniel park65e1c202018-04-03 13:15:28 +0900443 return osNetworkService.externalPeerRouter(exGatewayInfo);
daniel parkeeb8e042018-02-21 14:06:58 +0900444 }
daniel park65e1c202018-04-03 13:15:28 +0900445
Jian Li99892e92018-04-13 14:59:39 +0900446
447 private void associateFloatingIp(NetFloatingIP osFip) {
448 Port osPort = osNetworkService.port(osFip.getPortId());
449 if (osPort == null) {
450 final String errorFormat = ERR_FLOW + "port(%s) not found";
451 final String error = String.format(errorFormat,
452 osFip.getFloatingIpAddress(), osFip.getPortId());
453 throw new IllegalStateException(error);
454 }
455 // set floating IP rules only if the port is associated to a VM
456 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
457 setFloatingIpRules(osFip, osPort, true);
458 }
459 }
460
461 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
462 Port osPort = osNetworkService.port(portId);
463 if (osPort == null) {
464 // FIXME when a port with floating IP removed without
465 // disassociation step, it can reach here
466 return;
467 }
468 // set floating IP rules only if the port is associated to a VM
469 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
470 setFloatingIpRules(osFip, osPort, false);
471 }
472 }
473
Hyunsun Moon0d457362017-06-27 17:19:41 +0900474 private class InternalFloatingIpListener implements OpenstackRouterListener {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900475
476 @Override
477 public boolean isRelevant(OpenstackRouterEvent event) {
478 // do not allow to proceed without leadership
479 NodeId leader = leadershipService.getLeader(appId.name());
480 if (!Objects.equals(localNodeId, leader)) {
481 return false;
482 }
483 return event.floatingIp() != null;
484 }
485
486 @Override
487 public void event(OpenstackRouterEvent event) {
488 switch (event.type()) {
489 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900490 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900491 NetFloatingIP osFip = event.floatingIp();
492 associateFloatingIp(osFip);
493 log.info("Associated floating IP {}:{}",
494 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900495 });
496 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900497 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
498 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900499 NetFloatingIP osFip = event.floatingIp();
500 disassociateFloatingIp(osFip, event.portId());
501 log.info("Disassociated floating IP {}:{}",
502 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900503 });
504 break;
505 case OPENSTACK_FLOATING_IP_REMOVED:
506 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900507 NetFloatingIP osFip = event.floatingIp();
508 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
509 disassociateFloatingIp(osFip, osFip.getPortId());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900510 }
Hyunsun Moonb720e632017-05-16 15:41:36 +0900511 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900512 });
513 break;
514 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900515 eventExecutor.execute(() -> {
516 NetFloatingIP osFip = event.floatingIp();
517 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
518 associateFloatingIp(event.floatingIp());
519 }
520 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
521 });
522 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900523 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900524 case OPENSTACK_ROUTER_CREATED:
525 case OPENSTACK_ROUTER_UPDATED:
526 case OPENSTACK_ROUTER_REMOVED:
527 case OPENSTACK_ROUTER_INTERFACE_ADDED:
528 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
529 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
530 default:
531 // do nothing for the other events
532 break;
533 }
534 }
535 }
536
537 private class InternalNodeListener implements OpenstackNodeListener {
538
539 @Override
540 public boolean isRelevant(OpenstackNodeEvent event) {
541 // do not allow to proceed without leadership
542 NodeId leader = leadershipService.getLeader(appId.name());
543 if (!Objects.equals(localNodeId, leader)) {
544 return false;
545 }
546 return event.subject().type() == GATEWAY;
547 }
548
549 @Override
550 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900551
552 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900553 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900554 eventExecutor.execute(() -> {
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900555 for (NetFloatingIP fip : osRouterService.floatingIps()) {
556 if (Strings.isNullOrEmpty(fip.getPortId())) {
557 continue;
558 }
559 Port osPort = osNetworkService.port(fip.getPortId());
560 if (osPort == null) {
561 log.warn("Failed to set floating IP {}", fip.getId());
562 continue;
563 }
564 setFloatingIpRules(fip, osPort, true);
565 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900566 });
567 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900568 case OPENSTACK_NODE_CREATED:
569 case OPENSTACK_NODE_UPDATED:
570 case OPENSTACK_NODE_REMOVED:
571 case OPENSTACK_NODE_INCOMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900572 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900573 // do nothing
Hyunsun Moon44aac662017-02-18 02:07:01 +0900574 break;
575 }
576 }
577 }
Jian Li99892e92018-04-13 14:59:39 +0900578
579 private class InternalHostListener implements HostListener {
580
581 @Override
582 public boolean isRelevant(HostEvent event) {
583 Host host = event.subject();
584 if (!isValidHost(host)) {
585 log.debug("Invalid host detected, ignore it {}", host);
586 return false;
587 }
588 return true;
589 }
590
591 @Override
592 public void event(HostEvent event) {
593 InstancePort instPort = HostBasedInstancePort.of(event.subject());
594 switch (event.type()) {
595 case HOST_REMOVED:
596 storeTempInstPort(instPort);
597 break;
598 case HOST_UPDATED:
599 case HOST_ADDED:
600 default:
601 break;
602 }
603 }
604
605 private void storeTempInstPort(InstancePort port) {
606 Set<NetFloatingIP> ips = osRouterService.floatingIps();
607 for (NetFloatingIP fip : ips) {
608 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
609 continue;
610 }
611 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
612 continue;
613 }
614 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
615 removedPorts.put(port.macAddress(), port);
616 eventExecutor.execute(() -> {
617 disassociateFloatingIp(fip, port.portId());
618 log.info("Disassociated floating IP {}:{}",
619 fip.getFloatingIpAddress(), fip.getFixedIpAddress());
620 });
621 }
622 }
623 }
624
625 private boolean isValidHost(Host host) {
626 return !host.ipAddresses().isEmpty() &&
627 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
628 host.annotations().value(ANNOTATION_PORT_ID) != null;
629 }
630 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900631}