blob: 3976e5c56c7b3ac0b978f5aa90ac8b1dd3bfc03b [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 Li99892e92018-04-13 14:59:39 +090036import org.onosproject.net.Host;
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;
Jian Li99892e92018-04-13 14:59:39 +090043import org.onosproject.net.host.HostEvent;
44import org.onosproject.net.host.HostListener;
45import org.onosproject.net.host.HostService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090046import org.onosproject.openstacknetworking.api.Constants;
daniel park32b42202018-03-14 16:53:44 +090047import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Hyunsun Moon44aac662017-02-18 02:07:01 +090048import org.onosproject.openstacknetworking.api.InstancePort;
49import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090050import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
51import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lif3a28b02018-06-11 21:29:13 +090052import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090053import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
54import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090055import org.onosproject.openstacknode.api.OpenstackNode;
56import org.onosproject.openstacknode.api.OpenstackNodeEvent;
57import org.onosproject.openstacknode.api.OpenstackNodeListener;
58import org.onosproject.openstacknode.api.OpenstackNodeService;
daniel parkeeb8e042018-02-21 14:06:58 +090059import org.openstack4j.model.network.ExternalGateway;
Hyunsun Moon44aac662017-02-18 02:07:01 +090060import org.openstack4j.model.network.NetFloatingIP;
61import org.openstack4j.model.network.Network;
daniel parkee8700b2017-05-11 15:50:03 +090062import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090063import org.openstack4j.model.network.Port;
daniel parkeeb8e042018-02-21 14:06:58 +090064import org.openstack4j.model.network.Router;
65import org.openstack4j.model.network.RouterInterface;
66import org.openstack4j.model.network.Subnet;
Jian Lif3a28b02018-06-11 21:29:13 +090067import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090068import org.slf4j.Logger;
69import org.slf4j.LoggerFactory;
70
Jian Li99892e92018-04-13 14:59:39 +090071import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090072import java.util.Objects;
Jian Li99892e92018-04-13 14:59:39 +090073import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090074import java.util.concurrent.ExecutorService;
75
76import static java.util.concurrent.Executors.newSingleThreadExecutor;
77import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090078import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
79import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
daniel parkeeb8e042018-02-21 14:06:58 +090080import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE;
sanghodc375372017-06-08 10:41:30 +090081import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
82import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_INTERNAL;
daniel parkeeb8e042018-02-21 14:06:58 +090083import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
Jian Li99892e92018-04-13 14:59:39 +090084import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
85import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Jian Li1064e4f2018-05-29 16:16:53 +090086import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Jian Li26949762018-03-30 15:46:37 +090087import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090088import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090089
90/**
91 * Handles OpenStack floating IP events.
92 */
93@Component(immediate = true)
94public class OpenstackRoutingFloatingIpHandler {
95
96 private final Logger log = LoggerFactory.getLogger(getClass());
97
98 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
Ray Milkeyc6c9b172018-02-26 09:36:31 -080099 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type %s";
Hyunsun Moon44aac662017-02-18 02:07:01 +0900100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected CoreService coreService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected DeviceService deviceService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900108 protected LeadershipService leadershipService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected ClusterService clusterService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li99892e92018-04-13 14:59:39 +0900114 protected HostService hostService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900117 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));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900133 private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900134 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
Jian Li99892e92018-04-13 14:59:39 +0900135 private final HostListener hostListener = new InternalHostListener();
136 private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900137
138 private ApplicationId appId;
139 private NodeId localNodeId;
140
141 @Activate
142 protected void activate() {
143 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
144 localNodeId = clusterService.getLocalNode().id();
145 leadershipService.runForLeadership(appId.name());
Jian Li99892e92018-04-13 14:59:39 +0900146 hostService.addListener(hostListener);
Jian Lif3a28b02018-06-11 21:29:13 +0900147 osRouterAdminService.addListener(floatingIpLisener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900148 osNodeService.addListener(osNodeListener);
149
150 log.info("Started");
151 }
152
153 @Deactivate
154 protected void deactivate() {
Jian Li99892e92018-04-13 14:59:39 +0900155 hostService.removeListener(hostListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900156 osNodeService.removeListener(osNodeListener);
Jian Lif3a28b02018-06-11 21:29:13 +0900157 osRouterAdminService.removeListener(floatingIpLisener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900158 leadershipService.withdraw(appId.name());
159 eventExecutor.shutdown();
160
161 log.info("Stopped");
162 }
163
Hyunsun Moon44aac662017-02-18 02:07:01 +0900164 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
Jian Li1064e4f2018-05-29 16:16:53 +0900165 OpenstackNode gateway, boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900166 Network osNet = osNetworkService.network(osPort.getNetworkId());
167 if (osNet == null) {
Jian Li71670d12018-03-02 21:31:07 +0900168 final String errorFormat = ERR_FLOW + "no network(%s) exists";
169 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900170 floatingIp.getFloatingIpAddress(),
171 osPort.getNetworkId());
172 throw new IllegalStateException(error);
173 }
174
175 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
daniel parkc2a2ed62018-04-10 15:17:42 +0900176 log.trace("Mac address of openstack port: {}", srcMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900177 InstancePort instPort = instancePortService.instancePort(srcMac);
Jian Li99892e92018-04-13 14:59:39 +0900178
179 // sweep through removed port map
180 if (instPort == null) {
181 instPort = removedPorts.get(srcMac);
182 removedPorts.remove(srcMac);
183 }
184
Hyunsun Moon44aac662017-02-18 02:07:01 +0900185 if (instPort == null) {
Jian Li71670d12018-03-02 21:31:07 +0900186 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
187 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900188 floatingIp.getFloatingIpAddress(), srcMac);
189 throw new IllegalStateException(error);
190 }
191
daniel park32b42202018-03-14 16:53:44 +0900192 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
193 if (externalPeerRouter == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900194 final String errorFormat = ERR_FLOW + "no external peer router found";
195 throw new IllegalStateException(errorFormat);
daniel park32b42202018-03-14 16:53:44 +0900196 }
197
Jian Lide679782018-06-05 01:41:29 +0900198 updateComputeNodeRules(instPort, osNet, gateway, install);
199 updateGatewayNodeRules(floatingIp, instPort, osNet, externalPeerRouter, gateway, install);
200
201 // FIXME: downstream internal rules are still duplicated in all gateway nodes
202 // need to make the internal rules de-duplicated sooner or later
203 setDownstreamInternalRules(floatingIp, osNet, instPort, install);
204
205 // TODO: need to refactor setUpstreamRules if possible
daniel park32b42202018-03-14 16:53:44 +0900206 setUpstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900207 log.trace("Succeeded to set flow rules for floating ip {}:{} and install: {}",
208 floatingIp.getFloatingIpAddress(),
209 floatingIp.getFixedIpAddress(),
210 install);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900211 }
212
Jian Lide679782018-06-05 01:41:29 +0900213 private synchronized void updateGatewayNodeRules(NetFloatingIP fip,
214 InstancePort instPort,
215 Network osNet,
216 ExternalPeerRouter router,
217 OpenstackNode gateway,
218 boolean install) {
219
220 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
221 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
222 finalGws.addAll(ImmutableSet.copyOf(completedGws));
223
Jian Lia171a432018-06-11 11:52:11 +0900224
225 if (gateway == null) {
226 // these are floating IP related cases...
227 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
228 ImmutableSet.copyOf(finalGws), install);
229
Jian Lide679782018-06-05 01:41:29 +0900230 } else {
Jian Lia171a432018-06-11 11:52:11 +0900231 // these are openstack node related cases...
232 if (install) {
233 if (completedGws.contains(gateway)) {
234 if (completedGws.size() > 1) {
235 finalGws.remove(gateway);
236 if (fip.getPortId() != null) {
237 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
238 ImmutableSet.copyOf(finalGws), false);
239 finalGws.add(gateway);
240 }
241 }
Jian Lide679782018-06-05 01:41:29 +0900242 if (fip.getPortId() != null) {
243 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
244 ImmutableSet.copyOf(finalGws), true);
245 }
Jian Lia171a432018-06-11 11:52:11 +0900246 } else {
247 log.warn("Detected node should be included in completed gateway set");
Jian Lide679782018-06-05 01:41:29 +0900248 }
249 } else {
Jian Lia171a432018-06-11 11:52:11 +0900250 if (!completedGws.contains(gateway)) {
251 if (completedGws.size() >= 1) {
252 if (fip.getPortId() != null) {
253 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
254 ImmutableSet.copyOf(finalGws), true);
255 }
256 }
257 } else {
258 log.warn("Detected node should NOT be included in completed gateway set");
259 }
Jian Lide679782018-06-05 01:41:29 +0900260 }
261 }
262 }
263
264 private synchronized void updateComputeNodeRules(InstancePort instPort,
265 Network osNet,
266 OpenstackNode gateway,
267 boolean install) {
Jian Li1064e4f2018-05-29 16:16:53 +0900268
269 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
270 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
271 finalGws.addAll(ImmutableSet.copyOf(completedGws));
272
273 if (gateway == null) {
274 // these are floating IP related cases...
275 setComputeNodeToGatewayHelper(instPort, osNet,
276 ImmutableSet.copyOf(finalGws), install);
Jian Lide679782018-06-05 01:41:29 +0900277
Jian Li1064e4f2018-05-29 16:16:53 +0900278 } else {
279 // these are openstack node related cases...
280 if (install) {
281 if (completedGws.contains(gateway)) {
282 if (completedGws.size() > 1) {
283 finalGws.remove(gateway);
284 setComputeNodeToGatewayHelper(instPort, osNet,
285 ImmutableSet.copyOf(finalGws), false);
286 finalGws.add(gateway);
287 }
288
289 setComputeNodeToGatewayHelper(instPort, osNet,
290 ImmutableSet.copyOf(finalGws), true);
291 } else {
292 log.warn("Detected node should be included in completed gateway set");
293 }
294 } else {
295 if (!completedGws.contains(gateway)) {
296 finalGws.add(gateway);
297 setComputeNodeToGatewayHelper(instPort, osNet,
298 ImmutableSet.copyOf(finalGws), false);
299 finalGws.remove(gateway);
300 if (completedGws.size() >= 1) {
301 setComputeNodeToGatewayHelper(instPort, osNet,
302 ImmutableSet.copyOf(finalGws), true);
303 }
304 } else {
305 log.warn("Detected node should NOT be included in completed gateway set");
306 }
307 }
308 }
309 }
310
311 // a helper method
312 private void setComputeNodeToGatewayHelper(InstancePort instPort,
313 Network osNet,
314 Set<OpenstackNode> gateways,
315 boolean install) {
daniel parkeeb8e042018-02-21 14:06:58 +0900316 TrafficTreatment treatment;
317
318 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
319 .matchEthType(Ethernet.TYPE_IPV4)
320 .matchIPSrc(instPort.ipAddress().toIpPrefix())
321 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
322
323 switch (osNet.getNetworkType()) {
324 case VXLAN:
325 sBuilder.matchTunnelId(Long.parseLong(osNet.getProviderSegID()));
326 break;
327 case VLAN:
328 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
329 break;
330 default:
331 final String error = String.format(
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800332 ERR_UNSUPPORTED_NET_TYPE,
daniel parkeeb8e042018-02-21 14:06:58 +0900333 osNet.getNetworkType().toString());
334 throw new IllegalStateException(error);
335 }
336
Jian Li1064e4f2018-05-29 16:16:53 +0900337 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
338
daniel parkeeb8e042018-02-21 14:06:58 +0900339 if (selectedGatewayNode == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900340 final String errorFormat = ERR_FLOW + "no gateway node selected";
341 throw new IllegalStateException(errorFormat);
daniel parkeeb8e042018-02-21 14:06:58 +0900342 }
343 treatment = DefaultTrafficTreatment.builder()
344 .extension(buildExtension(
345 deviceService,
346 instPort.deviceId(),
347 selectedGatewayNode.dataIp().getIp4Address()),
348 instPort.deviceId())
349 .setOutput(osNodeService.node(instPort.deviceId()).tunnelPortNum())
350 .build();
351
352 osFlowRuleService.setRule(
353 appId,
354 instPort.deviceId(),
355 sBuilder.build(),
356 treatment,
357 PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE,
358 ROUTING_TABLE,
359 install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900360 log.trace("Succeeded to set flow rules from compute node to gateway on compute node");
daniel parkeeb8e042018-02-21 14:06:58 +0900361 }
362
Jian Lide679782018-06-05 01:41:29 +0900363 private void setDownstreamInternalRules(NetFloatingIP floatingIp,
364 Network osNet,
365 InstancePort instPort,
366 boolean install) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900367 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
368 if (cNode == null) {
369 final String error = String.format("Cannot find openstack node for device %s",
370 instPort.deviceId());
371 throw new IllegalStateException(error);
372 }
373 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900374 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
375 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900376 throw new IllegalStateException(error);
377 }
378 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900379 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
380 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900381 throw new IllegalStateException(error);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900382 }
383
384 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900385
Jian Lide679782018-06-05 01:41:29 +0900386 // TODO: following code snippet will be refactored sooner or later
Hyunsun Moon0d457362017-06-27 17:19:41 +0900387 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900388 // access from one VM to the others via floating IP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900389 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
390 .matchEthType(Ethernet.TYPE_IPV4)
391 .matchIPDst(floating.toIpPrefix())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900392 .matchInPort(gNode.tunnelPortNum())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900393 .build();
394
daniel parkee8700b2017-05-11 15:50:03 +0900395 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900396 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
397 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900398 .setIpDst(instPort.ipAddress().getIp4Address());
399
400 switch (osNet.getNetworkType()) {
401 case VXLAN:
402 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
403 .extension(buildExtension(
404 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900405 gNode.intgBridge(),
406 cNode.dataIp().getIp4Address()),
407 gNode.intgBridge())
daniel parkee8700b2017-05-11 15:50:03 +0900408 .setOutput(PortNumber.IN_PORT);
409 break;
410 case VLAN:
411 internalBuilder.pushVlan()
412 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
413 .setOutput(PortNumber.IN_PORT);
414 break;
415 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800416 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900417 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900418 throw new IllegalStateException(error);
419 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900420
sanghodc375372017-06-08 10:41:30 +0900421 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900422 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900423 gNode.intgBridge(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900424 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900425 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900426 PRIORITY_FLOATING_INTERNAL,
sanghodc375372017-06-08 10:41:30 +0900427 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900428 install);
429 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900430 log.trace("Succeeded to set flow rules for downstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900431 }
432
Jian Lide679782018-06-05 01:41:29 +0900433 private void setDownstreamExternalRulesHelper(NetFloatingIP floatingIp,
434 Network osNet,
435 InstancePort instPort,
436 ExternalPeerRouter externalPeerRouter,
437 Set<OpenstackNode> gateways, boolean install) {
438 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
439 if (cNode == null) {
440 final String error = String.format("Cannot find openstack node for device %s",
441 instPort.deviceId());
442 throw new IllegalStateException(error);
443 }
444 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
445 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
446 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
447 throw new IllegalStateException(error);
448 }
449 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
450 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
451 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
452 throw new IllegalStateException(error);
453 }
454
455 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
456
457 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
458
459 if (selectedGatewayNode == null) {
460 final String errorFormat = ERR_FLOW + "no gateway node selected";
461 throw new IllegalStateException(errorFormat);
462 }
463
464 TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
465 .matchEthType(Ethernet.TYPE_IPV4)
466 .matchIPDst(floating.toIpPrefix());
467
468 TrafficTreatment.Builder externalTreatmentBuilder = DefaultTrafficTreatment.builder()
469 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
470 .setEthDst(instPort.macAddress())
471 .setIpDst(instPort.ipAddress().getIp4Address());
472
473 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
474 externalSelectorBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId()).build();
475 externalTreatmentBuilder.popVlan();
476 }
477
478 switch (osNet.getNetworkType()) {
479 case VXLAN:
480 externalTreatmentBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
481 .extension(buildExtension(
482 deviceService,
483 selectedGatewayNode.intgBridge(),
484 cNode.dataIp().getIp4Address()),
485 selectedGatewayNode.intgBridge())
486 .setOutput(selectedGatewayNode.tunnelPortNum());
487 break;
488 case VLAN:
489 externalTreatmentBuilder.pushVlan()
490 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
491 .setOutput(selectedGatewayNode.vlanPortNum());
492 break;
493 default:
494 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
495 osNet.getNetworkType());
496 throw new IllegalStateException(error);
497 }
498
499 osFlowRuleService.setRule(
500 appId,
501 selectedGatewayNode.intgBridge(),
502 externalSelectorBuilder.build(),
503 externalTreatmentBuilder.build(),
504 PRIORITY_FLOATING_EXTERNAL,
505 GW_COMMON_TABLE,
506 install);
507 }
508
Hyunsun Moon44aac662017-02-18 02:07:01 +0900509 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900510 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
511 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900512 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900513 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900514 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900515 .matchIPSrc(instPort.ipAddress().toIpPrefix());
516
517 switch (osNet.getNetworkType()) {
518 case VXLAN:
519 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
520 break;
521 case VLAN:
522 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
523 break;
524 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800525 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900526 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900527 throw new IllegalStateException(error);
528 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900529
Daniel Park75e3d7f2018-05-29 14:43:53 +0900530 TrafficSelector selector = sBuilder.build();
daniel parkeeb8e042018-02-21 14:06:58 +0900531
Hyunsun Moon0d457362017-06-27 17:19:41 +0900532 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900533 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
534 .setIpSrc(floating.getIp4Address())
535 .setEthSrc(instPort.macAddress())
536 .setEthDst(externalPeerRouter.externalPeerRouterMac());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900537
Daniel Park75e3d7f2018-05-29 14:43:53 +0900538 if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
539 tBuilder.popVlan();
540 }
541
542 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
543 tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
544 }
sanghodc375372017-06-08 10:41:30 +0900545 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900546 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900547 gNode.intgBridge(),
Daniel Park75e3d7f2018-05-29 14:43:53 +0900548 selector,
daniel parkeeb8e042018-02-21 14:06:58 +0900549 tBuilder.setOutput(gNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900550 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900551 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900552 install);
Daniel Park75e3d7f2018-05-29 14:43:53 +0900553 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900554 log.trace("Succeeded to set flow rules for upstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900555 }
556
daniel park32b42202018-03-14 16:53:44 +0900557 private ExternalPeerRouter externalPeerRouter(Network network) {
daniel parkeeb8e042018-02-21 14:06:58 +0900558 if (network == null) {
559 return null;
560 }
561
562 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
563
564 if (subnet == null) {
565 return null;
566 }
567
Jian Lif3a28b02018-06-11 21:29:13 +0900568 RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
daniel parkeeb8e042018-02-21 14:06:58 +0900569 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
570 .findAny().orElse(null);
571 if (osRouterIface == null) {
572 return null;
573 }
574
Jian Lif3a28b02018-06-11 21:29:13 +0900575 Router osRouter = osRouterAdminService.router(osRouterIface.getId());
daniel parkeeb8e042018-02-21 14:06:58 +0900576 if (osRouter == null) {
577 return null;
578 }
579 if (osRouter.getExternalGatewayInfo() == null) {
580 return null;
581 }
582
583 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
daniel park65e1c202018-04-03 13:15:28 +0900584 return osNetworkService.externalPeerRouter(exGatewayInfo);
daniel parkeeb8e042018-02-21 14:06:58 +0900585 }
daniel park65e1c202018-04-03 13:15:28 +0900586
Jian Li99892e92018-04-13 14:59:39 +0900587
588 private void associateFloatingIp(NetFloatingIP osFip) {
589 Port osPort = osNetworkService.port(osFip.getPortId());
590 if (osPort == null) {
591 final String errorFormat = ERR_FLOW + "port(%s) not found";
592 final String error = String.format(errorFormat,
593 osFip.getFloatingIpAddress(), osFip.getPortId());
594 throw new IllegalStateException(error);
595 }
596 // set floating IP rules only if the port is associated to a VM
597 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900598 setFloatingIpRules(osFip, osPort, null, true);
Jian Li99892e92018-04-13 14:59:39 +0900599 }
600 }
601
602 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
603 Port osPort = osNetworkService.port(portId);
604 if (osPort == null) {
605 // FIXME when a port with floating IP removed without
606 // disassociation step, it can reach here
607 return;
608 }
609 // set floating IP rules only if the port is associated to a VM
610 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900611 setFloatingIpRules(osFip, osPort, null, false);
Jian Li99892e92018-04-13 14:59:39 +0900612 }
613 }
614
Hyunsun Moon0d457362017-06-27 17:19:41 +0900615 private class InternalFloatingIpListener implements OpenstackRouterListener {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900616
617 @Override
618 public boolean isRelevant(OpenstackRouterEvent event) {
619 // do not allow to proceed without leadership
620 NodeId leader = leadershipService.getLeader(appId.name());
621 if (!Objects.equals(localNodeId, leader)) {
622 return false;
623 }
624 return event.floatingIp() != null;
625 }
626
627 @Override
628 public void event(OpenstackRouterEvent event) {
629 switch (event.type()) {
630 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900631 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900632 NetFloatingIP osFip = event.floatingIp();
633 associateFloatingIp(osFip);
634 log.info("Associated floating IP {}:{}",
635 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900636 });
637 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900638 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
639 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900640 NetFloatingIP osFip = event.floatingIp();
641 disassociateFloatingIp(osFip, event.portId());
642 log.info("Disassociated floating IP {}:{}",
643 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900644 });
645 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900646 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900647 eventExecutor.execute(() -> {
648 NetFloatingIP osFip = event.floatingIp();
649 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
650 associateFloatingIp(event.floatingIp());
651 }
652 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
653 });
654 break;
Jian Lia171a432018-06-11 11:52:11 +0900655 case OPENSTACK_FLOATING_IP_REMOVED:
656 eventExecutor.execute(() -> {
657 NetFloatingIP osFip = event.floatingIp();
658 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
659 disassociateFloatingIp(osFip, osFip.getPortId());
660 }
661 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
662 });
663 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900664 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900665 case OPENSTACK_ROUTER_CREATED:
666 case OPENSTACK_ROUTER_UPDATED:
667 case OPENSTACK_ROUTER_REMOVED:
668 case OPENSTACK_ROUTER_INTERFACE_ADDED:
669 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
670 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
671 default:
672 // do nothing for the other events
673 break;
674 }
675 }
676 }
677
678 private class InternalNodeListener implements OpenstackNodeListener {
679
680 @Override
681 public boolean isRelevant(OpenstackNodeEvent event) {
682 // do not allow to proceed without leadership
683 NodeId leader = leadershipService.getLeader(appId.name());
684 if (!Objects.equals(localNodeId, leader)) {
685 return false;
686 }
687 return event.subject().type() == GATEWAY;
688 }
689
690 @Override
691 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900692
693 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900694 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900695 eventExecutor.execute(() -> {
Jian Lif3a28b02018-06-11 21:29:13 +0900696 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900697 if (Strings.isNullOrEmpty(fip.getPortId())) {
698 continue;
699 }
700 Port osPort = osNetworkService.port(fip.getPortId());
701 if (osPort == null) {
702 log.warn("Failed to set floating IP {}", fip.getId());
703 continue;
704 }
Jian Li1064e4f2018-05-29 16:16:53 +0900705 setFloatingIpRules(fip, osPort, event.subject(), true);
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900706 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900707 });
708 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900709 case OPENSTACK_NODE_INCOMPLETE:
Jian Li1064e4f2018-05-29 16:16:53 +0900710 eventExecutor.execute(() -> {
Jian Lif3a28b02018-06-11 21:29:13 +0900711 for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
Jian Li1064e4f2018-05-29 16:16:53 +0900712 if (Strings.isNullOrEmpty(fip.getPortId())) {
713 continue;
714 }
715 Port osPort = osNetworkService.port(fip.getPortId());
716 if (osPort == null) {
717 log.warn("Failed to set floating IP {}", fip.getId());
718 continue;
719 }
720 Network osNet = osNetworkService.network(osPort.getNetworkId());
721 if (osNet == null) {
722 final String errorFormat = ERR_FLOW + "no network(%s) exists";
723 final String error = String.format(errorFormat,
724 fip.getFloatingIpAddress(),
725 osPort.getNetworkId());
726 throw new IllegalStateException(error);
727 }
728 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
729 log.trace("Mac address of openstack port: {}", srcMac);
730 InstancePort instPort = instancePortService.instancePort(srcMac);
731
732 if (instPort == null) {
733 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
734 final String error = String.format(errorFormat,
735 fip.getFloatingIpAddress(), srcMac);
736 throw new IllegalStateException(error);
737 }
738
Jian Lide679782018-06-05 01:41:29 +0900739 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
740 if (externalPeerRouter == null) {
741 final String errorFormat = ERR_FLOW + "no external peer router found";
742 throw new IllegalStateException(errorFormat);
743 }
744
745 updateComputeNodeRules(instPort, osNet, event.subject(), false);
746 updateGatewayNodeRules(fip, instPort, osNet,
747 externalPeerRouter, event.subject(), false);
Jian Li1064e4f2018-05-29 16:16:53 +0900748 }
749 });
750 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900751 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900752 // do nothing
Hyunsun Moon44aac662017-02-18 02:07:01 +0900753 break;
754 }
755 }
756 }
Jian Li99892e92018-04-13 14:59:39 +0900757
758 private class InternalHostListener implements HostListener {
759
760 @Override
761 public boolean isRelevant(HostEvent event) {
762 Host host = event.subject();
763 if (!isValidHost(host)) {
764 log.debug("Invalid host detected, ignore it {}", host);
765 return false;
766 }
767 return true;
768 }
769
770 @Override
771 public void event(HostEvent event) {
772 InstancePort instPort = HostBasedInstancePort.of(event.subject());
773 switch (event.type()) {
774 case HOST_REMOVED:
775 storeTempInstPort(instPort);
776 break;
777 case HOST_UPDATED:
778 case HOST_ADDED:
779 default:
780 break;
781 }
782 }
783
784 private void storeTempInstPort(InstancePort port) {
Jian Lif3a28b02018-06-11 21:29:13 +0900785 Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
Jian Li99892e92018-04-13 14:59:39 +0900786 for (NetFloatingIP fip : ips) {
787 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
788 continue;
789 }
790 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
791 continue;
792 }
793 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
794 removedPorts.put(port.macAddress(), port);
Jian Lif3a28b02018-06-11 21:29:13 +0900795 NeutronFloatingIP neutronFip = (NeutronFloatingIP) fip;
796 // invalidate bound fixed IP and port
797 neutronFip.setFixedIpAddress(null);
798 neutronFip.setPortId(null);
799 osRouterAdminService.updateFloatingIp(neutronFip);
800 log.info("Updated floating IP {}, due to host removal",
801 neutronFip.getFloatingIpAddress());
Jian Li99892e92018-04-13 14:59:39 +0900802 }
803 }
804 }
805
806 private boolean isValidHost(Host host) {
807 return !host.ipAddresses().isEmpty() &&
808 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
809 host.annotations().value(ANNOTATION_PORT_ID) != null;
810 }
811 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900812}