blob: 095080cb3396ce47a5c451f7fef6318e2ca3e426 [file] [log] [blame]
Jian Li4f368e82018-07-02 14:22:22 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
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.openstackvtap.impl;
17
Jian Li614cb092018-07-03 22:41:42 +090018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Lists;
Jian Li26ef1302018-07-04 14:37:06 +090021import com.google.common.collect.Sets;
Jian Li26ef1302018-07-04 14:37:06 +090022import org.onlab.packet.IpAddress;
Jian Li614cb092018-07-03 22:41:42 +090023import org.onlab.packet.IpPrefix;
Jimo Jung14e87bf2018-09-03 16:28:13 +090024import org.onlab.util.Tools;
25import org.onosproject.cfg.ComponentConfigService;
Jian Li614cb092018-07-03 22:41:42 +090026import org.onosproject.cluster.ClusterService;
27import org.onosproject.cluster.LeadershipService;
28import org.onosproject.cluster.NodeId;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
31import org.onosproject.core.GroupId;
Jian Li19f25262018-07-03 22:37:12 +090032import org.onosproject.event.AbstractListenerManager;
Jian Li26ef1302018-07-04 14:37:06 +090033import org.onosproject.net.Device;
Jian Li38e4d942018-07-03 22:19:16 +090034import org.onosproject.net.DeviceId;
Jian Li614cb092018-07-03 22:41:42 +090035import org.onosproject.net.Host;
36import org.onosproject.net.HostLocation;
Jimo Jung14e87bf2018-09-03 16:28:13 +090037import org.onosproject.net.Port;
Jian Li19f25262018-07-03 22:37:12 +090038import org.onosproject.net.PortNumber;
Jimo Jung14e87bf2018-09-03 16:28:13 +090039import org.onosproject.net.behaviour.DefaultTunnelDescription;
Jian Li614cb092018-07-03 22:41:42 +090040import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
Jimo Jung14e87bf2018-09-03 16:28:13 +090041import org.onosproject.net.behaviour.InterfaceConfig;
42import org.onosproject.net.behaviour.TunnelDescription;
43import org.onosproject.net.behaviour.TunnelEndPoints;
44import org.onosproject.net.behaviour.TunnelKey;
Jian Li614cb092018-07-03 22:41:42 +090045import org.onosproject.net.device.DeviceEvent;
46import org.onosproject.net.device.DeviceListener;
47import org.onosproject.net.device.DeviceService;
Jian Li614cb092018-07-03 22:41:42 +090048import org.onosproject.net.flow.DefaultFlowRule;
49import org.onosproject.net.flow.DefaultTrafficSelector;
50import org.onosproject.net.flow.DefaultTrafficTreatment;
51import org.onosproject.net.flow.FlowRule;
52import org.onosproject.net.flow.FlowRuleOperations;
53import org.onosproject.net.flow.FlowRuleOperationsContext;
54import org.onosproject.net.flow.FlowRuleService;
55import org.onosproject.net.flow.TrafficSelector;
56import org.onosproject.net.flow.TrafficTreatment;
Jimo Jung14e87bf2018-09-03 16:28:13 +090057import org.onosproject.net.flow.instructions.ExtensionPropertyException;
Jian Li614cb092018-07-03 22:41:42 +090058import org.onosproject.net.flow.instructions.ExtensionTreatment;
Jian Li614cb092018-07-03 22:41:42 +090059import org.onosproject.net.group.DefaultGroupBucket;
60import org.onosproject.net.group.DefaultGroupDescription;
Jian Li614cb092018-07-03 22:41:42 +090061import org.onosproject.net.group.GroupBucket;
62import org.onosproject.net.group.GroupBuckets;
63import org.onosproject.net.group.GroupDescription;
Jian Li614cb092018-07-03 22:41:42 +090064import org.onosproject.net.group.GroupService;
65import org.onosproject.net.host.HostEvent;
66import org.onosproject.net.host.HostListener;
67import org.onosproject.net.host.HostService;
Jimo Jung14e87bf2018-09-03 16:28:13 +090068import org.onosproject.openstacknode.api.OpenstackNode;
Jian Li26ef1302018-07-04 14:37:06 +090069import org.onosproject.openstacknode.api.OpenstackNodeEvent;
70import org.onosproject.openstacknode.api.OpenstackNodeListener;
71import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li38e4d942018-07-03 22:19:16 +090072import org.onosproject.openstackvtap.api.OpenstackVtap;
Jian Li26ef1302018-07-04 14:37:06 +090073import org.onosproject.openstackvtap.api.OpenstackVtap.Type;
Jian Li19f25262018-07-03 22:37:12 +090074import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
75import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
76import org.onosproject.openstackvtap.api.OpenstackVtapEvent;
Jian Li26ef1302018-07-04 14:37:06 +090077import org.onosproject.openstackvtap.api.OpenstackVtapId;
Jian Li38e4d942018-07-03 22:19:16 +090078import org.onosproject.openstackvtap.api.OpenstackVtapListener;
Jimo Jung14e87bf2018-09-03 16:28:13 +090079import org.onosproject.openstackvtap.api.OpenstackVtapNetwork;
80import org.onosproject.openstackvtap.api.OpenstackVtapNetwork.Mode;
Jian Li4f368e82018-07-02 14:22:22 +090081import org.onosproject.openstackvtap.api.OpenstackVtapService;
Jian Li614cb092018-07-03 22:41:42 +090082import org.onosproject.openstackvtap.api.OpenstackVtapStore;
83import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate;
Jian Li614cb092018-07-03 22:41:42 +090084import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085import org.osgi.service.component.annotations.Activate;
86import org.osgi.service.component.annotations.Component;
87import org.osgi.service.component.annotations.Deactivate;
Ray Milkeyd5425682018-10-23 10:21:33 -070088import org.osgi.service.component.annotations.Modified;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070089import org.osgi.service.component.annotations.Reference;
90import org.osgi.service.component.annotations.ReferenceCardinality;
Jian Li614cb092018-07-03 22:41:42 +090091import org.slf4j.Logger;
Jian Li4f368e82018-07-02 14:22:22 +090092
Jimo Jung14e87bf2018-09-03 16:28:13 +090093import java.util.Dictionary;
Jian Li614cb092018-07-03 22:41:42 +090094import java.util.List;
95import java.util.Objects;
Jian Li38e4d942018-07-03 22:19:16 +090096import java.util.Set;
Jian Li614cb092018-07-03 22:41:42 +090097import java.util.concurrent.ScheduledExecutorService;
Jian Li614cb092018-07-03 22:41:42 +090098import java.util.stream.Collectors;
99import java.util.stream.StreamSupport;
100
101import static com.google.common.base.Preconditions.checkNotNull;
102import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
103import static org.onlab.packet.Ethernet.TYPE_IPV4;
104import static org.onlab.packet.IPv4.PROTOCOL_ICMP;
105import static org.onlab.packet.IPv4.PROTOCOL_TCP;
106import static org.onlab.packet.IPv4.PROTOCOL_UDP;
107import static org.onlab.util.Tools.groupedThreads;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900108import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Jian Li26ef1302018-07-04 14:37:06 +0900109import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900110import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
Jian Li5c09e212018-10-24 18:23:58 +0900111import static org.onosproject.openstacknetworking.api.Constants.DHCP_TABLE;
Jian Li614cb092018-07-03 22:41:42 +0900112import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE;
113import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
114import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_GROUP_TABLE;
115import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_MIRROR_TABLE;
116import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_TABLE;
117import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_GROUP_TABLE;
118import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_MIRROR_TABLE;
119import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_TABLE;
120import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_GROUP_TABLE;
121import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_MIRROR_TABLE;
122import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_TABLE;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900123import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
124import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
Jian Li26ef1302018-07-04 14:37:06 +0900125import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Ray Milkey8e406512018-10-24 15:56:50 -0700126import static org.onosproject.openstackvtap.impl.OsgiPropertyConstants.TUNNEL_NICIRA;
127import static org.onosproject.openstackvtap.impl.OsgiPropertyConstants.TUNNEL_NICRA_DEFAULT;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900128import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.containsIp;
129import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.dumpStackTrace;
Jian Li26ef1302018-07-04 14:37:06 +0900130import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getGroupKey;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900131import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getTunnelName;
132import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getTunnelType;
133import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.hostCompareIp;
134import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.isValidHost;
Jian Li614cb092018-07-03 22:41:42 +0900135import static org.slf4j.LoggerFactory.getLogger;
Jian Li38e4d942018-07-03 22:19:16 +0900136
Jian Li4f368e82018-07-02 14:22:22 +0900137/**
Jimo Jung14e87bf2018-09-03 16:28:13 +0900138 * Provides implementation of the openstack vtap and openstack vtap network APIs.
Jian Li4f368e82018-07-02 14:22:22 +0900139 */
Ray Milkey8e406512018-10-24 15:56:50 -0700140@Component(
141 immediate = true,
142 service = { OpenstackVtapService.class, OpenstackVtapAdminService.class },
143 property = {
144 TUNNEL_NICIRA + ":Boolean=" + TUNNEL_NICRA_DEFAULT
145 }
146)
Jian Li19f25262018-07-03 22:37:12 +0900147public class OpenstackVtapManager
148 extends AbstractListenerManager<OpenstackVtapEvent, OpenstackVtapListener>
149 implements OpenstackVtapService, OpenstackVtapAdminService {
Jian Li4f368e82018-07-02 14:22:22 +0900150
Jian Li614cb092018-07-03 22:41:42 +0900151 private final Logger log = getLogger(getClass());
152
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900154 protected CoreService coreService;
155
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900157 protected ClusterService clusterService;
158
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900160 protected LeadershipService leadershipService;
161
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900163 protected FlowRuleService flowRuleService;
164
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700165 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900166 protected GroupService groupService;
167
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700168 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900169 protected DeviceService deviceService;
170
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700171 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jimo Jung14e87bf2018-09-03 16:28:13 +0900172 protected OpenstackNodeService osNodeService;
173
Ray Milkeyd5425682018-10-23 10:21:33 -0700174 @Reference(cardinality = ReferenceCardinality.MANDATORY)
175
Jian Li614cb092018-07-03 22:41:42 +0900176 protected HostService hostService;
177
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700178 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900179 protected OpenstackVtapStore store;
180
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700181 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jimo Jung14e87bf2018-09-03 16:28:13 +0900182 protected ComponentConfigService componentConfigService;
183
Ray Milkey8e406512018-10-24 15:56:50 -0700184 /** Use nicra extension for tunneling. */
185 private boolean tunnelNicira = TUNNEL_NICRA_DEFAULT;
Jian Li26ef1302018-07-04 14:37:06 +0900186
Jian Li614cb092018-07-03 22:41:42 +0900187 public static final String APP_ID = "org.onosproject.openstackvtap";
Jimo Jung14e87bf2018-09-03 16:28:13 +0900188 public static final String VTAP_DESC_NULL = "vtap field %s cannot be null";
Jian Li614cb092018-07-03 22:41:42 +0900189
190 private static final int PRIORITY_VTAP_RULE = 50000;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900191 private static final int PRIORITY_VTAP_OUTPUT_RULE = 1000;
192 private static final int PRIORITY_VTAP_OUTPUT_DROP = 0;
Jian Li614cb092018-07-03 22:41:42 +0900193
Jian Li5c09e212018-10-24 18:23:58 +0900194 private static final int INBOUND_NEXT_TABLE = DHCP_TABLE;
Jian Li614cb092018-07-03 22:41:42 +0900195 private static final int FLAT_OUTBOUND_NEXT_TABLE = FLAT_TABLE;
196 private static final int OUTBOUND_NEXT_TABLE = FORWARDING_TABLE;
197
Jimo Jung14e87bf2018-09-03 16:28:13 +0900198 private static final int[][] VTAP_TABLES = {
199 {VTAP_INBOUND_TABLE, VTAP_INBOUND_GROUP_TABLE,
200 INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE},
201 {VTAP_FLAT_OUTBOUND_TABLE, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
202 FLAT_OUTBOUND_NEXT_TABLE, VTAP_FLAT_OUTBOUND_MIRROR_TABLE},
203 {VTAP_OUTBOUND_TABLE, VTAP_OUTBOUND_GROUP_TABLE,
204 OUTBOUND_NEXT_TABLE, VTAP_OUTBOUND_MIRROR_TABLE}};
205 private static final int VTAP_TABLE_INBOUND_IDX = 0;
206 private static final int VTAP_TABLE_FLAT_OUTBOUND_IDX = 1;
207 private static final int VTAP_TABLE_OUTBOUND_IDX = 2;
208 private static final int VTAP_TABLE_INPUT_IDX = 0;
209 private static final int VTAP_TABLE_GROUP_IDX = 1;
210 private static final int VTAP_TABLE_NEXT_IDX = 2;
211 private static final int VTAP_TABLE_OUTPUT_IDX = 3;
212
Jian Li311a9c92018-07-09 16:48:36 +0900213 private static final IpPrefix ARBITRARY_IP_PREFIX =
214 IpPrefix.valueOf(IpAddress.valueOf("0.0.0.0"), 0);
Jimo Jung14e87bf2018-09-03 16:28:13 +0900215 private static final String TABLE_EXTENSION = "table";
216 private static final String TUNNEL_DST_EXTENSION = "tunnelDst";
Jimo Jung14e87bf2018-09-03 16:28:13 +0900217
218 private static final int VTAP_NETWORK_KEY = 0;
Jian Li311a9c92018-07-09 16:48:36 +0900219
Jian Li614cb092018-07-03 22:41:42 +0900220 private final DeviceListener deviceListener = new InternalDeviceListener();
Jian Li26ef1302018-07-04 14:37:06 +0900221 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900222 private final HostListener hostListener = new InternalHostListener();
Jian Li614cb092018-07-03 22:41:42 +0900223
224 private OpenstackVtapStoreDelegate delegate = new InternalStoreDelegate();
225
226 private ApplicationId appId;
227 private NodeId localNodeId;
228 private ScheduledExecutorService eventExecutor;
229
Jimo Jung14e87bf2018-09-03 16:28:13 +0900230 private final Object syncInterface = new Object(); // notification of tunnel interface
231 private static final int INTERFACE_MANIPULATION_TIMEOUT = 1000; // 1000msec
232 private static final int INTERFACE_MANIPULATION_RETRY = 10; // 10 times (totally 10sec)
Jian Li614cb092018-07-03 22:41:42 +0900233
234 @Activate
Jimo Jung1bf54352018-11-08 14:16:08 +0900235 public void activate() {
Jian Li614cb092018-07-03 22:41:42 +0900236 appId = coreService.registerApplication(APP_ID);
237 localNodeId = clusterService.getLocalNode().id();
238 leadershipService.runForLeadership(appId.name());
Jimo Jung14e87bf2018-09-03 16:28:13 +0900239 componentConfigService.registerProperties(getClass());
Jian Li614cb092018-07-03 22:41:42 +0900240
241 eventExecutor = newSingleThreadScheduledExecutor(
242 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
243
244 store.setDelegate(delegate);
245 eventDispatcher.addSink(OpenstackVtapEvent.class, listenerRegistry);
246
247 deviceService.addListener(deviceListener);
Jian Li26ef1302018-07-04 14:37:06 +0900248 osNodeService.addListener(osNodeListener);
Jimo Jung14e87bf2018-09-03 16:28:13 +0900249 hostService.addListener(hostListener);
Jian Li26ef1302018-07-04 14:37:06 +0900250
Jimo Jung14e87bf2018-09-03 16:28:13 +0900251 initVtap();
Jian Li614cb092018-07-03 22:41:42 +0900252
Jimo Jung14e87bf2018-09-03 16:28:13 +0900253 log.info("Started");
Jian Li19f25262018-07-03 22:37:12 +0900254 }
255
Jian Li614cb092018-07-03 22:41:42 +0900256 @Deactivate
257 public void deactivate() {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900258 clearVtap();
Jian Lib1ca1a22018-07-06 13:31:39 +0900259
Jian Li614cb092018-07-03 22:41:42 +0900260 hostService.removeListener(hostListener);
Jimo Jung14e87bf2018-09-03 16:28:13 +0900261 osNodeService.removeListener(osNodeListener);
Jian Li614cb092018-07-03 22:41:42 +0900262 deviceService.removeListener(deviceListener);
Jian Li19f25262018-07-03 22:37:12 +0900263
Jian Li614cb092018-07-03 22:41:42 +0900264 eventDispatcher.removeSink(OpenstackVtapEvent.class);
265 store.unsetDelegate(delegate);
Jian Li19f25262018-07-03 22:37:12 +0900266
Jian Li614cb092018-07-03 22:41:42 +0900267 eventExecutor.shutdown();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900268
269 componentConfigService.unregisterProperties(getClass(), false);
Jian Li614cb092018-07-03 22:41:42 +0900270 leadershipService.withdraw(appId.name());
Jian Li19f25262018-07-03 22:37:12 +0900271
Jimo Jung14e87bf2018-09-03 16:28:13 +0900272 log.info("Stopped");
273 }
274
275 @Modified
276 protected void modified(ComponentContext context) {
277 Dictionary<?, ?> properties = context.getProperties();
278
279 boolean updatedTunnelNicira = Tools.isPropertyEnabled(properties, TUNNEL_NICIRA);
280 if (tunnelNicira != updatedTunnelNicira) {
281 if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
282 // Update the tunnel flow rule by reflecting the change.
283 osNodeService.completeNodes(COMPUTE)
284 .forEach(osNode -> applyVtapNetwork(getVtapNetwork(), osNode, false));
285 tunnelNicira = updatedTunnelNicira;
286 osNodeService.completeNodes(COMPUTE).stream()
287 .filter(osNode -> osNode.state() == COMPLETE)
288 .forEach(osNode -> applyVtapNetwork(getVtapNetwork(), osNode, true));
289 log.debug("Apply {} nicira extension for tunneling", tunnelNicira ? "enable" : "disable");
290 } else {
291 tunnelNicira = updatedTunnelNicira;
292 }
293 }
294
295 log.info("Modified");
296 }
297
298 /**
299 * Initializes the flow rules and group tables, tunneling interface for all completed compute nodes.
300 */
301 @Override
302 public void initVtap() {
303 if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
304 osNodeService.completeNodes(COMPUTE).stream()
305 .filter(osNode -> osNode.state() == COMPLETE)
306 .forEach(osNode -> initVtapForNode(osNode));
307 log.trace("{} flow rules, groups, tunnel interface are initialized", appId.name());
308 }
309 }
310
311 /**
312 * Clears the flow rules and group tables, tunneling interface for all compute nodes.
313 */
314 @Override
315 public void clearVtap() {
316 if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
317 osNodeService.completeNodes(COMPUTE).stream()
318 .forEach(osNode -> clearVtapForNode(osNode));
319 log.trace("{} flow rules, groups, tunnel interface are cleared", appId.name());
320 }
321 }
322
323 /**
324 * Purges all flow rules and group tables, tunneling interface for openstack vtap.
325 */
326 @Override
327 public void purgeVtap() {
328 // Remove all flow rules
329 flowRuleService.removeFlowRulesById(appId);
330
331 // Remove all groups and tunnel interfaces
332 osNodeService.completeNodes(COMPUTE).stream()
333 .filter(osNode -> osNode.state() == COMPLETE)
334 .forEach(osNode -> {
335 groupService.getGroups(osNode.intgBridge(), appId)
336 .forEach(group ->
337 groupService.removeGroup(osNode.intgBridge(), group.appCookie(), appId));
338
339 OpenstackVtapNetwork vtapNetwork = getVtapNetwork();
340 setTunnelInterface(osNode, vtapNetwork, false);
341 });
342
343 log.trace("{} all flow rules, groups, tunnel interface are purged", appId.name());
344 }
345
346 private void initVtapForNode(OpenstackNode osNode) {
347 // Make base vtap network
348 initVtapNetwork(osNode);
349
350 // Make vtap connections by OpenstackVtap config
351 getVtapsByDeviceId(osNode.intgBridge())
352 .forEach(vtap -> applyVtap(vtap, osNode, true));
353
354 // Make vtap networks by OpenstackVtapNetwork config
355 applyVtapNetwork(getVtapNetwork(), osNode, true);
356 }
357
358 private void clearVtapForNode(OpenstackNode osNode) {
359 // Clear vtap networks by OpenstackVtapNetwork config
360 applyVtapNetwork(getVtapNetwork(), osNode, false);
361
362 // Clear vtap connections by OpenstackVtap config
363 getVtapsByDeviceId(osNode.intgBridge())
364 .forEach(vtap -> applyVtap(vtap, osNode, false));
365
366 // Clear base vtap network
367 clearVtapNetwork(osNode);
368 }
369
370 /**
371 * Initializes vtap pipeline of the given device.
372 *
373 * @param osNode device identifier
374 */
375 private void initVtapNetwork(OpenstackNode osNode) {
376 // Create default output tables
377 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
378 setOutputTableForDrop(osNode.intgBridge(),
379 VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX], true);
380 }
381
382 // Create group tables
383 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
384 createGroupTable(osNode.intgBridge(),
385 VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX],
386 ImmutableList.of(VTAP_TABLES[idx][VTAP_TABLE_NEXT_IDX],
387 VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX]),
388 null);
389 }
390 }
391
392 /**
393 * Clear vtap pipeline of the given device.
394 *
395 * @param osNode device identifier
396 */
397 private void clearVtapNetwork(OpenstackNode osNode) {
398 // Clear group tables
399 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
400 removeGroupTable(osNode.intgBridge(),
401 VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX]);
402 }
403
404 // Clear default output tables
405 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
406 setOutputTableForDrop(osNode.intgBridge(),
407 VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX], false);
408 }
409 }
410
411 @Override
412 public OpenstackVtapNetwork getVtapNetwork() {
413 return store.getVtapNetwork(VTAP_NETWORK_KEY);
414 }
415
416 @Override
417 public OpenstackVtapNetwork createVtapNetwork(Mode mode, Integer networkId, IpAddress serverIp) {
418 checkNotNull(mode, VTAP_DESC_NULL, "mode");
419 checkNotNull(serverIp, VTAP_DESC_NULL, "serverIp");
420 DefaultOpenstackVtapNetwork vtapNetwork = DefaultOpenstackVtapNetwork.builder()
421 .mode(mode)
422 .networkId(networkId)
423 .serverIp(serverIp)
424 .build();
425 return store.createVtapNetwork(VTAP_NETWORK_KEY, vtapNetwork);
426 }
427
428 @Override
429 public OpenstackVtapNetwork updateVtapNetwork(OpenstackVtapNetwork description) {
430 checkNotNull(description, VTAP_DESC_NULL, "vtapNetwork");
431 return store.updateVtapNetwork(VTAP_NETWORK_KEY, description);
432 }
433
434 @Override
435 public OpenstackVtapNetwork removeVtapNetwork() {
436 return store.removeVtapNetwork(VTAP_NETWORK_KEY);
437 }
438
439 @Override
440 public Set<DeviceId> getVtapNetworkDevices() {
441 return store.getVtapNetworkDevices(VTAP_NETWORK_KEY);
Jian Li19f25262018-07-03 22:37:12 +0900442 }
443
444 @Override
445 public int getVtapCount(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900446 return store.getVtapCount(type);
Jian Li38e4d942018-07-03 22:19:16 +0900447 }
448
449 @Override
Jian Li19f25262018-07-03 22:37:12 +0900450 public Set<OpenstackVtap> getVtaps(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900451 return store.getVtaps(type);
Jian Li38e4d942018-07-03 22:19:16 +0900452 }
453
454 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900455 public OpenstackVtap getVtap(OpenstackVtapId vtapId) {
456 return store.getVtap(vtapId);
Jian Li38e4d942018-07-03 22:19:16 +0900457 }
458
459 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900460 public Set<OpenstackVtap> getVtapsByDeviceId(DeviceId deviceId) {
461 return store.getVtapsByDeviceId(deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900462 }
463
Jian Li614cb092018-07-03 22:41:42 +0900464 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900465 public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vtapCriterion) {
466 checkNotNull(type, VTAP_DESC_NULL, "type");
467 checkNotNull(vtapCriterion, VTAP_DESC_NULL, "vtapCriterion");
Jian Li614cb092018-07-03 22:41:42 +0900468
469 Set<DeviceId> txDevices = type.isValid(Type.VTAP_TX) ?
Jimo Jung14e87bf2018-09-03 16:28:13 +0900470 getEdgeDevice(Type.VTAP_TX, vtapCriterion) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900471 Set<DeviceId> rxDevices = type.isValid(Type.VTAP_RX) ?
Jimo Jung14e87bf2018-09-03 16:28:13 +0900472 getEdgeDevice(Type.VTAP_RX, vtapCriterion) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900473
Jimo Jung14e87bf2018-09-03 16:28:13 +0900474 DefaultOpenstackVtap description = DefaultOpenstackVtap.builder()
475 .id(OpenstackVtapId.vtapId())
476 .type(type)
477 .vtapCriterion(vtapCriterion)
478 .txDeviceIds(txDevices)
479 .rxDeviceIds(rxDevices)
480 .build();
481 return store.createVtap(description);
Jian Li614cb092018-07-03 22:41:42 +0900482 }
483
484 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900485 public OpenstackVtap updateVtap(OpenstackVtap description) {
486 checkNotNull(description, VTAP_DESC_NULL, "vtap");
Jian Li614cb092018-07-03 22:41:42 +0900487
Jimo Jung14e87bf2018-09-03 16:28:13 +0900488 Set<DeviceId> txDevices = description.type().isValid(Type.VTAP_TX) ?
489 getEdgeDevice(Type.VTAP_TX, description.vtapCriterion()) : ImmutableSet.of();
490 Set<DeviceId> rxDevices = description.type().isValid(Type.VTAP_RX) ?
491 getEdgeDevice(Type.VTAP_RX, description.vtapCriterion()) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900492
Jimo Jung14e87bf2018-09-03 16:28:13 +0900493 DefaultOpenstackVtap vtap = DefaultOpenstackVtap.builder(description)
494 .txDeviceIds(txDevices)
495 .rxDeviceIds(rxDevices)
496 .build();
497 return store.updateVtap(vtap, true);
Jian Li614cb092018-07-03 22:41:42 +0900498 }
499
500 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900501 public OpenstackVtap removeVtap(OpenstackVtapId vtapId) {
502 return store.removeVtap(vtapId);
Jian Li614cb092018-07-03 22:41:42 +0900503 }
504
Jian Li26ef1302018-07-04 14:37:06 +0900505 /**
506 * Obtains the identifier set of edge device where the targeted host is located.
507 * Note that, in most of cases target host is attached to one device,
508 * however, in some cases, the host can be attached to multiple devices.
509 *
Jimo Jung14e87bf2018-09-03 16:28:13 +0900510 * @param type vtap type
511 * @param criterion vtap criterion
Jian Li26ef1302018-07-04 14:37:06 +0900512 * @return a collection of device identifiers
513 */
514 private Set<DeviceId> getEdgeDevice(Type type, OpenstackVtapCriterion criterion) {
Jimo Jung1bf54352018-11-08 14:16:08 +0900515 if (hostService.getHosts() == null) {
516 return ImmutableSet.of();
517 }
518
Jian Li26ef1302018-07-04 14:37:06 +0900519 Set<DeviceId> deviceIds = Sets.newConcurrentHashSet();
520 StreamSupport.stream(hostService.getHosts().spliterator(), true)
Jimo Jung14e87bf2018-09-03 16:28:13 +0900521 .filter(host -> isValidHost(host) &&
522 host.ipAddresses().stream().anyMatch(ip -> containsIp(type, criterion, ip)))
523 .forEach(host -> {
524 Set<DeviceId> hostDeviceIds =
525 host.locations().stream()
526 .map(HostLocation::deviceId)
527 .filter(deviceId -> Objects.nonNull(osNodeService.node(deviceId)))
528 .collect(Collectors.toSet());
529 deviceIds.addAll(hostDeviceIds);
530 });
Jian Li26ef1302018-07-04 14:37:06 +0900531 return deviceIds;
Jian Li614cb092018-07-03 22:41:42 +0900532 }
533
Jian Li26ef1302018-07-04 14:37:06 +0900534 /**
Jimo Jung14e87bf2018-09-03 16:28:13 +0900535 * Updates device list of vtaps with respect to the host changes.
Jian Li26ef1302018-07-04 14:37:06 +0900536 *
537 * @param newHost new host instance
538 * @param oldHost old host instance
539 */
Jimo Jung14e87bf2018-09-03 16:28:13 +0900540 private void updateHostbyType(Type type, Host newHost, Host oldHost) {
541 getVtaps(type).forEach(vtap -> {
542 IpPrefix prefix = (type == Type.VTAP_TX) ?
543 vtap.vtapCriterion().srcIpPrefix() :
544 vtap.vtapCriterion().dstIpPrefix();
545
546 int hostDiff = hostCompareIp(newHost, oldHost, prefix);
547 if (hostDiff < 0) {
548 oldHost.locations().stream()
549 .map(HostLocation::deviceId)
550 .forEach(deviceId ->
551 store.removeDeviceFromVtap(vtap.id(), type, deviceId));
552 } else if (hostDiff > 0) {
553 newHost.locations().stream()
554 .map(HostLocation::deviceId)
555 .filter(deviceId -> Objects.nonNull(osNodeService.node(deviceId)))
556 .forEach(deviceId ->
557 store.addDeviceToVtap(vtap.id(), type, deviceId));
558 }
559 });
560 }
561
Jian Li614cb092018-07-03 22:41:42 +0900562 private void updateHost(Host newHost, Host oldHost) {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900563 // update devices for vtap tx
564 updateHostbyType(Type.VTAP_TX, newHost, oldHost);
Jian Li26ef1302018-07-04 14:37:06 +0900565
Jimo Jung14e87bf2018-09-03 16:28:13 +0900566 // update devices for vtap rx
567 updateHostbyType(Type.VTAP_RX, newHost, oldHost);
Jian Li614cb092018-07-03 22:41:42 +0900568 }
569
Jimo Jung14e87bf2018-09-03 16:28:13 +0900570 private void applyFlowRule(FlowRule flowRule, boolean install) {
571 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
Jian Li614cb092018-07-03 22:41:42 +0900572
Jimo Jung14e87bf2018-09-03 16:28:13 +0900573 if (install) {
574 flowOpsBuilder.add(flowRule);
575 } else {
576 flowOpsBuilder.remove(flowRule);
Jian Li26ef1302018-07-04 14:37:06 +0900577 }
578
Jimo Jung14e87bf2018-09-03 16:28:13 +0900579 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
580 @Override
581 public void onSuccess(FlowRuleOperations ops) {
582 log.debug("Installed flow rules for vtap");
583 }
Jian Li26ef1302018-07-04 14:37:06 +0900584
Jimo Jung14e87bf2018-09-03 16:28:13 +0900585 @Override
586 public void onError(FlowRuleOperations ops) {
587 log.warn("Failed to install flow rules for vtap");
588 }
589 }));
Jian Li614cb092018-07-03 22:41:42 +0900590 }
591
Jimo Jung14e87bf2018-09-03 16:28:13 +0900592 private void connectTables(DeviceId deviceId,
593 int fromTable,
594 int toTableOrGroup, boolean isGroup,
595 OpenstackVtapCriterion vtapCriterion,
596 int rulePriority, boolean install) {
597 log.debug("Table Transition: table[{}] -> table/group[{}]", fromTable, toTableOrGroup);
Jian Li614cb092018-07-03 22:41:42 +0900598
599 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Jian Li311a9c92018-07-09 16:48:36 +0900600 .matchEthType(TYPE_IPV4);
Jian Li614cb092018-07-03 22:41:42 +0900601
Jian Li311a9c92018-07-09 16:48:36 +0900602 // if the IpPrefix is "0.0.0.0/0", we do not include such a match into the flow rule
Jimo Jung14e87bf2018-09-03 16:28:13 +0900603 if (!vtapCriterion.srcIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
604 selectorBuilder.matchIPSrc(vtapCriterion.srcIpPrefix());
Jian Li311a9c92018-07-09 16:48:36 +0900605 }
606
Jimo Jung14e87bf2018-09-03 16:28:13 +0900607 if (!vtapCriterion.dstIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
608 selectorBuilder.matchIPDst(vtapCriterion.dstIpPrefix());
Jian Li311a9c92018-07-09 16:48:36 +0900609 }
610
Jimo Jung14e87bf2018-09-03 16:28:13 +0900611 switch (vtapCriterion.ipProtocol()) {
Jian Li614cb092018-07-03 22:41:42 +0900612 case PROTOCOL_TCP:
Jimo Jung14e87bf2018-09-03 16:28:13 +0900613 selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900614
615 // Add port match only if the port number is greater than zero
Jimo Jung14e87bf2018-09-03 16:28:13 +0900616 if (vtapCriterion.srcTpPort().toInt() > 0) {
617 selectorBuilder.matchTcpSrc(vtapCriterion.srcTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900618 }
Jimo Jung14e87bf2018-09-03 16:28:13 +0900619 if (vtapCriterion.dstTpPort().toInt() > 0) {
620 selectorBuilder.matchTcpDst(vtapCriterion.dstTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900621 }
622 break;
623 case PROTOCOL_UDP:
Jimo Jung14e87bf2018-09-03 16:28:13 +0900624 selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900625
626 // Add port match only if the port number is greater than zero
Jimo Jung14e87bf2018-09-03 16:28:13 +0900627 if (vtapCriterion.srcTpPort().toInt() > 0) {
628 selectorBuilder.matchUdpSrc(vtapCriterion.srcTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900629 }
Jimo Jung14e87bf2018-09-03 16:28:13 +0900630 if (vtapCriterion.dstTpPort().toInt() > 0) {
631 selectorBuilder.matchUdpDst(vtapCriterion.dstTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900632 }
633 break;
634 case PROTOCOL_ICMP:
Jimo Jung14e87bf2018-09-03 16:28:13 +0900635 selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol());
Jian Li614cb092018-07-03 22:41:42 +0900636 break;
637 default:
638 break;
639 }
640
641 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900642 if (isGroup) {
643 treatmentBuilder.group(GroupId.valueOf(toTableOrGroup));
Jian Li614cb092018-07-03 22:41:42 +0900644 } else {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900645 treatmentBuilder.transition(toTableOrGroup);
Jian Li614cb092018-07-03 22:41:42 +0900646 }
647
648 FlowRule flowRule = DefaultFlowRule.builder()
649 .forDevice(deviceId)
650 .withSelector(selectorBuilder.build())
651 .withTreatment(treatmentBuilder.build())
652 .withPriority(rulePriority)
653 .fromApp(appId)
654 .makePermanent()
655 .forTable(fromTable)
656 .build();
657
658 applyFlowRule(flowRule, install);
659 }
660
Jimo Jung14e87bf2018-09-03 16:28:13 +0900661 /**
662 * Creates/Removes a tunnel interface in a given openstack node by vtap network information.
663 *
664 * @param osNode openstack node
665 * @param vtapNetwork openstack vtap network for making
666 *
667 */
668 private boolean setTunnelInterface(OpenstackNode osNode,
669 OpenstackVtapNetwork vtapNetwork,
670 boolean install) {
671 String tunnelName = getTunnelName(vtapNetwork.mode());
672 if (tunnelName == null) {
673 return false;
Jian Li614cb092018-07-03 22:41:42 +0900674 }
Jimo Jung14e87bf2018-09-03 16:28:13 +0900675
676 if (!deviceService.isAvailable(osNode.ovsdb())) {
677 log.warn("Not available osNode {} ovs {}", osNode.hostname(), osNode.ovsdb());
678 return false;
679 }
680
681 if (install == isInterfaceEnabled(osNode.intgBridge(), tunnelName)) {
682 log.warn("Already {} {} interface on osNode ovs {}, bridge {}",
683 install ? "add" : "remove",
684 tunnelName, osNode.ovsdb(), osNode.intgBridge());
685 return true;
686 }
687
688 Device device = deviceService.getDevice(osNode.ovsdb());
689 if (device == null || !device.is(InterfaceConfig.class)) {
690 log.warn("Not able to get InterfaceConfig on osNode ovs {}", osNode.ovsdb());
691 return false;
692 }
693
694 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
695 if (install) {
696 TunnelDescription.Builder tunnelDesc = DefaultTunnelDescription.builder()
697 .deviceId(INTEGRATION_BRIDGE)
698 .ifaceName(tunnelName)
699 .type(getTunnelType(vtapNetwork.mode()))
700 .key((vtapNetwork.networkId() == 0) ? null : new TunnelKey<>(vtapNetwork.networkId()))
701 .remote(TunnelEndPoints.ipTunnelEndpoint(vtapNetwork.serverIp()));
702 if (!ifaceConfig.addTunnelMode(tunnelName, tunnelDesc.build())) {
703 log.error("Fail to create {} interface on osNode ovs {}", tunnelName, osNode.ovsdb());
704 return false;
705 }
706 } else {
707 if (!ifaceConfig.removeTunnelMode(tunnelName)) {
708 log.error("Fail to remove {} interface on osNode ovs {}", tunnelName, osNode.ovsdb());
709 return false;
710 }
711 }
712
713 // Wait for tunnel interface create/remove complete
714 synchronized (syncInterface) {
715 for (int i = 0; i < INTERFACE_MANIPULATION_RETRY; i++) {
716 try {
717 syncInterface.wait(INTERFACE_MANIPULATION_TIMEOUT);
718 if (install == isInterfaceEnabled(osNode.intgBridge(), tunnelName)) {
719 log.debug("Success to {} {} interface on osNode ovs {}, bridge {}",
720 install ? "add" : "remove",
721 tunnelName, osNode.ovsdb(), osNode.intgBridge());
722 return true;
723 }
724 } catch (InterruptedException e) {
725 break;
726 }
727 }
728 }
729 log.warn("Fail to {} {} interface on osNode ovs {}, bridge {}",
730 install ? "add" : "remove",
731 tunnelName, osNode.ovsdb(), osNode.intgBridge());
732 return false;
733 }
734
735 /**
736 * Checks whether a given network interface in a given openstack node is enabled or not.
737 *
738 * @param deviceId openstack node
739 * @param interfaceName network interface name
740 * @return true if the given interface is enabled, false otherwise
741 */
742 private boolean isInterfaceEnabled(DeviceId deviceId, String interfaceName) {
743 return deviceService.isAvailable(deviceId) &&
744 deviceService.getPorts(deviceId).parallelStream().anyMatch(port ->
745 Objects.equals(port.annotations().value(PORT_NAME), interfaceName) && port.isEnabled());
746 }
747
748 private PortNumber portNumber(DeviceId deviceId, String interfaceName) {
749 Port port = deviceService.getPorts(deviceId).stream()
750 .filter(p -> p.isEnabled() &&
751 Objects.equals(p.annotations().value(PORT_NAME), interfaceName))
752 .findAny().orElse(null);
753 return port != null ? port.number() : null;
754 }
755
756 private void setOutputTableForTunnel(DeviceId deviceId, int tableId,
757 PortNumber outPort, IpAddress serverIp,
758 boolean install) {
759 log.debug("setOutputTableForTunnel[{}]: deviceId={}, tableId={}, outPort={}, serverIp={}",
760 install ? "add" : "remove", deviceId, tableId, outPort, serverIp);
761
762 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
763 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
764 .setOutput(outPort);
765
766 if (tunnelNicira) {
767 ExtensionTreatment extensionTreatment = buildTunnelExtension(deviceId, serverIp);
768 if (extensionTreatment == null) {
769 return;
770 }
771 treatment.extension(extensionTreatment, deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900772 }
773
774 FlowRule flowRule = DefaultFlowRule.builder()
775 .forDevice(deviceId)
776 .withSelector(selector.build())
777 .withTreatment(treatment.build())
Jimo Jung14e87bf2018-09-03 16:28:13 +0900778 .withPriority(PRIORITY_VTAP_OUTPUT_RULE)
Jian Li614cb092018-07-03 22:41:42 +0900779 .makePermanent()
780 .forTable(tableId)
781 .fromApp(appId)
782 .build();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900783
784 log.debug("setOutputTableForTunnel flowRule={}, install={}", flowRule, install);
785 applyFlowRule(flowRule, install);
Jian Li614cb092018-07-03 22:41:42 +0900786 }
787
Jimo Jung14e87bf2018-09-03 16:28:13 +0900788 private void setOutputTableForDrop(DeviceId deviceId, int tableId,
789 boolean install) {
790 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
791 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
Jian Li614cb092018-07-03 22:41:42 +0900792
Jimo Jung14e87bf2018-09-03 16:28:13 +0900793 FlowRule flowRule = DefaultFlowRule.builder()
794 .forDevice(deviceId)
795 .withSelector(selector.build())
796 .withTreatment(treatment.build())
797 .withPriority(PRIORITY_VTAP_OUTPUT_DROP)
798 .makePermanent()
799 .forTable(tableId)
800 .fromApp(appId)
801 .build();
802 applyFlowRule(flowRule, install);
803 }
Jian Li614cb092018-07-03 22:41:42 +0900804
Jimo Jung14e87bf2018-09-03 16:28:13 +0900805 private void setOutputTable(DeviceId deviceId, Mode mode,
806 IpAddress serverIp, boolean install) {
807 log.debug("setOutputTable[{}]: deviceId={}, mode={}, serverIp={}",
808 install ? "add" : "remove", deviceId, mode, serverIp);
809
810 if (deviceId == null) {
811 return;
Jian Li614cb092018-07-03 22:41:42 +0900812 }
813
Jimo Jung14e87bf2018-09-03 16:28:13 +0900814 switch (mode) {
815 case GRE:
816 case VXLAN:
817 String tunnelName = getTunnelName(mode);
818 PortNumber vtapPort = portNumber(deviceId, tunnelName);
819 if (vtapPort != null) {
820 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
821 setOutputTableForTunnel(deviceId, VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX],
822 vtapPort, serverIp, install);
823 }
824 } else {
825 log.warn("Vtap tunnel port {} doesn't exist", tunnelName);
826 }
827 break;
828 default:
829 log.warn("Invalid vtap network mode {}", mode);
830 break;
831 }
832 }
833
834 /**
835 * Returns tunnel destination extension treatment object.
836 *
837 * @param deviceId device id to apply this treatment
838 * @param remoteIp tunnel destination ip address
839 * @return extension treatment
840 */
841 private ExtensionTreatment buildTunnelExtension(DeviceId deviceId, IpAddress remoteIp) {
842 Device device = deviceService.getDevice(deviceId);
843 if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
844 log.warn("Nicira extension treatment is not supported");
845 return null;
846 }
847
848 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
849 ExtensionTreatment treatment =
850 resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
851 try {
852 treatment.setPropertyValue(TUNNEL_DST_EXTENSION, remoteIp.getIp4Address());
853 return treatment;
854 } catch (ExtensionPropertyException e) {
855 log.error("Failed to set nicira tunnelDst extension treatment for {}", deviceId);
856 return null;
857 }
858 }
859
860 private ExtensionTreatment buildResubmitExtension(DeviceId deviceId, int tableId) {
861 Device device = deviceService.getDevice(deviceId);
862 if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
863 log.warn("Nicira extension treatment is not supported");
864 return null;
865 }
866
867 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
868 ExtensionTreatment treatment =
869 resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type());
870
871 try {
872 treatment.setPropertyValue(TABLE_EXTENSION, ((short) tableId));
873 return treatment;
874 } catch (ExtensionPropertyException e) {
875 log.error("Failed to set nicira resubmit extension treatment for {}", deviceId);
876 return null;
877 }
Jian Li614cb092018-07-03 22:41:42 +0900878 }
879
880 private void createGroupTable(DeviceId deviceId, int groupId,
881 List<Integer> tableIds, List<PortNumber> ports) {
882 List<GroupBucket> buckets = Lists.newArrayList();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900883 if (tableIds != null) {
884 tableIds.forEach(tableId -> {
885 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
886 .extension(buildResubmitExtension(deviceId, tableId), deviceId);
887 GroupBucket bucket = DefaultGroupBucket
888 .createAllGroupBucket(treatment.build());
889 buckets.add(bucket);
890 });
891 }
892 if (ports != null) {
893 ports.forEach(port -> {
894 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
895 .setOutput(port);
896 GroupBucket bucket = DefaultGroupBucket
897 .createAllGroupBucket(treatment.build());
898 buckets.add(bucket);
899 });
900 }
Jian Li614cb092018-07-03 22:41:42 +0900901
902 GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
903 GroupDescription.Type.ALL,
904 new GroupBuckets(buckets),
905 getGroupKey(groupId),
906 groupId,
907 appId);
908 groupService.addGroup(groupDescription);
909 }
910
Jimo Jung14e87bf2018-09-03 16:28:13 +0900911 private void removeGroupTable(DeviceId deviceId, int groupId) {
912 groupService.removeGroup(deviceId, getGroupKey(groupId), appId);
Jian Li26ef1302018-07-04 14:37:06 +0900913 }
914
Jimo Jung14e87bf2018-09-03 16:28:13 +0900915 /**
916 * Internal listener for device events.
917 */
Jian Li26ef1302018-07-04 14:37:06 +0900918 private class InternalDeviceListener implements DeviceListener {
Jian Li26ef1302018-07-04 14:37:06 +0900919
920 @Override
921 public void event(DeviceEvent event) {
922 DeviceEvent.Type type = event.type();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900923 Device device = event.subject();
Jian Li26ef1302018-07-04 14:37:06 +0900924
925 switch (type) {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900926 case PORT_ADDED:
927 case PORT_UPDATED:
928 case PORT_REMOVED:
929 String portName = event.port().annotations().value(PORT_NAME);
930 if (portName.equals(getTunnelName(Mode.GRE)) ||
931 portName.equals(getTunnelName(Mode.VXLAN))) {
932 log.trace("InternalDeviceListener type={}, host={}", type, device);
933 synchronized (syncInterface) {
934 try {
935 syncInterface.notifyAll();
936 } catch (IllegalMonitorStateException e) {
937 log.warn("Already syncInterface exited");
938 }
939 }
940 }
Jian Li26ef1302018-07-04 14:37:06 +0900941 break;
942 default:
943 break;
944 }
945 }
946 }
947
Jimo Jung14e87bf2018-09-03 16:28:13 +0900948 /**
949 * Internal listener for openstack node events.
950 */
951 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
952
953 @Override
954 public boolean isRelevant(OpenstackNodeEvent event) {
955 // do not allow to proceed without leadership and compute node
956 NodeId leader = leadershipService.getLeader(appId.name());
957 OpenstackNode osNode = event.subject();
958
959 return Objects.equals(localNodeId, leader) && osNode.type() == COMPUTE;
960 }
961
962 @Override
963 public void event(OpenstackNodeEvent event) {
964 OpenstackNodeEvent.Type type = event.type();
965 OpenstackNode osNode = event.subject();
966 log.trace("InternalOpenstackNodeListener type={}, osNode={}", type, osNode);
967
968 eventExecutor.execute(() -> {
969 try {
970 switch (type) {
971 case OPENSTACK_NODE_COMPLETE:
972 initVtapForNode(osNode);
973 break;
974
975 case OPENSTACK_NODE_REMOVED:
976 clearVtapForNode(osNode);
977 break;
978
979 default:
980 break;
981 }
982 } catch (Exception e) {
983 dumpStackTrace(log, e);
984 }
985 });
986 }
987 }
988
989 /**
990 * Internal listener for host events.
991 */
Jian Li26ef1302018-07-04 14:37:06 +0900992 private class InternalHostListener implements HostListener {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900993
Jian Li26ef1302018-07-04 14:37:06 +0900994 @Override
995 public boolean isRelevant(HostEvent event) {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900996 Host host = event.subject();
997 if (!isValidHost(host)) {
998 log.debug("Invalid host detected, ignore it {}", host);
999 return false;
1000 }
1001
Jian Li26ef1302018-07-04 14:37:06 +09001002 // do not allow to proceed without leadership
1003 NodeId leader = leadershipService.getLeader(appId.name());
1004 return Objects.equals(localNodeId, leader);
1005 }
1006
1007 @Override
1008 public void event(HostEvent event) {
1009 HostEvent.Type type = event.type();
1010 Host host = event.subject();
Jimo Jung14e87bf2018-09-03 16:28:13 +09001011 Host prevHost = event.prevSubject();
1012 log.trace("InternalHostListener {}: {} -> {}", type, prevHost, host);
Jian Li26ef1302018-07-04 14:37:06 +09001013
Jimo Jung14e87bf2018-09-03 16:28:13 +09001014 eventExecutor.execute(() -> {
1015 try {
1016 switch (event.type()) {
1017 case HOST_ADDED:
1018 updateHost(host, null);
1019 break;
Jian Li26ef1302018-07-04 14:37:06 +09001020
Jimo Jung14e87bf2018-09-03 16:28:13 +09001021 case HOST_REMOVED:
1022 updateHost(null, host);
1023 break;
Jian Li26ef1302018-07-04 14:37:06 +09001024
Jimo Jung14e87bf2018-09-03 16:28:13 +09001025 case HOST_MOVED:
1026 case HOST_UPDATED:
1027 updateHost(host, prevHost);
1028 break;
Jian Li26ef1302018-07-04 14:37:06 +09001029
Jimo Jung14e87bf2018-09-03 16:28:13 +09001030 default:
1031 break;
1032 }
1033 } catch (Exception e) {
1034 dumpStackTrace(log, e);
1035 }
1036 });
Jian Li26ef1302018-07-04 14:37:06 +09001037 }
1038 }
1039
1040 // Store delegate to re-post events emitted from the store.
1041 private class InternalStoreDelegate implements OpenstackVtapStoreDelegate {
Jimo Jung14e87bf2018-09-03 16:28:13 +09001042
Jian Li26ef1302018-07-04 14:37:06 +09001043 @Override
1044 public void notify(OpenstackVtapEvent event) {
1045 OpenstackVtapEvent.Type type = event.type();
Jimo Jung14e87bf2018-09-03 16:28:13 +09001046 log.trace("InternalStoreDelegate {}: {} -> {}", type, event.prevSubject(), event.subject());
Jian Li26ef1302018-07-04 14:37:06 +09001047
Jimo Jung14e87bf2018-09-03 16:28:13 +09001048 if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
1049 eventExecutor.execute(() -> {
1050 try {
1051 switch (type) {
1052 case VTAP_NETWORK_ADDED:
1053 case VTAP_NETWORK_UPDATED:
1054 case VTAP_NETWORK_REMOVED:
1055 // Update network
1056 updateVtapNetwork(event.openstackVtapNetwork(),
1057 event.prevOpenstackVtapNetwork());
1058 break;
Jian Li26ef1302018-07-04 14:37:06 +09001059
Jimo Jung14e87bf2018-09-03 16:28:13 +09001060 case VTAP_ADDED:
1061 case VTAP_UPDATED:
1062 case VTAP_REMOVED:
1063 // Update vtap rule
1064 updateVtap(event.openstackVtap(),
1065 event.prevOpenstackVtap());
1066 break;
Jian Li26ef1302018-07-04 14:37:06 +09001067
Jimo Jung14e87bf2018-09-03 16:28:13 +09001068 default:
1069 break;
1070 }
1071 } catch (Exception e) {
1072 dumpStackTrace(log, e);
1073 }
1074 });
Jian Li26ef1302018-07-04 14:37:06 +09001075 }
1076 post(event);
1077 }
Jian Li38e4d942018-07-03 22:19:16 +09001078 }
Jimo Jung14e87bf2018-09-03 16:28:13 +09001079
1080 private void applyVtap(OpenstackVtap vtap,
1081 OpenstackNode osNode,
1082 boolean install) {
1083 if (vtap == null || osNode == null) {
1084 return;
1085 }
1086
1087 log.debug("applyVtap vtap={}, osNode={}, install={}", vtap, osNode, install);
1088
1089 DeviceId deviceId = osNode.intgBridge();
1090 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
1091 if ((idx == VTAP_TABLE_INBOUND_IDX &&
1092 vtap.type().isValid(Type.VTAP_TX) &&
1093 vtap.txDeviceIds().contains(deviceId)) ||
1094 (idx != VTAP_TABLE_INBOUND_IDX &&
1095 vtap.type().isValid(Type.VTAP_RX) &&
1096 vtap.rxDeviceIds().contains(deviceId))) {
1097 connectTables(deviceId,
1098 VTAP_TABLES[idx][VTAP_TABLE_INPUT_IDX],
1099 VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX],
1100 true,
1101 vtap.vtapCriterion(), PRIORITY_VTAP_RULE, install);
1102 }
1103 }
1104 }
1105
1106 private void updateVtap(OpenstackVtap vtap,
1107 OpenstackVtap prevVtap) {
1108 if (Objects.equals(vtap, prevVtap)) {
1109 return;
1110 }
1111
1112 Set<DeviceId> prevTxDeviceIds = (prevVtap != null ? prevVtap.txDeviceIds() : ImmutableSet.of());
1113 Set<DeviceId> txDeviceIds = (vtap != null ? vtap.txDeviceIds() : ImmutableSet.of());
1114 Set<DeviceId> prevRxDeviceIds = (prevVtap != null ? prevVtap.rxDeviceIds() : ImmutableSet.of());
1115 Set<DeviceId> rxDeviceIds = (vtap != null ? vtap.rxDeviceIds() : ImmutableSet.of());
1116
1117 // Remake all vtap rule
1118 if (prevVtap != null) {
1119 Set<DeviceId> deviceIds = Sets.newHashSet();
1120 deviceIds.addAll(Sets.difference(prevTxDeviceIds, txDeviceIds));
1121 deviceIds.addAll(Sets.difference(prevRxDeviceIds, rxDeviceIds));
1122 deviceIds.stream()
1123 .map(deviceId -> osNodeService.node(deviceId))
1124 .filter(osNode -> Objects.nonNull(osNode) &&
1125 osNode.type() == COMPUTE)
1126 .forEach(osNode -> applyVtap(prevVtap, osNode, false));
1127 }
1128 if (vtap != null) {
1129 Set<DeviceId> deviceIds = Sets.newHashSet();
1130 deviceIds.addAll(Sets.difference(txDeviceIds, prevTxDeviceIds));
1131 deviceIds.addAll(Sets.difference(rxDeviceIds, prevRxDeviceIds));
1132 deviceIds.stream()
1133 .map(deviceId -> osNodeService.node(deviceId))
1134 .filter(osNode -> Objects.nonNull(osNode) &&
1135 osNode.type() == COMPUTE && osNode.state() == COMPLETE)
1136 .forEach(osNode -> applyVtap(vtap, osNode, true));
1137 }
1138 }
1139
1140 // create/remove tunnel interface and output table
1141 private boolean applyVtapNetwork(OpenstackVtapNetwork vtapNetwork,
1142 OpenstackNode osNode,
1143 boolean install) {
1144 if (vtapNetwork == null || osNode == null) {
1145 return false;
1146 }
1147
1148 if (install) {
1149 if (setTunnelInterface(osNode, vtapNetwork, true)) {
1150 setOutputTable(osNode.intgBridge(), vtapNetwork.mode(), vtapNetwork.serverIp(), true);
1151 store.addDeviceToVtapNetwork(VTAP_NETWORK_KEY, osNode.intgBridge());
1152 return true;
1153 }
1154 } else {
1155 Set<DeviceId> deviceIds = getVtapNetworkDevices();
1156 if (deviceIds != null && deviceIds.contains(osNode.intgBridge())) {
1157 store.removeDeviceFromVtapNetwork(VTAP_NETWORK_KEY, osNode.intgBridge());
1158 setOutputTable(osNode.intgBridge(), vtapNetwork.mode(), vtapNetwork.serverIp(), false);
1159 setTunnelInterface(osNode, vtapNetwork, false);
1160 return true;
1161 }
1162 }
1163 return false;
1164 }
1165
1166 private void updateVtapNetwork(OpenstackVtapNetwork network,
1167 OpenstackVtapNetwork prevNetwork) {
1168 // Remake all output tables
1169 if (prevNetwork != null) {
1170 osNodeService.completeNodes(COMPUTE)
1171 .forEach(osNode -> applyVtapNetwork(prevNetwork, osNode, false));
1172 }
1173 if (network != null) {
1174 osNodeService.completeNodes(COMPUTE).stream()
1175 .filter(osNode -> osNode.state() == COMPLETE)
1176 .forEach(osNode -> applyVtapNetwork(network, osNode, true));
1177 }
1178 }
1179
Jian Li4f368e82018-07-02 14:22:22 +09001180}