blob: 6493fc822e34532e0216636b777e24ff0c1b5c29 [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;
Hyunsun Moon44aac662017-02-18 02:07:01 +090052import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
53import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
54import org.onosproject.openstacknetworking.api.OpenstackRouterService;
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;
Hyunsun Moon44aac662017-02-18 02:07:01 +090067import org.slf4j.Logger;
68import org.slf4j.LoggerFactory;
69
Jian Li99892e92018-04-13 14:59:39 +090070import java.util.Map;
Hyunsun Moon44aac662017-02-18 02:07:01 +090071import java.util.Objects;
Jian Li99892e92018-04-13 14:59:39 +090072import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090073import java.util.concurrent.ExecutorService;
74
75import static java.util.concurrent.Executors.newSingleThreadExecutor;
76import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090077import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
78import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
daniel parkeeb8e042018-02-21 14:06:58 +090079import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE;
sanghodc375372017-06-08 10:41:30 +090080import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
81import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_INTERNAL;
daniel parkeeb8e042018-02-21 14:06:58 +090082import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
Jian Li99892e92018-04-13 14:59:39 +090083import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
84import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
Jian Li1064e4f2018-05-29 16:16:53 +090085import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
Jian Li26949762018-03-30 15:46:37 +090086import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090087import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090088
89/**
90 * Handles OpenStack floating IP events.
91 */
92@Component(immediate = true)
93public class OpenstackRoutingFloatingIpHandler {
94
95 private final Logger log = LoggerFactory.getLogger(getClass());
96
97 private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
Ray Milkeyc6c9b172018-02-26 09:36:31 -080098 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type %s";
Hyunsun Moon44aac662017-02-18 02:07:01 +090099
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected CoreService coreService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected DeviceService deviceService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900107 protected LeadershipService leadershipService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected ClusterService clusterService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li99892e92018-04-13 14:59:39 +0900113 protected HostService hostService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900116 protected OpenstackNodeService osNodeService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected InstancePortService instancePortService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected OpenstackRouterService osRouterService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected OpenstackNetworkService osNetworkService;
126
sanghodc375372017-06-08 10:41:30 +0900127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected OpenstackFlowRuleService osFlowRuleService;
129
Hyunsun Moon44aac662017-02-18 02:07:01 +0900130 private final ExecutorService eventExecutor = newSingleThreadExecutor(
131 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900132 private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900133 private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
Jian Li99892e92018-04-13 14:59:39 +0900134 private final HostListener hostListener = new InternalHostListener();
135 private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900136
137 private ApplicationId appId;
138 private NodeId localNodeId;
139
140 @Activate
141 protected void activate() {
142 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
143 localNodeId = clusterService.getLocalNode().id();
144 leadershipService.runForLeadership(appId.name());
Jian Li99892e92018-04-13 14:59:39 +0900145 hostService.addListener(hostListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900146 osRouterService.addListener(floatingIpLisener);
147 osNodeService.addListener(osNodeListener);
148
149 log.info("Started");
150 }
151
152 @Deactivate
153 protected void deactivate() {
Jian Li99892e92018-04-13 14:59:39 +0900154 hostService.removeListener(hostListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900155 osNodeService.removeListener(osNodeListener);
156 osRouterService.removeListener(floatingIpLisener);
157 leadershipService.withdraw(appId.name());
158 eventExecutor.shutdown();
159
160 log.info("Stopped");
161 }
162
Hyunsun Moon44aac662017-02-18 02:07:01 +0900163 private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
Jian Li1064e4f2018-05-29 16:16:53 +0900164 OpenstackNode gateway, boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900165 Network osNet = osNetworkService.network(osPort.getNetworkId());
166 if (osNet == null) {
Jian Li71670d12018-03-02 21:31:07 +0900167 final String errorFormat = ERR_FLOW + "no network(%s) exists";
168 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169 floatingIp.getFloatingIpAddress(),
170 osPort.getNetworkId());
171 throw new IllegalStateException(error);
172 }
173
174 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
daniel parkc2a2ed62018-04-10 15:17:42 +0900175 log.trace("Mac address of openstack port: {}", srcMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900176 InstancePort instPort = instancePortService.instancePort(srcMac);
Jian Li99892e92018-04-13 14:59:39 +0900177
178 // sweep through removed port map
179 if (instPort == null) {
180 instPort = removedPorts.get(srcMac);
181 removedPorts.remove(srcMac);
182 }
183
Hyunsun Moon44aac662017-02-18 02:07:01 +0900184 if (instPort == null) {
Jian Li71670d12018-03-02 21:31:07 +0900185 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
186 final String error = String.format(errorFormat,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900187 floatingIp.getFloatingIpAddress(), srcMac);
188 throw new IllegalStateException(error);
189 }
190
daniel park32b42202018-03-14 16:53:44 +0900191 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
192 if (externalPeerRouter == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900193 final String errorFormat = ERR_FLOW + "no external peer router found";
194 throw new IllegalStateException(errorFormat);
daniel park32b42202018-03-14 16:53:44 +0900195 }
196
Jian Lide679782018-06-05 01:41:29 +0900197 updateComputeNodeRules(instPort, osNet, gateway, install);
198 updateGatewayNodeRules(floatingIp, instPort, osNet, externalPeerRouter, gateway, install);
199
200 // FIXME: downstream internal rules are still duplicated in all gateway nodes
201 // need to make the internal rules de-duplicated sooner or later
202 setDownstreamInternalRules(floatingIp, osNet, instPort, install);
203
204 // TODO: need to refactor setUpstreamRules if possible
daniel park32b42202018-03-14 16:53:44 +0900205 setUpstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900206 log.trace("Succeeded to set flow rules for floating ip {}:{} and install: {}",
207 floatingIp.getFloatingIpAddress(),
208 floatingIp.getFixedIpAddress(),
209 install);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900210 }
211
Jian Lide679782018-06-05 01:41:29 +0900212 private synchronized void updateGatewayNodeRules(NetFloatingIP fip,
213 InstancePort instPort,
214 Network osNet,
215 ExternalPeerRouter router,
216 OpenstackNode gateway,
217 boolean install) {
218
219 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
220 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
221 finalGws.addAll(ImmutableSet.copyOf(completedGws));
222
Jian Lia171a432018-06-11 11:52:11 +0900223
224 if (gateway == null) {
225 // these are floating IP related cases...
226 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
227 ImmutableSet.copyOf(finalGws), install);
228
Jian Lide679782018-06-05 01:41:29 +0900229 } else {
Jian Lia171a432018-06-11 11:52:11 +0900230 // these are openstack node related cases...
231 if (install) {
232 if (completedGws.contains(gateway)) {
233 if (completedGws.size() > 1) {
234 finalGws.remove(gateway);
235 if (fip.getPortId() != null) {
236 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
237 ImmutableSet.copyOf(finalGws), false);
238 finalGws.add(gateway);
239 }
240 }
Jian Lide679782018-06-05 01:41:29 +0900241 if (fip.getPortId() != null) {
242 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
243 ImmutableSet.copyOf(finalGws), true);
244 }
Jian Lia171a432018-06-11 11:52:11 +0900245 } else {
246 log.warn("Detected node should be included in completed gateway set");
Jian Lide679782018-06-05 01:41:29 +0900247 }
248 } else {
Jian Lia171a432018-06-11 11:52:11 +0900249 if (!completedGws.contains(gateway)) {
250 if (completedGws.size() >= 1) {
251 if (fip.getPortId() != null) {
252 setDownstreamExternalRulesHelper(fip, osNet, instPort, router,
253 ImmutableSet.copyOf(finalGws), true);
254 }
255 }
256 } else {
257 log.warn("Detected node should NOT be included in completed gateway set");
258 }
Jian Lide679782018-06-05 01:41:29 +0900259 }
260 }
261 }
262
263 private synchronized void updateComputeNodeRules(InstancePort instPort,
264 Network osNet,
265 OpenstackNode gateway,
266 boolean install) {
Jian Li1064e4f2018-05-29 16:16:53 +0900267
268 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
269 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
270 finalGws.addAll(ImmutableSet.copyOf(completedGws));
271
272 if (gateway == null) {
273 // these are floating IP related cases...
274 setComputeNodeToGatewayHelper(instPort, osNet,
275 ImmutableSet.copyOf(finalGws), install);
Jian Lide679782018-06-05 01:41:29 +0900276
Jian Li1064e4f2018-05-29 16:16:53 +0900277 } else {
278 // these are openstack node related cases...
279 if (install) {
280 if (completedGws.contains(gateway)) {
281 if (completedGws.size() > 1) {
282 finalGws.remove(gateway);
283 setComputeNodeToGatewayHelper(instPort, osNet,
284 ImmutableSet.copyOf(finalGws), false);
285 finalGws.add(gateway);
286 }
287
288 setComputeNodeToGatewayHelper(instPort, osNet,
289 ImmutableSet.copyOf(finalGws), true);
290 } else {
291 log.warn("Detected node should be included in completed gateway set");
292 }
293 } else {
294 if (!completedGws.contains(gateway)) {
295 finalGws.add(gateway);
296 setComputeNodeToGatewayHelper(instPort, osNet,
297 ImmutableSet.copyOf(finalGws), false);
298 finalGws.remove(gateway);
299 if (completedGws.size() >= 1) {
300 setComputeNodeToGatewayHelper(instPort, osNet,
301 ImmutableSet.copyOf(finalGws), true);
302 }
303 } else {
304 log.warn("Detected node should NOT be included in completed gateway set");
305 }
306 }
307 }
308 }
309
310 // a helper method
311 private void setComputeNodeToGatewayHelper(InstancePort instPort,
312 Network osNet,
313 Set<OpenstackNode> gateways,
314 boolean install) {
daniel parkeeb8e042018-02-21 14:06:58 +0900315 TrafficTreatment treatment;
316
317 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
318 .matchEthType(Ethernet.TYPE_IPV4)
319 .matchIPSrc(instPort.ipAddress().toIpPrefix())
320 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
321
322 switch (osNet.getNetworkType()) {
323 case VXLAN:
324 sBuilder.matchTunnelId(Long.parseLong(osNet.getProviderSegID()));
325 break;
326 case VLAN:
327 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
328 break;
329 default:
330 final String error = String.format(
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800331 ERR_UNSUPPORTED_NET_TYPE,
daniel parkeeb8e042018-02-21 14:06:58 +0900332 osNet.getNetworkType().toString());
333 throw new IllegalStateException(error);
334 }
335
Jian Li1064e4f2018-05-29 16:16:53 +0900336 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
337
daniel parkeeb8e042018-02-21 14:06:58 +0900338 if (selectedGatewayNode == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900339 final String errorFormat = ERR_FLOW + "no gateway node selected";
340 throw new IllegalStateException(errorFormat);
daniel parkeeb8e042018-02-21 14:06:58 +0900341 }
342 treatment = DefaultTrafficTreatment.builder()
343 .extension(buildExtension(
344 deviceService,
345 instPort.deviceId(),
346 selectedGatewayNode.dataIp().getIp4Address()),
347 instPort.deviceId())
348 .setOutput(osNodeService.node(instPort.deviceId()).tunnelPortNum())
349 .build();
350
351 osFlowRuleService.setRule(
352 appId,
353 instPort.deviceId(),
354 sBuilder.build(),
355 treatment,
356 PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE,
357 ROUTING_TABLE,
358 install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900359 log.trace("Succeeded to set flow rules from compute node to gateway on compute node");
daniel parkeeb8e042018-02-21 14:06:58 +0900360 }
361
Jian Lide679782018-06-05 01:41:29 +0900362 private void setDownstreamInternalRules(NetFloatingIP floatingIp,
363 Network osNet,
364 InstancePort instPort,
365 boolean install) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900366 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
367 if (cNode == null) {
368 final String error = String.format("Cannot find openstack node for device %s",
369 instPort.deviceId());
370 throw new IllegalStateException(error);
371 }
372 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900373 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
374 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900375 throw new IllegalStateException(error);
376 }
377 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900378 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
379 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900380 throw new IllegalStateException(error);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900381 }
382
383 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900384
Jian Lide679782018-06-05 01:41:29 +0900385 // TODO: following code snippet will be refactored sooner or later
Hyunsun Moon0d457362017-06-27 17:19:41 +0900386 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900387 // access from one VM to the others via floating IP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900388 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
389 .matchEthType(Ethernet.TYPE_IPV4)
390 .matchIPDst(floating.toIpPrefix())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900391 .matchInPort(gNode.tunnelPortNum())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900392 .build();
393
daniel parkee8700b2017-05-11 15:50:03 +0900394 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900395 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
396 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900397 .setIpDst(instPort.ipAddress().getIp4Address());
398
399 switch (osNet.getNetworkType()) {
400 case VXLAN:
401 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
402 .extension(buildExtension(
403 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900404 gNode.intgBridge(),
405 cNode.dataIp().getIp4Address()),
406 gNode.intgBridge())
daniel parkee8700b2017-05-11 15:50:03 +0900407 .setOutput(PortNumber.IN_PORT);
408 break;
409 case VLAN:
410 internalBuilder.pushVlan()
411 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
412 .setOutput(PortNumber.IN_PORT);
413 break;
414 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800415 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900416 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900417 throw new IllegalStateException(error);
418 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900419
sanghodc375372017-06-08 10:41:30 +0900420 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900421 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900422 gNode.intgBridge(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900423 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900424 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900425 PRIORITY_FLOATING_INTERNAL,
sanghodc375372017-06-08 10:41:30 +0900426 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900427 install);
428 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900429 log.trace("Succeeded to set flow rules for downstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900430 }
431
Jian Lide679782018-06-05 01:41:29 +0900432 private void setDownstreamExternalRulesHelper(NetFloatingIP floatingIp,
433 Network osNet,
434 InstancePort instPort,
435 ExternalPeerRouter externalPeerRouter,
436 Set<OpenstackNode> gateways, boolean install) {
437 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
438 if (cNode == null) {
439 final String error = String.format("Cannot find openstack node for device %s",
440 instPort.deviceId());
441 throw new IllegalStateException(error);
442 }
443 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
444 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
445 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
446 throw new IllegalStateException(error);
447 }
448 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
449 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
450 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
451 throw new IllegalStateException(error);
452 }
453
454 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
455
456 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
457
458 if (selectedGatewayNode == null) {
459 final String errorFormat = ERR_FLOW + "no gateway node selected";
460 throw new IllegalStateException(errorFormat);
461 }
462
463 TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
464 .matchEthType(Ethernet.TYPE_IPV4)
465 .matchIPDst(floating.toIpPrefix());
466
467 TrafficTreatment.Builder externalTreatmentBuilder = DefaultTrafficTreatment.builder()
468 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
469 .setEthDst(instPort.macAddress())
470 .setIpDst(instPort.ipAddress().getIp4Address());
471
472 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
473 externalSelectorBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId()).build();
474 externalTreatmentBuilder.popVlan();
475 }
476
477 switch (osNet.getNetworkType()) {
478 case VXLAN:
479 externalTreatmentBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
480 .extension(buildExtension(
481 deviceService,
482 selectedGatewayNode.intgBridge(),
483 cNode.dataIp().getIp4Address()),
484 selectedGatewayNode.intgBridge())
485 .setOutput(selectedGatewayNode.tunnelPortNum());
486 break;
487 case VLAN:
488 externalTreatmentBuilder.pushVlan()
489 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
490 .setOutput(selectedGatewayNode.vlanPortNum());
491 break;
492 default:
493 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
494 osNet.getNetworkType());
495 throw new IllegalStateException(error);
496 }
497
498 osFlowRuleService.setRule(
499 appId,
500 selectedGatewayNode.intgBridge(),
501 externalSelectorBuilder.build(),
502 externalTreatmentBuilder.build(),
503 PRIORITY_FLOATING_EXTERNAL,
504 GW_COMMON_TABLE,
505 install);
506 }
507
Hyunsun Moon44aac662017-02-18 02:07:01 +0900508 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900509 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
510 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900511 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900512 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900513 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900514 .matchIPSrc(instPort.ipAddress().toIpPrefix());
515
516 switch (osNet.getNetworkType()) {
517 case VXLAN:
518 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
519 break;
520 case VLAN:
521 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
522 break;
523 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800524 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900525 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900526 throw new IllegalStateException(error);
527 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900528
Daniel Park75e3d7f2018-05-29 14:43:53 +0900529 TrafficSelector selector = sBuilder.build();
daniel parkeeb8e042018-02-21 14:06:58 +0900530
Hyunsun Moon0d457362017-06-27 17:19:41 +0900531 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900532 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
533 .setIpSrc(floating.getIp4Address())
534 .setEthSrc(instPort.macAddress())
535 .setEthDst(externalPeerRouter.externalPeerRouterMac());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900536
Daniel Park75e3d7f2018-05-29 14:43:53 +0900537 if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
538 tBuilder.popVlan();
539 }
540
541 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
542 tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
543 }
sanghodc375372017-06-08 10:41:30 +0900544 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900545 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900546 gNode.intgBridge(),
Daniel Park75e3d7f2018-05-29 14:43:53 +0900547 selector,
daniel parkeeb8e042018-02-21 14:06:58 +0900548 tBuilder.setOutput(gNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900549 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900550 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900551 install);
Daniel Park75e3d7f2018-05-29 14:43:53 +0900552 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900553 log.trace("Succeeded to set flow rules for upstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900554 }
555
daniel park32b42202018-03-14 16:53:44 +0900556 private ExternalPeerRouter externalPeerRouter(Network network) {
daniel parkeeb8e042018-02-21 14:06:58 +0900557 if (network == null) {
558 return null;
559 }
560
561 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
562
563 if (subnet == null) {
564 return null;
565 }
566
567 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
568 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
569 .findAny().orElse(null);
570 if (osRouterIface == null) {
571 return null;
572 }
573
574 Router osRouter = osRouterService.router(osRouterIface.getId());
575 if (osRouter == null) {
576 return null;
577 }
578 if (osRouter.getExternalGatewayInfo() == null) {
579 return null;
580 }
581
582 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
daniel park65e1c202018-04-03 13:15:28 +0900583 return osNetworkService.externalPeerRouter(exGatewayInfo);
daniel parkeeb8e042018-02-21 14:06:58 +0900584 }
daniel park65e1c202018-04-03 13:15:28 +0900585
Jian Li99892e92018-04-13 14:59:39 +0900586
587 private void associateFloatingIp(NetFloatingIP osFip) {
588 Port osPort = osNetworkService.port(osFip.getPortId());
589 if (osPort == null) {
590 final String errorFormat = ERR_FLOW + "port(%s) not found";
591 final String error = String.format(errorFormat,
592 osFip.getFloatingIpAddress(), osFip.getPortId());
593 throw new IllegalStateException(error);
594 }
595 // set floating IP rules only if the port is associated to a VM
596 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900597 setFloatingIpRules(osFip, osPort, null, true);
Jian Li99892e92018-04-13 14:59:39 +0900598 }
599 }
600
601 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
602 Port osPort = osNetworkService.port(portId);
603 if (osPort == null) {
604 // FIXME when a port with floating IP removed without
605 // disassociation step, it can reach here
606 return;
607 }
608 // set floating IP rules only if the port is associated to a VM
609 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900610 setFloatingIpRules(osFip, osPort, null, false);
Jian Li99892e92018-04-13 14:59:39 +0900611 }
612 }
613
Hyunsun Moon0d457362017-06-27 17:19:41 +0900614 private class InternalFloatingIpListener implements OpenstackRouterListener {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900615
616 @Override
617 public boolean isRelevant(OpenstackRouterEvent event) {
618 // do not allow to proceed without leadership
619 NodeId leader = leadershipService.getLeader(appId.name());
620 if (!Objects.equals(localNodeId, leader)) {
621 return false;
622 }
623 return event.floatingIp() != null;
624 }
625
626 @Override
627 public void event(OpenstackRouterEvent event) {
628 switch (event.type()) {
629 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900630 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900631 NetFloatingIP osFip = event.floatingIp();
632 associateFloatingIp(osFip);
633 log.info("Associated floating IP {}:{}",
634 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900635 });
636 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900637 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
638 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900639 NetFloatingIP osFip = event.floatingIp();
640 disassociateFloatingIp(osFip, event.portId());
641 log.info("Disassociated floating IP {}:{}",
642 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900643 });
644 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900645 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900646 eventExecutor.execute(() -> {
647 NetFloatingIP osFip = event.floatingIp();
648 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
649 associateFloatingIp(event.floatingIp());
650 }
651 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
652 });
653 break;
Jian Lia171a432018-06-11 11:52:11 +0900654 case OPENSTACK_FLOATING_IP_REMOVED:
655 eventExecutor.execute(() -> {
656 NetFloatingIP osFip = event.floatingIp();
657 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
658 disassociateFloatingIp(osFip, osFip.getPortId());
659 }
660 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
661 });
662 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900663 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900664 case OPENSTACK_ROUTER_CREATED:
665 case OPENSTACK_ROUTER_UPDATED:
666 case OPENSTACK_ROUTER_REMOVED:
667 case OPENSTACK_ROUTER_INTERFACE_ADDED:
668 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
669 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
670 default:
671 // do nothing for the other events
672 break;
673 }
674 }
675 }
676
677 private class InternalNodeListener implements OpenstackNodeListener {
678
679 @Override
680 public boolean isRelevant(OpenstackNodeEvent event) {
681 // do not allow to proceed without leadership
682 NodeId leader = leadershipService.getLeader(appId.name());
683 if (!Objects.equals(localNodeId, leader)) {
684 return false;
685 }
686 return event.subject().type() == GATEWAY;
687 }
688
689 @Override
690 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900691
692 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900693 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900694 eventExecutor.execute(() -> {
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900695 for (NetFloatingIP fip : osRouterService.floatingIps()) {
696 if (Strings.isNullOrEmpty(fip.getPortId())) {
697 continue;
698 }
699 Port osPort = osNetworkService.port(fip.getPortId());
700 if (osPort == null) {
701 log.warn("Failed to set floating IP {}", fip.getId());
702 continue;
703 }
Jian Li1064e4f2018-05-29 16:16:53 +0900704 setFloatingIpRules(fip, osPort, event.subject(), true);
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900705 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900706 });
707 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900708 case OPENSTACK_NODE_INCOMPLETE:
Jian Li1064e4f2018-05-29 16:16:53 +0900709 eventExecutor.execute(() -> {
710 for (NetFloatingIP fip : osRouterService.floatingIps()) {
711 if (Strings.isNullOrEmpty(fip.getPortId())) {
712 continue;
713 }
714 Port osPort = osNetworkService.port(fip.getPortId());
715 if (osPort == null) {
716 log.warn("Failed to set floating IP {}", fip.getId());
717 continue;
718 }
719 Network osNet = osNetworkService.network(osPort.getNetworkId());
720 if (osNet == null) {
721 final String errorFormat = ERR_FLOW + "no network(%s) exists";
722 final String error = String.format(errorFormat,
723 fip.getFloatingIpAddress(),
724 osPort.getNetworkId());
725 throw new IllegalStateException(error);
726 }
727 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
728 log.trace("Mac address of openstack port: {}", srcMac);
729 InstancePort instPort = instancePortService.instancePort(srcMac);
730
731 if (instPort == null) {
732 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
733 final String error = String.format(errorFormat,
734 fip.getFloatingIpAddress(), srcMac);
735 throw new IllegalStateException(error);
736 }
737
Jian Lide679782018-06-05 01:41:29 +0900738 ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
739 if (externalPeerRouter == null) {
740 final String errorFormat = ERR_FLOW + "no external peer router found";
741 throw new IllegalStateException(errorFormat);
742 }
743
744 updateComputeNodeRules(instPort, osNet, event.subject(), false);
745 updateGatewayNodeRules(fip, instPort, osNet,
746 externalPeerRouter, event.subject(), false);
Jian Li1064e4f2018-05-29 16:16:53 +0900747 }
748 });
749 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900750 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900751 // do nothing
Hyunsun Moon44aac662017-02-18 02:07:01 +0900752 break;
753 }
754 }
755 }
Jian Li99892e92018-04-13 14:59:39 +0900756
757 private class InternalHostListener implements HostListener {
758
759 @Override
760 public boolean isRelevant(HostEvent event) {
761 Host host = event.subject();
762 if (!isValidHost(host)) {
763 log.debug("Invalid host detected, ignore it {}", host);
764 return false;
765 }
766 return true;
767 }
768
769 @Override
770 public void event(HostEvent event) {
771 InstancePort instPort = HostBasedInstancePort.of(event.subject());
772 switch (event.type()) {
773 case HOST_REMOVED:
774 storeTempInstPort(instPort);
775 break;
776 case HOST_UPDATED:
777 case HOST_ADDED:
778 default:
779 break;
780 }
781 }
782
783 private void storeTempInstPort(InstancePort port) {
784 Set<NetFloatingIP> ips = osRouterService.floatingIps();
785 for (NetFloatingIP fip : ips) {
786 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
787 continue;
788 }
789 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
790 continue;
791 }
792 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
793 removedPorts.put(port.macAddress(), port);
794 eventExecutor.execute(() -> {
795 disassociateFloatingIp(fip, port.portId());
796 log.info("Disassociated floating IP {}:{}",
797 fip.getFloatingIpAddress(), fip.getFixedIpAddress());
798 });
799 }
800 }
801 }
802
803 private boolean isValidHost(Host host) {
804 return !host.ipAddresses().isEmpty() &&
805 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
806 host.annotations().value(ANNOTATION_PORT_ID) != null;
807 }
808 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900809}