blob: dfe20b80be97218db02132ab55d0ed5aa37026b4 [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
192 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 Li1064e4f2018-05-29 16:16:53 +0900198 setComputeNodeToGateway(instPort, osNet, gateway, install);
daniel park32b42202018-03-14 16:53:44 +0900199 setDownstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
200 setUpstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900201 log.trace("Succeeded to set flow rules for floating ip {}:{} and install: {}",
202 floatingIp.getFloatingIpAddress(),
203 floatingIp.getFixedIpAddress(),
204 install);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900205 }
206
Jian Li1064e4f2018-05-29 16:16:53 +0900207 private synchronized void setComputeNodeToGateway(InstancePort instPort,
208 Network osNet,
209 OpenstackNode gateway,
210 boolean install) {
211
212 Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
213 Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
214 finalGws.addAll(ImmutableSet.copyOf(completedGws));
215
216 if (gateway == null) {
217 // these are floating IP related cases...
218 setComputeNodeToGatewayHelper(instPort, osNet,
219 ImmutableSet.copyOf(finalGws), install);
220 } else {
221 // these are openstack node related cases...
222 if (install) {
223 if (completedGws.contains(gateway)) {
224 if (completedGws.size() > 1) {
225 finalGws.remove(gateway);
226 setComputeNodeToGatewayHelper(instPort, osNet,
227 ImmutableSet.copyOf(finalGws), false);
228 finalGws.add(gateway);
229 }
230
231 setComputeNodeToGatewayHelper(instPort, osNet,
232 ImmutableSet.copyOf(finalGws), true);
233 } else {
234 log.warn("Detected node should be included in completed gateway set");
235 }
236 } else {
237 if (!completedGws.contains(gateway)) {
238 finalGws.add(gateway);
239 setComputeNodeToGatewayHelper(instPort, osNet,
240 ImmutableSet.copyOf(finalGws), false);
241 finalGws.remove(gateway);
242 if (completedGws.size() >= 1) {
243 setComputeNodeToGatewayHelper(instPort, osNet,
244 ImmutableSet.copyOf(finalGws), true);
245 }
246 } else {
247 log.warn("Detected node should NOT be included in completed gateway set");
248 }
249 }
250 }
251 }
252
253 // a helper method
254 private void setComputeNodeToGatewayHelper(InstancePort instPort,
255 Network osNet,
256 Set<OpenstackNode> gateways,
257 boolean install) {
daniel parkeeb8e042018-02-21 14:06:58 +0900258 TrafficTreatment treatment;
259
260 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
261 .matchEthType(Ethernet.TYPE_IPV4)
262 .matchIPSrc(instPort.ipAddress().toIpPrefix())
263 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
264
265 switch (osNet.getNetworkType()) {
266 case VXLAN:
267 sBuilder.matchTunnelId(Long.parseLong(osNet.getProviderSegID()));
268 break;
269 case VLAN:
270 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
271 break;
272 default:
273 final String error = String.format(
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800274 ERR_UNSUPPORTED_NET_TYPE,
daniel parkeeb8e042018-02-21 14:06:58 +0900275 osNet.getNetworkType().toString());
276 throw new IllegalStateException(error);
277 }
278
Jian Li1064e4f2018-05-29 16:16:53 +0900279 OpenstackNode selectedGatewayNode = getGwByComputeDevId(gateways, instPort.deviceId());
280
daniel parkeeb8e042018-02-21 14:06:58 +0900281 if (selectedGatewayNode == null) {
daniel parkc2a2ed62018-04-10 15:17:42 +0900282 final String errorFormat = ERR_FLOW + "no gateway node selected";
283 throw new IllegalStateException(errorFormat);
daniel parkeeb8e042018-02-21 14:06:58 +0900284 }
285 treatment = DefaultTrafficTreatment.builder()
286 .extension(buildExtension(
287 deviceService,
288 instPort.deviceId(),
289 selectedGatewayNode.dataIp().getIp4Address()),
290 instPort.deviceId())
291 .setOutput(osNodeService.node(instPort.deviceId()).tunnelPortNum())
292 .build();
293
294 osFlowRuleService.setRule(
295 appId,
296 instPort.deviceId(),
297 sBuilder.build(),
298 treatment,
299 PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE,
300 ROUTING_TABLE,
301 install);
daniel parkc2a2ed62018-04-10 15:17:42 +0900302 log.trace("Succeeded to set flow rules from compute node to gateway on compute node");
daniel parkeeb8e042018-02-21 14:06:58 +0900303 }
304
Hyunsun Moon44aac662017-02-18 02:07:01 +0900305 private void setDownstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900306 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
307 boolean install) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900308 OpenstackNode cNode = osNodeService.node(instPort.deviceId());
309 if (cNode == null) {
310 final String error = String.format("Cannot find openstack node for device %s",
311 instPort.deviceId());
312 throw new IllegalStateException(error);
313 }
314 if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900315 final String errorFormat = ERR_FLOW + "VXLAN mode is not ready for %s";
316 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900317 throw new IllegalStateException(error);
318 }
319 if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
Jian Li71670d12018-03-02 21:31:07 +0900320 final String errorFormat = ERR_FLOW + "VLAN mode is not ready for %s";
321 final String error = String.format(errorFormat, floatingIp, cNode.hostname());
Hyunsun Moon0d457362017-06-27 17:19:41 +0900322 throw new IllegalStateException(error);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900323 }
324
325 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900326
Hyunsun Moon0d457362017-06-27 17:19:41 +0900327 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park3ad82132018-05-25 18:47:46 +0900328 TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
329 .matchEthType(Ethernet.TYPE_IPV4)
330 .matchIPDst(floating.toIpPrefix());
daniel park32b42202018-03-14 16:53:44 +0900331
Daniel Park3ad82132018-05-25 18:47:46 +0900332 TrafficTreatment.Builder externalTreatmentBuilder = DefaultTrafficTreatment.builder()
333 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
334 .setEthDst(instPort.macAddress())
335 .setIpDst(instPort.ipAddress().getIp4Address());
336
337 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
338 externalSelectorBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId()).build();
339 externalTreatmentBuilder.popVlan();
340 }
daniel parkee8700b2017-05-11 15:50:03 +0900341
342 switch (osNet.getNetworkType()) {
343 case VXLAN:
Daniel Park3ad82132018-05-25 18:47:46 +0900344 externalTreatmentBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
daniel parkee8700b2017-05-11 15:50:03 +0900345 .extension(buildExtension(
346 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900347 gNode.intgBridge(),
348 cNode.dataIp().getIp4Address()),
349 gNode.intgBridge())
350 .setOutput(gNode.tunnelPortNum());
daniel parkee8700b2017-05-11 15:50:03 +0900351 break;
352 case VLAN:
Daniel Park3ad82132018-05-25 18:47:46 +0900353 externalTreatmentBuilder.pushVlan()
daniel parkee8700b2017-05-11 15:50:03 +0900354 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
Hyunsun Moon0d457362017-06-27 17:19:41 +0900355 .setOutput(gNode.vlanPortNum());
daniel parkee8700b2017-05-11 15:50:03 +0900356 break;
357 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800358 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900359 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900360 throw new IllegalStateException(error);
361 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900362
sanghodc375372017-06-08 10:41:30 +0900363 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900364 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900365 gNode.intgBridge(),
daniel park32b42202018-03-14 16:53:44 +0900366 externalSelectorBuilder.build(),
Daniel Park3ad82132018-05-25 18:47:46 +0900367 externalTreatmentBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900368 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900369 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900370 install);
371
Hyunsun Moon0d457362017-06-27 17:19:41 +0900372 // access from one VM to the others via floating IP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900373 TrafficSelector internalSelector = DefaultTrafficSelector.builder()
374 .matchEthType(Ethernet.TYPE_IPV4)
375 .matchIPDst(floating.toIpPrefix())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900376 .matchInPort(gNode.tunnelPortNum())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900377 .build();
378
daniel parkee8700b2017-05-11 15:50:03 +0900379 TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900380 .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
381 .setEthDst(instPort.macAddress())
daniel parkee8700b2017-05-11 15:50:03 +0900382 .setIpDst(instPort.ipAddress().getIp4Address());
383
384 switch (osNet.getNetworkType()) {
385 case VXLAN:
386 internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
387 .extension(buildExtension(
388 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900389 gNode.intgBridge(),
390 cNode.dataIp().getIp4Address()),
391 gNode.intgBridge())
daniel parkee8700b2017-05-11 15:50:03 +0900392 .setOutput(PortNumber.IN_PORT);
393 break;
394 case VLAN:
395 internalBuilder.pushVlan()
396 .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
397 .setOutput(PortNumber.IN_PORT);
398 break;
399 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800400 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900401 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900402 throw new IllegalStateException(error);
403 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900404
sanghodc375372017-06-08 10:41:30 +0900405 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900406 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900407 gNode.intgBridge(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900408 internalSelector,
daniel parkee8700b2017-05-11 15:50:03 +0900409 internalBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900410 PRIORITY_FLOATING_INTERNAL,
sanghodc375372017-06-08 10:41:30 +0900411 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900412 install);
413 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900414 log.trace("Succeeded to set flow rules for downstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900415 }
416
417 private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
daniel park32b42202018-03-14 16:53:44 +0900418 InstancePort instPort, ExternalPeerRouter externalPeerRouter,
419 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900420 IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
daniel parkee8700b2017-05-11 15:50:03 +0900421 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900422 .matchEthType(Ethernet.TYPE_IPV4)
daniel parkee8700b2017-05-11 15:50:03 +0900423 .matchIPSrc(instPort.ipAddress().toIpPrefix());
424
425 switch (osNet.getNetworkType()) {
426 case VXLAN:
427 sBuilder.matchTunnelId(Long.valueOf(osNet.getProviderSegID()));
428 break;
429 case VLAN:
430 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
431 break;
432 default:
Ray Milkeyc6c9b172018-02-26 09:36:31 -0800433 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900434 osNet.getNetworkType());
daniel parkee8700b2017-05-11 15:50:03 +0900435 throw new IllegalStateException(error);
436 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900437
Daniel Park75e3d7f2018-05-29 14:43:53 +0900438 TrafficSelector selector = sBuilder.build();
daniel parkeeb8e042018-02-21 14:06:58 +0900439
Hyunsun Moon0d457362017-06-27 17:19:41 +0900440 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Daniel Park75e3d7f2018-05-29 14:43:53 +0900441 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
442 .setIpSrc(floating.getIp4Address())
443 .setEthSrc(instPort.macAddress())
444 .setEthDst(externalPeerRouter.externalPeerRouterMac());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900445
Daniel Park75e3d7f2018-05-29 14:43:53 +0900446 if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
447 tBuilder.popVlan();
448 }
449
450 if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
451 tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
452 }
sanghodc375372017-06-08 10:41:30 +0900453 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900454 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900455 gNode.intgBridge(),
Daniel Park75e3d7f2018-05-29 14:43:53 +0900456 selector,
daniel parkeeb8e042018-02-21 14:06:58 +0900457 tBuilder.setOutput(gNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900458 PRIORITY_FLOATING_EXTERNAL,
sanghodc375372017-06-08 10:41:30 +0900459 GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900460 install);
Daniel Park75e3d7f2018-05-29 14:43:53 +0900461 });
daniel parkc2a2ed62018-04-10 15:17:42 +0900462 log.trace("Succeeded to set flow rules for upstream on gateway nodes");
Hyunsun Moon44aac662017-02-18 02:07:01 +0900463 }
464
daniel park32b42202018-03-14 16:53:44 +0900465 private ExternalPeerRouter externalPeerRouter(Network network) {
daniel parkeeb8e042018-02-21 14:06:58 +0900466 if (network == null) {
467 return null;
468 }
469
470 Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
471
472 if (subnet == null) {
473 return null;
474 }
475
476 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
477 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
478 .findAny().orElse(null);
479 if (osRouterIface == null) {
480 return null;
481 }
482
483 Router osRouter = osRouterService.router(osRouterIface.getId());
484 if (osRouter == null) {
485 return null;
486 }
487 if (osRouter.getExternalGatewayInfo() == null) {
488 return null;
489 }
490
491 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
daniel park65e1c202018-04-03 13:15:28 +0900492 return osNetworkService.externalPeerRouter(exGatewayInfo);
daniel parkeeb8e042018-02-21 14:06:58 +0900493 }
daniel park65e1c202018-04-03 13:15:28 +0900494
Jian Li99892e92018-04-13 14:59:39 +0900495
496 private void associateFloatingIp(NetFloatingIP osFip) {
497 Port osPort = osNetworkService.port(osFip.getPortId());
498 if (osPort == null) {
499 final String errorFormat = ERR_FLOW + "port(%s) not found";
500 final String error = String.format(errorFormat,
501 osFip.getFloatingIpAddress(), osFip.getPortId());
502 throw new IllegalStateException(error);
503 }
504 // set floating IP rules only if the port is associated to a VM
505 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900506 setFloatingIpRules(osFip, osPort, null, true);
Jian Li99892e92018-04-13 14:59:39 +0900507 }
508 }
509
510 private void disassociateFloatingIp(NetFloatingIP osFip, String portId) {
511 Port osPort = osNetworkService.port(portId);
512 if (osPort == null) {
513 // FIXME when a port with floating IP removed without
514 // disassociation step, it can reach here
515 return;
516 }
517 // set floating IP rules only if the port is associated to a VM
518 if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
Jian Li1064e4f2018-05-29 16:16:53 +0900519 setFloatingIpRules(osFip, osPort, null, false);
Jian Li99892e92018-04-13 14:59:39 +0900520 }
521 }
522
Hyunsun Moon0d457362017-06-27 17:19:41 +0900523 private class InternalFloatingIpListener implements OpenstackRouterListener {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900524
525 @Override
526 public boolean isRelevant(OpenstackRouterEvent event) {
527 // do not allow to proceed without leadership
528 NodeId leader = leadershipService.getLeader(appId.name());
529 if (!Objects.equals(localNodeId, leader)) {
530 return false;
531 }
532 return event.floatingIp() != null;
533 }
534
535 @Override
536 public void event(OpenstackRouterEvent event) {
537 switch (event.type()) {
538 case OPENSTACK_FLOATING_IP_ASSOCIATED:
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900539 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900540 NetFloatingIP osFip = event.floatingIp();
541 associateFloatingIp(osFip);
542 log.info("Associated floating IP {}:{}",
543 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900544 });
545 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900546 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
547 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900548 NetFloatingIP osFip = event.floatingIp();
549 disassociateFloatingIp(osFip, event.portId());
550 log.info("Disassociated floating IP {}:{}",
551 osFip.getFloatingIpAddress(), osFip.getFixedIpAddress());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900552 });
553 break;
554 case OPENSTACK_FLOATING_IP_REMOVED:
555 eventExecutor.execute(() -> {
Hyunsun Moonb720e632017-05-16 15:41:36 +0900556 NetFloatingIP osFip = event.floatingIp();
557 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
558 disassociateFloatingIp(osFip, osFip.getPortId());
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900559 }
Hyunsun Moonb720e632017-05-16 15:41:36 +0900560 log.info("Removed floating IP {}", osFip.getFloatingIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900561 });
562 break;
563 case OPENSTACK_FLOATING_IP_CREATED:
Hyunsun Moonb720e632017-05-16 15:41:36 +0900564 eventExecutor.execute(() -> {
565 NetFloatingIP osFip = event.floatingIp();
566 if (!Strings.isNullOrEmpty(osFip.getPortId())) {
567 associateFloatingIp(event.floatingIp());
568 }
569 log.info("Created floating IP {}", osFip.getFloatingIpAddress());
570 });
571 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900572 case OPENSTACK_FLOATING_IP_UPDATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900573 case OPENSTACK_ROUTER_CREATED:
574 case OPENSTACK_ROUTER_UPDATED:
575 case OPENSTACK_ROUTER_REMOVED:
576 case OPENSTACK_ROUTER_INTERFACE_ADDED:
577 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
578 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
579 default:
580 // do nothing for the other events
581 break;
582 }
583 }
584 }
585
586 private class InternalNodeListener implements OpenstackNodeListener {
587
588 @Override
589 public boolean isRelevant(OpenstackNodeEvent event) {
590 // do not allow to proceed without leadership
591 NodeId leader = leadershipService.getLeader(appId.name());
592 if (!Objects.equals(localNodeId, leader)) {
593 return false;
594 }
595 return event.subject().type() == GATEWAY;
596 }
597
598 @Override
599 public void event(OpenstackNodeEvent event) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900600
601 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900602 case OPENSTACK_NODE_COMPLETE:
Hyunsun Moon44aac662017-02-18 02:07:01 +0900603 eventExecutor.execute(() -> {
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900604 for (NetFloatingIP fip : osRouterService.floatingIps()) {
605 if (Strings.isNullOrEmpty(fip.getPortId())) {
606 continue;
607 }
608 Port osPort = osNetworkService.port(fip.getPortId());
609 if (osPort == null) {
610 log.warn("Failed to set floating IP {}", fip.getId());
611 continue;
612 }
Jian Li1064e4f2018-05-29 16:16:53 +0900613 setFloatingIpRules(fip, osPort, event.subject(), true);
Hyunsun Moon7a5f9042017-05-11 18:19:01 +0900614 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900615 });
616 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900617 case OPENSTACK_NODE_INCOMPLETE:
Jian Li1064e4f2018-05-29 16:16:53 +0900618
619 // we only purge the routing related rules stored in each
620 // compute node when gateway node becomes unavailable
621 if (!event.subject().type().equals(GATEWAY)) {
622 return;
623 }
624
625 eventExecutor.execute(() -> {
626 for (NetFloatingIP fip : osRouterService.floatingIps()) {
627 if (Strings.isNullOrEmpty(fip.getPortId())) {
628 continue;
629 }
630 Port osPort = osNetworkService.port(fip.getPortId());
631 if (osPort == null) {
632 log.warn("Failed to set floating IP {}", fip.getId());
633 continue;
634 }
635 Network osNet = osNetworkService.network(osPort.getNetworkId());
636 if (osNet == null) {
637 final String errorFormat = ERR_FLOW + "no network(%s) exists";
638 final String error = String.format(errorFormat,
639 fip.getFloatingIpAddress(),
640 osPort.getNetworkId());
641 throw new IllegalStateException(error);
642 }
643 MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
644 log.trace("Mac address of openstack port: {}", srcMac);
645 InstancePort instPort = instancePortService.instancePort(srcMac);
646
647 if (instPort == null) {
648 final String errorFormat = ERR_FLOW + "no host(MAC:%s) found";
649 final String error = String.format(errorFormat,
650 fip.getFloatingIpAddress(), srcMac);
651 throw new IllegalStateException(error);
652 }
653
654 setComputeNodeToGateway(instPort, osNet, event.subject(), false);
655 }
656 });
657 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900658 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900659 // do nothing
Hyunsun Moon44aac662017-02-18 02:07:01 +0900660 break;
661 }
662 }
663 }
Jian Li99892e92018-04-13 14:59:39 +0900664
665 private class InternalHostListener implements HostListener {
666
667 @Override
668 public boolean isRelevant(HostEvent event) {
669 Host host = event.subject();
670 if (!isValidHost(host)) {
671 log.debug("Invalid host detected, ignore it {}", host);
672 return false;
673 }
674 return true;
675 }
676
677 @Override
678 public void event(HostEvent event) {
679 InstancePort instPort = HostBasedInstancePort.of(event.subject());
680 switch (event.type()) {
681 case HOST_REMOVED:
682 storeTempInstPort(instPort);
683 break;
684 case HOST_UPDATED:
685 case HOST_ADDED:
686 default:
687 break;
688 }
689 }
690
691 private void storeTempInstPort(InstancePort port) {
692 Set<NetFloatingIP> ips = osRouterService.floatingIps();
693 for (NetFloatingIP fip : ips) {
694 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
695 continue;
696 }
697 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
698 continue;
699 }
700 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
701 removedPorts.put(port.macAddress(), port);
702 eventExecutor.execute(() -> {
703 disassociateFloatingIp(fip, port.portId());
704 log.info("Disassociated floating IP {}:{}",
705 fip.getFloatingIpAddress(), fip.getFixedIpAddress());
706 });
707 }
708 }
709 }
710
711 private boolean isValidHost(Host host) {
712 return !host.ipAddresses().isEmpty() &&
713 host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
714 host.annotations().value(ANNOTATION_PORT_ID) != null;
715 }
716 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900717}