blob: 6370de6acc36fac7dc013cadcb394b0c0c21586c [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 Li614cb092018-07-03 22:41:42 +090022import org.apache.felix.scr.annotations.Activate;
Jian Li19f25262018-07-03 22:37:12 +090023import org.apache.felix.scr.annotations.Component;
Jian Li614cb092018-07-03 22:41:42 +090024import org.apache.felix.scr.annotations.Deactivate;
Jimo Jung14e87bf2018-09-03 16:28:13 +090025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Jian Li614cb092018-07-03 22:41:42 +090027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
Jian Li19f25262018-07-03 22:37:12 +090029import org.apache.felix.scr.annotations.Service;
Jian Li26ef1302018-07-04 14:37:06 +090030import org.onlab.packet.IpAddress;
Jian Li614cb092018-07-03 22:41:42 +090031import org.onlab.packet.IpPrefix;
Jimo Jung14e87bf2018-09-03 16:28:13 +090032import org.onlab.util.Tools;
33import org.onosproject.cfg.ComponentConfigService;
Jian Li614cb092018-07-03 22:41:42 +090034import org.onosproject.cluster.ClusterService;
35import org.onosproject.cluster.LeadershipService;
36import org.onosproject.cluster.NodeId;
37import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
39import org.onosproject.core.GroupId;
Jian Li19f25262018-07-03 22:37:12 +090040import org.onosproject.event.AbstractListenerManager;
Jian Li26ef1302018-07-04 14:37:06 +090041import org.onosproject.net.Device;
Jian Li38e4d942018-07-03 22:19:16 +090042import org.onosproject.net.DeviceId;
Jian Li614cb092018-07-03 22:41:42 +090043import org.onosproject.net.Host;
44import org.onosproject.net.HostLocation;
Jimo Jung14e87bf2018-09-03 16:28:13 +090045import org.onosproject.net.Port;
Jian Li19f25262018-07-03 22:37:12 +090046import org.onosproject.net.PortNumber;
Jimo Jung14e87bf2018-09-03 16:28:13 +090047import org.onosproject.net.behaviour.DefaultTunnelDescription;
Jian Li614cb092018-07-03 22:41:42 +090048import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
Jimo Jung14e87bf2018-09-03 16:28:13 +090049import org.onosproject.net.behaviour.InterfaceConfig;
50import org.onosproject.net.behaviour.TunnelDescription;
51import org.onosproject.net.behaviour.TunnelEndPoints;
52import org.onosproject.net.behaviour.TunnelKey;
Jian Li614cb092018-07-03 22:41:42 +090053import org.onosproject.net.device.DeviceEvent;
54import org.onosproject.net.device.DeviceListener;
55import org.onosproject.net.device.DeviceService;
Jian Li614cb092018-07-03 22:41:42 +090056import org.onosproject.net.flow.DefaultFlowRule;
57import org.onosproject.net.flow.DefaultTrafficSelector;
58import org.onosproject.net.flow.DefaultTrafficTreatment;
59import org.onosproject.net.flow.FlowRule;
60import org.onosproject.net.flow.FlowRuleOperations;
61import org.onosproject.net.flow.FlowRuleOperationsContext;
62import org.onosproject.net.flow.FlowRuleService;
63import org.onosproject.net.flow.TrafficSelector;
64import org.onosproject.net.flow.TrafficTreatment;
Jimo Jung14e87bf2018-09-03 16:28:13 +090065import org.onosproject.net.flow.instructions.ExtensionPropertyException;
Jian Li614cb092018-07-03 22:41:42 +090066import org.onosproject.net.flow.instructions.ExtensionTreatment;
Jian Li614cb092018-07-03 22:41:42 +090067import org.onosproject.net.group.DefaultGroupBucket;
68import org.onosproject.net.group.DefaultGroupDescription;
Jian Li614cb092018-07-03 22:41:42 +090069import org.onosproject.net.group.GroupBucket;
70import org.onosproject.net.group.GroupBuckets;
71import org.onosproject.net.group.GroupDescription;
Jian Li614cb092018-07-03 22:41:42 +090072import org.onosproject.net.group.GroupService;
73import org.onosproject.net.host.HostEvent;
74import org.onosproject.net.host.HostListener;
75import org.onosproject.net.host.HostService;
Jimo Jung14e87bf2018-09-03 16:28:13 +090076import org.onosproject.openstacknode.api.OpenstackNode;
Jian Li26ef1302018-07-04 14:37:06 +090077import org.onosproject.openstacknode.api.OpenstackNodeEvent;
78import org.onosproject.openstacknode.api.OpenstackNodeListener;
79import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li38e4d942018-07-03 22:19:16 +090080import org.onosproject.openstackvtap.api.OpenstackVtap;
Jian Li26ef1302018-07-04 14:37:06 +090081import org.onosproject.openstackvtap.api.OpenstackVtap.Type;
Jian Li19f25262018-07-03 22:37:12 +090082import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
83import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
84import org.onosproject.openstackvtap.api.OpenstackVtapEvent;
Jian Li26ef1302018-07-04 14:37:06 +090085import org.onosproject.openstackvtap.api.OpenstackVtapId;
Jian Li38e4d942018-07-03 22:19:16 +090086import org.onosproject.openstackvtap.api.OpenstackVtapListener;
Jimo Jung14e87bf2018-09-03 16:28:13 +090087import org.onosproject.openstackvtap.api.OpenstackVtapNetwork;
88import org.onosproject.openstackvtap.api.OpenstackVtapNetwork.Mode;
Jian Li4f368e82018-07-02 14:22:22 +090089import org.onosproject.openstackvtap.api.OpenstackVtapService;
Jian Li614cb092018-07-03 22:41:42 +090090import org.onosproject.openstackvtap.api.OpenstackVtapStore;
91import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate;
Jian Li614cb092018-07-03 22:41:42 +090092import org.osgi.service.component.ComponentContext;
93import org.slf4j.Logger;
Jian Li4f368e82018-07-02 14:22:22 +090094
Jimo Jung14e87bf2018-09-03 16:28:13 +090095import java.util.Dictionary;
Jian Li614cb092018-07-03 22:41:42 +090096import java.util.List;
97import java.util.Objects;
Jian Li38e4d942018-07-03 22:19:16 +090098import java.util.Set;
Jian Li614cb092018-07-03 22:41:42 +090099import java.util.concurrent.ScheduledExecutorService;
Jian Li614cb092018-07-03 22:41:42 +0900100import java.util.stream.Collectors;
101import java.util.stream.StreamSupport;
102
103import static com.google.common.base.Preconditions.checkNotNull;
104import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
105import static org.onlab.packet.Ethernet.TYPE_IPV4;
106import static org.onlab.packet.IPv4.PROTOCOL_ICMP;
107import static org.onlab.packet.IPv4.PROTOCOL_TCP;
108import static org.onlab.packet.IPv4.PROTOCOL_UDP;
109import static org.onlab.util.Tools.groupedThreads;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900110import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Jian Li26ef1302018-07-04 14:37:06 +0900111import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900112import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
Jian Li614cb092018-07-03 22:41:42 +0900113import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
114import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE;
115import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
116import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_GROUP_TABLE;
117import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_MIRROR_TABLE;
118import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_TABLE;
119import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_GROUP_TABLE;
120import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_MIRROR_TABLE;
121import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_TABLE;
122import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_GROUP_TABLE;
123import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_MIRROR_TABLE;
124import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_TABLE;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900125import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
126import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
Jian Li26ef1302018-07-04 14:37:06 +0900127import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
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 */
Jian Li19f25262018-07-03 22:37:12 +0900140@Component(immediate = true)
141@Service
142public class OpenstackVtapManager
143 extends AbstractListenerManager<OpenstackVtapEvent, OpenstackVtapListener>
144 implements OpenstackVtapService, OpenstackVtapAdminService {
Jian Li4f368e82018-07-02 14:22:22 +0900145
Jian Li614cb092018-07-03 22:41:42 +0900146 private final Logger log = getLogger(getClass());
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
149 protected CoreService coreService;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 protected ClusterService clusterService;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
155 protected LeadershipService leadershipService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li614cb092018-07-03 22:41:42 +0900158 protected FlowRuleService flowRuleService;
159
160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected GroupService groupService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected DeviceService deviceService;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jimo Jung14e87bf2018-09-03 16:28:13 +0900167 protected OpenstackNodeService osNodeService;
168
169 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li614cb092018-07-03 22:41:42 +0900170 protected HostService hostService;
171
172 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
173 protected OpenstackVtapStore store;
174
Jian Li26ef1302018-07-04 14:37:06 +0900175 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jimo Jung14e87bf2018-09-03 16:28:13 +0900176 protected ComponentConfigService componentConfigService;
177
178 private static final boolean DEFAULT_TUNNEL_NICRA = false;
179 @Property(name = TUNNEL_NICIRA, boolValue = DEFAULT_TUNNEL_NICRA,
180 label = "Use nicra extension for tunneling")
181 private boolean tunnelNicira = DEFAULT_TUNNEL_NICRA;
Jian Li26ef1302018-07-04 14:37:06 +0900182
Jian Li614cb092018-07-03 22:41:42 +0900183 public static final String APP_ID = "org.onosproject.openstackvtap";
Jimo Jung14e87bf2018-09-03 16:28:13 +0900184 public static final String VTAP_DESC_NULL = "vtap field %s cannot be null";
Jian Li614cb092018-07-03 22:41:42 +0900185
186 private static final int PRIORITY_VTAP_RULE = 50000;
Jimo Jung14e87bf2018-09-03 16:28:13 +0900187 private static final int PRIORITY_VTAP_OUTPUT_RULE = 1000;
188 private static final int PRIORITY_VTAP_OUTPUT_DROP = 0;
Jian Li614cb092018-07-03 22:41:42 +0900189
Jian Li614cb092018-07-03 22:41:42 +0900190 private static final int INBOUND_NEXT_TABLE = DHCP_ARP_TABLE;
191 private static final int FLAT_OUTBOUND_NEXT_TABLE = FLAT_TABLE;
192 private static final int OUTBOUND_NEXT_TABLE = FORWARDING_TABLE;
193
Jimo Jung14e87bf2018-09-03 16:28:13 +0900194 private static final int[][] VTAP_TABLES = {
195 {VTAP_INBOUND_TABLE, VTAP_INBOUND_GROUP_TABLE,
196 INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE},
197 {VTAP_FLAT_OUTBOUND_TABLE, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
198 FLAT_OUTBOUND_NEXT_TABLE, VTAP_FLAT_OUTBOUND_MIRROR_TABLE},
199 {VTAP_OUTBOUND_TABLE, VTAP_OUTBOUND_GROUP_TABLE,
200 OUTBOUND_NEXT_TABLE, VTAP_OUTBOUND_MIRROR_TABLE}};
201 private static final int VTAP_TABLE_INBOUND_IDX = 0;
202 private static final int VTAP_TABLE_FLAT_OUTBOUND_IDX = 1;
203 private static final int VTAP_TABLE_OUTBOUND_IDX = 2;
204 private static final int VTAP_TABLE_INPUT_IDX = 0;
205 private static final int VTAP_TABLE_GROUP_IDX = 1;
206 private static final int VTAP_TABLE_NEXT_IDX = 2;
207 private static final int VTAP_TABLE_OUTPUT_IDX = 3;
208
Jian Li311a9c92018-07-09 16:48:36 +0900209 private static final IpPrefix ARBITRARY_IP_PREFIX =
210 IpPrefix.valueOf(IpAddress.valueOf("0.0.0.0"), 0);
Jimo Jung14e87bf2018-09-03 16:28:13 +0900211 private static final String TABLE_EXTENSION = "table";
212 private static final String TUNNEL_DST_EXTENSION = "tunnelDst";
213 private static final String TUNNEL_NICIRA = "tunnelNicira";
214
215 private static final int VTAP_NETWORK_KEY = 0;
Jian Li311a9c92018-07-09 16:48:36 +0900216
Jian Li614cb092018-07-03 22:41:42 +0900217 private final DeviceListener deviceListener = new InternalDeviceListener();
Jian Li26ef1302018-07-04 14:37:06 +0900218 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900219 private final HostListener hostListener = new InternalHostListener();
Jian Li614cb092018-07-03 22:41:42 +0900220
221 private OpenstackVtapStoreDelegate delegate = new InternalStoreDelegate();
222
223 private ApplicationId appId;
224 private NodeId localNodeId;
225 private ScheduledExecutorService eventExecutor;
226
Jimo Jung14e87bf2018-09-03 16:28:13 +0900227 private final Object syncInterface = new Object(); // notification of tunnel interface
228 private static final int INTERFACE_MANIPULATION_TIMEOUT = 1000; // 1000msec
229 private static final int INTERFACE_MANIPULATION_RETRY = 10; // 10 times (totally 10sec)
Jian Li614cb092018-07-03 22:41:42 +0900230
231 @Activate
232 public void activate(ComponentContext context) {
233 appId = coreService.registerApplication(APP_ID);
234 localNodeId = clusterService.getLocalNode().id();
235 leadershipService.runForLeadership(appId.name());
Jimo Jung14e87bf2018-09-03 16:28:13 +0900236 componentConfigService.registerProperties(getClass());
Jian Li614cb092018-07-03 22:41:42 +0900237
238 eventExecutor = newSingleThreadScheduledExecutor(
239 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
240
241 store.setDelegate(delegate);
242 eventDispatcher.addSink(OpenstackVtapEvent.class, listenerRegistry);
243
244 deviceService.addListener(deviceListener);
Jian Li26ef1302018-07-04 14:37:06 +0900245 osNodeService.addListener(osNodeListener);
Jimo Jung14e87bf2018-09-03 16:28:13 +0900246 hostService.addListener(hostListener);
Jian Li26ef1302018-07-04 14:37:06 +0900247
Jimo Jung14e87bf2018-09-03 16:28:13 +0900248 initVtap();
Jian Li614cb092018-07-03 22:41:42 +0900249
Jimo Jung14e87bf2018-09-03 16:28:13 +0900250 log.info("Started");
Jian Li19f25262018-07-03 22:37:12 +0900251 }
252
Jian Li614cb092018-07-03 22:41:42 +0900253 @Deactivate
254 public void deactivate() {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900255 clearVtap();
Jian Lib1ca1a22018-07-06 13:31:39 +0900256
Jian Li614cb092018-07-03 22:41:42 +0900257 hostService.removeListener(hostListener);
Jimo Jung14e87bf2018-09-03 16:28:13 +0900258 osNodeService.removeListener(osNodeListener);
Jian Li614cb092018-07-03 22:41:42 +0900259 deviceService.removeListener(deviceListener);
Jian Li19f25262018-07-03 22:37:12 +0900260
Jian Li614cb092018-07-03 22:41:42 +0900261 eventDispatcher.removeSink(OpenstackVtapEvent.class);
262 store.unsetDelegate(delegate);
Jian Li19f25262018-07-03 22:37:12 +0900263
Jian Li614cb092018-07-03 22:41:42 +0900264 eventExecutor.shutdown();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900265
266 componentConfigService.unregisterProperties(getClass(), false);
Jian Li614cb092018-07-03 22:41:42 +0900267 leadershipService.withdraw(appId.name());
Jian Li19f25262018-07-03 22:37:12 +0900268
Jimo Jung14e87bf2018-09-03 16:28:13 +0900269 log.info("Stopped");
270 }
271
272 @Modified
273 protected void modified(ComponentContext context) {
274 Dictionary<?, ?> properties = context.getProperties();
275
276 boolean updatedTunnelNicira = Tools.isPropertyEnabled(properties, TUNNEL_NICIRA);
277 if (tunnelNicira != updatedTunnelNicira) {
278 if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
279 // Update the tunnel flow rule by reflecting the change.
280 osNodeService.completeNodes(COMPUTE)
281 .forEach(osNode -> applyVtapNetwork(getVtapNetwork(), osNode, false));
282 tunnelNicira = updatedTunnelNicira;
283 osNodeService.completeNodes(COMPUTE).stream()
284 .filter(osNode -> osNode.state() == COMPLETE)
285 .forEach(osNode -> applyVtapNetwork(getVtapNetwork(), osNode, true));
286 log.debug("Apply {} nicira extension for tunneling", tunnelNicira ? "enable" : "disable");
287 } else {
288 tunnelNicira = updatedTunnelNicira;
289 }
290 }
291
292 log.info("Modified");
293 }
294
295 /**
296 * Initializes the flow rules and group tables, tunneling interface for all completed compute nodes.
297 */
298 @Override
299 public void initVtap() {
300 if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
301 osNodeService.completeNodes(COMPUTE).stream()
302 .filter(osNode -> osNode.state() == COMPLETE)
303 .forEach(osNode -> initVtapForNode(osNode));
304 log.trace("{} flow rules, groups, tunnel interface are initialized", appId.name());
305 }
306 }
307
308 /**
309 * Clears the flow rules and group tables, tunneling interface for all compute nodes.
310 */
311 @Override
312 public void clearVtap() {
313 if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
314 osNodeService.completeNodes(COMPUTE).stream()
315 .forEach(osNode -> clearVtapForNode(osNode));
316 log.trace("{} flow rules, groups, tunnel interface are cleared", appId.name());
317 }
318 }
319
320 /**
321 * Purges all flow rules and group tables, tunneling interface for openstack vtap.
322 */
323 @Override
324 public void purgeVtap() {
325 // Remove all flow rules
326 flowRuleService.removeFlowRulesById(appId);
327
328 // Remove all groups and tunnel interfaces
329 osNodeService.completeNodes(COMPUTE).stream()
330 .filter(osNode -> osNode.state() == COMPLETE)
331 .forEach(osNode -> {
332 groupService.getGroups(osNode.intgBridge(), appId)
333 .forEach(group ->
334 groupService.removeGroup(osNode.intgBridge(), group.appCookie(), appId));
335
336 OpenstackVtapNetwork vtapNetwork = getVtapNetwork();
337 setTunnelInterface(osNode, vtapNetwork, false);
338 });
339
340 log.trace("{} all flow rules, groups, tunnel interface are purged", appId.name());
341 }
342
343 private void initVtapForNode(OpenstackNode osNode) {
344 // Make base vtap network
345 initVtapNetwork(osNode);
346
347 // Make vtap connections by OpenstackVtap config
348 getVtapsByDeviceId(osNode.intgBridge())
349 .forEach(vtap -> applyVtap(vtap, osNode, true));
350
351 // Make vtap networks by OpenstackVtapNetwork config
352 applyVtapNetwork(getVtapNetwork(), osNode, true);
353 }
354
355 private void clearVtapForNode(OpenstackNode osNode) {
356 // Clear vtap networks by OpenstackVtapNetwork config
357 applyVtapNetwork(getVtapNetwork(), osNode, false);
358
359 // Clear vtap connections by OpenstackVtap config
360 getVtapsByDeviceId(osNode.intgBridge())
361 .forEach(vtap -> applyVtap(vtap, osNode, false));
362
363 // Clear base vtap network
364 clearVtapNetwork(osNode);
365 }
366
367 /**
368 * Initializes vtap pipeline of the given device.
369 *
370 * @param osNode device identifier
371 */
372 private void initVtapNetwork(OpenstackNode osNode) {
373 // Create default output tables
374 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
375 setOutputTableForDrop(osNode.intgBridge(),
376 VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX], true);
377 }
378
379 // Create group tables
380 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
381 createGroupTable(osNode.intgBridge(),
382 VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX],
383 ImmutableList.of(VTAP_TABLES[idx][VTAP_TABLE_NEXT_IDX],
384 VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX]),
385 null);
386 }
387 }
388
389 /**
390 * Clear vtap pipeline of the given device.
391 *
392 * @param osNode device identifier
393 */
394 private void clearVtapNetwork(OpenstackNode osNode) {
395 // Clear group tables
396 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
397 removeGroupTable(osNode.intgBridge(),
398 VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX]);
399 }
400
401 // Clear default output tables
402 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
403 setOutputTableForDrop(osNode.intgBridge(),
404 VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX], false);
405 }
406 }
407
408 @Override
409 public OpenstackVtapNetwork getVtapNetwork() {
410 return store.getVtapNetwork(VTAP_NETWORK_KEY);
411 }
412
413 @Override
414 public OpenstackVtapNetwork createVtapNetwork(Mode mode, Integer networkId, IpAddress serverIp) {
415 checkNotNull(mode, VTAP_DESC_NULL, "mode");
416 checkNotNull(serverIp, VTAP_DESC_NULL, "serverIp");
417 DefaultOpenstackVtapNetwork vtapNetwork = DefaultOpenstackVtapNetwork.builder()
418 .mode(mode)
419 .networkId(networkId)
420 .serverIp(serverIp)
421 .build();
422 return store.createVtapNetwork(VTAP_NETWORK_KEY, vtapNetwork);
423 }
424
425 @Override
426 public OpenstackVtapNetwork updateVtapNetwork(OpenstackVtapNetwork description) {
427 checkNotNull(description, VTAP_DESC_NULL, "vtapNetwork");
428 return store.updateVtapNetwork(VTAP_NETWORK_KEY, description);
429 }
430
431 @Override
432 public OpenstackVtapNetwork removeVtapNetwork() {
433 return store.removeVtapNetwork(VTAP_NETWORK_KEY);
434 }
435
436 @Override
437 public Set<DeviceId> getVtapNetworkDevices() {
438 return store.getVtapNetworkDevices(VTAP_NETWORK_KEY);
Jian Li19f25262018-07-03 22:37:12 +0900439 }
440
441 @Override
442 public int getVtapCount(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900443 return store.getVtapCount(type);
Jian Li38e4d942018-07-03 22:19:16 +0900444 }
445
446 @Override
Jian Li19f25262018-07-03 22:37:12 +0900447 public Set<OpenstackVtap> getVtaps(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900448 return store.getVtaps(type);
Jian Li38e4d942018-07-03 22:19:16 +0900449 }
450
451 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900452 public OpenstackVtap getVtap(OpenstackVtapId vtapId) {
453 return store.getVtap(vtapId);
Jian Li38e4d942018-07-03 22:19:16 +0900454 }
455
456 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900457 public Set<OpenstackVtap> getVtapsByDeviceId(DeviceId deviceId) {
458 return store.getVtapsByDeviceId(deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900459 }
460
Jian Li614cb092018-07-03 22:41:42 +0900461 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900462 public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vtapCriterion) {
463 checkNotNull(type, VTAP_DESC_NULL, "type");
464 checkNotNull(vtapCriterion, VTAP_DESC_NULL, "vtapCriterion");
Jian Li614cb092018-07-03 22:41:42 +0900465
466 Set<DeviceId> txDevices = type.isValid(Type.VTAP_TX) ?
Jimo Jung14e87bf2018-09-03 16:28:13 +0900467 getEdgeDevice(Type.VTAP_TX, vtapCriterion) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900468 Set<DeviceId> rxDevices = type.isValid(Type.VTAP_RX) ?
Jimo Jung14e87bf2018-09-03 16:28:13 +0900469 getEdgeDevice(Type.VTAP_RX, vtapCriterion) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900470
Jimo Jung14e87bf2018-09-03 16:28:13 +0900471 DefaultOpenstackVtap description = DefaultOpenstackVtap.builder()
472 .id(OpenstackVtapId.vtapId())
473 .type(type)
474 .vtapCriterion(vtapCriterion)
475 .txDeviceIds(txDevices)
476 .rxDeviceIds(rxDevices)
477 .build();
478 return store.createVtap(description);
Jian Li614cb092018-07-03 22:41:42 +0900479 }
480
481 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900482 public OpenstackVtap updateVtap(OpenstackVtap description) {
483 checkNotNull(description, VTAP_DESC_NULL, "vtap");
Jian Li614cb092018-07-03 22:41:42 +0900484
Jimo Jung14e87bf2018-09-03 16:28:13 +0900485 Set<DeviceId> txDevices = description.type().isValid(Type.VTAP_TX) ?
486 getEdgeDevice(Type.VTAP_TX, description.vtapCriterion()) : ImmutableSet.of();
487 Set<DeviceId> rxDevices = description.type().isValid(Type.VTAP_RX) ?
488 getEdgeDevice(Type.VTAP_RX, description.vtapCriterion()) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900489
Jimo Jung14e87bf2018-09-03 16:28:13 +0900490 DefaultOpenstackVtap vtap = DefaultOpenstackVtap.builder(description)
491 .txDeviceIds(txDevices)
492 .rxDeviceIds(rxDevices)
493 .build();
494 return store.updateVtap(vtap, true);
Jian Li614cb092018-07-03 22:41:42 +0900495 }
496
497 @Override
Jimo Jung14e87bf2018-09-03 16:28:13 +0900498 public OpenstackVtap removeVtap(OpenstackVtapId vtapId) {
499 return store.removeVtap(vtapId);
Jian Li614cb092018-07-03 22:41:42 +0900500 }
501
Jian Li26ef1302018-07-04 14:37:06 +0900502 /**
503 * Obtains the identifier set of edge device where the targeted host is located.
504 * Note that, in most of cases target host is attached to one device,
505 * however, in some cases, the host can be attached to multiple devices.
506 *
Jimo Jung14e87bf2018-09-03 16:28:13 +0900507 * @param type vtap type
508 * @param criterion vtap criterion
Jian Li26ef1302018-07-04 14:37:06 +0900509 * @return a collection of device identifiers
510 */
511 private Set<DeviceId> getEdgeDevice(Type type, OpenstackVtapCriterion criterion) {
512 Set<DeviceId> deviceIds = Sets.newConcurrentHashSet();
513 StreamSupport.stream(hostService.getHosts().spliterator(), true)
Jimo Jung14e87bf2018-09-03 16:28:13 +0900514 .filter(host -> isValidHost(host) &&
515 host.ipAddresses().stream().anyMatch(ip -> containsIp(type, criterion, ip)))
516 .forEach(host -> {
517 Set<DeviceId> hostDeviceIds =
518 host.locations().stream()
519 .map(HostLocation::deviceId)
520 .filter(deviceId -> Objects.nonNull(osNodeService.node(deviceId)))
521 .collect(Collectors.toSet());
522 deviceIds.addAll(hostDeviceIds);
523 });
Jian Li26ef1302018-07-04 14:37:06 +0900524 return deviceIds;
Jian Li614cb092018-07-03 22:41:42 +0900525 }
526
Jian Li26ef1302018-07-04 14:37:06 +0900527 /**
Jimo Jung14e87bf2018-09-03 16:28:13 +0900528 * Updates device list of vtaps with respect to the host changes.
Jian Li26ef1302018-07-04 14:37:06 +0900529 *
530 * @param newHost new host instance
531 * @param oldHost old host instance
532 */
Jimo Jung14e87bf2018-09-03 16:28:13 +0900533 private void updateHostbyType(Type type, Host newHost, Host oldHost) {
534 getVtaps(type).forEach(vtap -> {
535 IpPrefix prefix = (type == Type.VTAP_TX) ?
536 vtap.vtapCriterion().srcIpPrefix() :
537 vtap.vtapCriterion().dstIpPrefix();
538
539 int hostDiff = hostCompareIp(newHost, oldHost, prefix);
540 if (hostDiff < 0) {
541 oldHost.locations().stream()
542 .map(HostLocation::deviceId)
543 .forEach(deviceId ->
544 store.removeDeviceFromVtap(vtap.id(), type, deviceId));
545 } else if (hostDiff > 0) {
546 newHost.locations().stream()
547 .map(HostLocation::deviceId)
548 .filter(deviceId -> Objects.nonNull(osNodeService.node(deviceId)))
549 .forEach(deviceId ->
550 store.addDeviceToVtap(vtap.id(), type, deviceId));
551 }
552 });
553 }
554
Jian Li614cb092018-07-03 22:41:42 +0900555 private void updateHost(Host newHost, Host oldHost) {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900556 // update devices for vtap tx
557 updateHostbyType(Type.VTAP_TX, newHost, oldHost);
Jian Li26ef1302018-07-04 14:37:06 +0900558
Jimo Jung14e87bf2018-09-03 16:28:13 +0900559 // update devices for vtap rx
560 updateHostbyType(Type.VTAP_RX, newHost, oldHost);
Jian Li614cb092018-07-03 22:41:42 +0900561 }
562
Jimo Jung14e87bf2018-09-03 16:28:13 +0900563 private void applyFlowRule(FlowRule flowRule, boolean install) {
564 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
Jian Li614cb092018-07-03 22:41:42 +0900565
Jimo Jung14e87bf2018-09-03 16:28:13 +0900566 if (install) {
567 flowOpsBuilder.add(flowRule);
568 } else {
569 flowOpsBuilder.remove(flowRule);
Jian Li26ef1302018-07-04 14:37:06 +0900570 }
571
Jimo Jung14e87bf2018-09-03 16:28:13 +0900572 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
573 @Override
574 public void onSuccess(FlowRuleOperations ops) {
575 log.debug("Installed flow rules for vtap");
576 }
Jian Li26ef1302018-07-04 14:37:06 +0900577
Jimo Jung14e87bf2018-09-03 16:28:13 +0900578 @Override
579 public void onError(FlowRuleOperations ops) {
580 log.warn("Failed to install flow rules for vtap");
581 }
582 }));
Jian Li614cb092018-07-03 22:41:42 +0900583 }
584
Jimo Jung14e87bf2018-09-03 16:28:13 +0900585 private void connectTables(DeviceId deviceId,
586 int fromTable,
587 int toTableOrGroup, boolean isGroup,
588 OpenstackVtapCriterion vtapCriterion,
589 int rulePriority, boolean install) {
590 log.debug("Table Transition: table[{}] -> table/group[{}]", fromTable, toTableOrGroup);
Jian Li614cb092018-07-03 22:41:42 +0900591
592 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Jian Li311a9c92018-07-09 16:48:36 +0900593 .matchEthType(TYPE_IPV4);
Jian Li614cb092018-07-03 22:41:42 +0900594
Jian Li311a9c92018-07-09 16:48:36 +0900595 // 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 +0900596 if (!vtapCriterion.srcIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
597 selectorBuilder.matchIPSrc(vtapCriterion.srcIpPrefix());
Jian Li311a9c92018-07-09 16:48:36 +0900598 }
599
Jimo Jung14e87bf2018-09-03 16:28:13 +0900600 if (!vtapCriterion.dstIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
601 selectorBuilder.matchIPDst(vtapCriterion.dstIpPrefix());
Jian Li311a9c92018-07-09 16:48:36 +0900602 }
603
Jimo Jung14e87bf2018-09-03 16:28:13 +0900604 switch (vtapCriterion.ipProtocol()) {
Jian Li614cb092018-07-03 22:41:42 +0900605 case PROTOCOL_TCP:
Jimo Jung14e87bf2018-09-03 16:28:13 +0900606 selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900607
608 // Add port match only if the port number is greater than zero
Jimo Jung14e87bf2018-09-03 16:28:13 +0900609 if (vtapCriterion.srcTpPort().toInt() > 0) {
610 selectorBuilder.matchTcpSrc(vtapCriterion.srcTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900611 }
Jimo Jung14e87bf2018-09-03 16:28:13 +0900612 if (vtapCriterion.dstTpPort().toInt() > 0) {
613 selectorBuilder.matchTcpDst(vtapCriterion.dstTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900614 }
615 break;
616 case PROTOCOL_UDP:
Jimo Jung14e87bf2018-09-03 16:28:13 +0900617 selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900618
619 // Add port match only if the port number is greater than zero
Jimo Jung14e87bf2018-09-03 16:28:13 +0900620 if (vtapCriterion.srcTpPort().toInt() > 0) {
621 selectorBuilder.matchUdpSrc(vtapCriterion.srcTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900622 }
Jimo Jung14e87bf2018-09-03 16:28:13 +0900623 if (vtapCriterion.dstTpPort().toInt() > 0) {
624 selectorBuilder.matchUdpDst(vtapCriterion.dstTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900625 }
626 break;
627 case PROTOCOL_ICMP:
Jimo Jung14e87bf2018-09-03 16:28:13 +0900628 selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol());
Jian Li614cb092018-07-03 22:41:42 +0900629 break;
630 default:
631 break;
632 }
633
634 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900635 if (isGroup) {
636 treatmentBuilder.group(GroupId.valueOf(toTableOrGroup));
Jian Li614cb092018-07-03 22:41:42 +0900637 } else {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900638 treatmentBuilder.transition(toTableOrGroup);
Jian Li614cb092018-07-03 22:41:42 +0900639 }
640
641 FlowRule flowRule = DefaultFlowRule.builder()
642 .forDevice(deviceId)
643 .withSelector(selectorBuilder.build())
644 .withTreatment(treatmentBuilder.build())
645 .withPriority(rulePriority)
646 .fromApp(appId)
647 .makePermanent()
648 .forTable(fromTable)
649 .build();
650
651 applyFlowRule(flowRule, install);
652 }
653
Jimo Jung14e87bf2018-09-03 16:28:13 +0900654 /**
655 * Creates/Removes a tunnel interface in a given openstack node by vtap network information.
656 *
657 * @param osNode openstack node
658 * @param vtapNetwork openstack vtap network for making
659 *
660 */
661 private boolean setTunnelInterface(OpenstackNode osNode,
662 OpenstackVtapNetwork vtapNetwork,
663 boolean install) {
664 String tunnelName = getTunnelName(vtapNetwork.mode());
665 if (tunnelName == null) {
666 return false;
Jian Li614cb092018-07-03 22:41:42 +0900667 }
Jimo Jung14e87bf2018-09-03 16:28:13 +0900668
669 if (!deviceService.isAvailable(osNode.ovsdb())) {
670 log.warn("Not available osNode {} ovs {}", osNode.hostname(), osNode.ovsdb());
671 return false;
672 }
673
674 if (install == isInterfaceEnabled(osNode.intgBridge(), tunnelName)) {
675 log.warn("Already {} {} interface on osNode ovs {}, bridge {}",
676 install ? "add" : "remove",
677 tunnelName, osNode.ovsdb(), osNode.intgBridge());
678 return true;
679 }
680
681 Device device = deviceService.getDevice(osNode.ovsdb());
682 if (device == null || !device.is(InterfaceConfig.class)) {
683 log.warn("Not able to get InterfaceConfig on osNode ovs {}", osNode.ovsdb());
684 return false;
685 }
686
687 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
688 if (install) {
689 TunnelDescription.Builder tunnelDesc = DefaultTunnelDescription.builder()
690 .deviceId(INTEGRATION_BRIDGE)
691 .ifaceName(tunnelName)
692 .type(getTunnelType(vtapNetwork.mode()))
693 .key((vtapNetwork.networkId() == 0) ? null : new TunnelKey<>(vtapNetwork.networkId()))
694 .remote(TunnelEndPoints.ipTunnelEndpoint(vtapNetwork.serverIp()));
695 if (!ifaceConfig.addTunnelMode(tunnelName, tunnelDesc.build())) {
696 log.error("Fail to create {} interface on osNode ovs {}", tunnelName, osNode.ovsdb());
697 return false;
698 }
699 } else {
700 if (!ifaceConfig.removeTunnelMode(tunnelName)) {
701 log.error("Fail to remove {} interface on osNode ovs {}", tunnelName, osNode.ovsdb());
702 return false;
703 }
704 }
705
706 // Wait for tunnel interface create/remove complete
707 synchronized (syncInterface) {
708 for (int i = 0; i < INTERFACE_MANIPULATION_RETRY; i++) {
709 try {
710 syncInterface.wait(INTERFACE_MANIPULATION_TIMEOUT);
711 if (install == isInterfaceEnabled(osNode.intgBridge(), tunnelName)) {
712 log.debug("Success to {} {} interface on osNode ovs {}, bridge {}",
713 install ? "add" : "remove",
714 tunnelName, osNode.ovsdb(), osNode.intgBridge());
715 return true;
716 }
717 } catch (InterruptedException e) {
718 break;
719 }
720 }
721 }
722 log.warn("Fail to {} {} interface on osNode ovs {}, bridge {}",
723 install ? "add" : "remove",
724 tunnelName, osNode.ovsdb(), osNode.intgBridge());
725 return false;
726 }
727
728 /**
729 * Checks whether a given network interface in a given openstack node is enabled or not.
730 *
731 * @param deviceId openstack node
732 * @param interfaceName network interface name
733 * @return true if the given interface is enabled, false otherwise
734 */
735 private boolean isInterfaceEnabled(DeviceId deviceId, String interfaceName) {
736 return deviceService.isAvailable(deviceId) &&
737 deviceService.getPorts(deviceId).parallelStream().anyMatch(port ->
738 Objects.equals(port.annotations().value(PORT_NAME), interfaceName) && port.isEnabled());
739 }
740
741 private PortNumber portNumber(DeviceId deviceId, String interfaceName) {
742 Port port = deviceService.getPorts(deviceId).stream()
743 .filter(p -> p.isEnabled() &&
744 Objects.equals(p.annotations().value(PORT_NAME), interfaceName))
745 .findAny().orElse(null);
746 return port != null ? port.number() : null;
747 }
748
749 private void setOutputTableForTunnel(DeviceId deviceId, int tableId,
750 PortNumber outPort, IpAddress serverIp,
751 boolean install) {
752 log.debug("setOutputTableForTunnel[{}]: deviceId={}, tableId={}, outPort={}, serverIp={}",
753 install ? "add" : "remove", deviceId, tableId, outPort, serverIp);
754
755 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
756 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
757 .setOutput(outPort);
758
759 if (tunnelNicira) {
760 ExtensionTreatment extensionTreatment = buildTunnelExtension(deviceId, serverIp);
761 if (extensionTreatment == null) {
762 return;
763 }
764 treatment.extension(extensionTreatment, deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900765 }
766
767 FlowRule flowRule = DefaultFlowRule.builder()
768 .forDevice(deviceId)
769 .withSelector(selector.build())
770 .withTreatment(treatment.build())
Jimo Jung14e87bf2018-09-03 16:28:13 +0900771 .withPriority(PRIORITY_VTAP_OUTPUT_RULE)
Jian Li614cb092018-07-03 22:41:42 +0900772 .makePermanent()
773 .forTable(tableId)
774 .fromApp(appId)
775 .build();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900776
777 log.debug("setOutputTableForTunnel flowRule={}, install={}", flowRule, install);
778 applyFlowRule(flowRule, install);
Jian Li614cb092018-07-03 22:41:42 +0900779 }
780
Jimo Jung14e87bf2018-09-03 16:28:13 +0900781 private void setOutputTableForDrop(DeviceId deviceId, int tableId,
782 boolean install) {
783 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
784 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
Jian Li614cb092018-07-03 22:41:42 +0900785
Jimo Jung14e87bf2018-09-03 16:28:13 +0900786 FlowRule flowRule = DefaultFlowRule.builder()
787 .forDevice(deviceId)
788 .withSelector(selector.build())
789 .withTreatment(treatment.build())
790 .withPriority(PRIORITY_VTAP_OUTPUT_DROP)
791 .makePermanent()
792 .forTable(tableId)
793 .fromApp(appId)
794 .build();
795 applyFlowRule(flowRule, install);
796 }
Jian Li614cb092018-07-03 22:41:42 +0900797
Jimo Jung14e87bf2018-09-03 16:28:13 +0900798 private void setOutputTable(DeviceId deviceId, Mode mode,
799 IpAddress serverIp, boolean install) {
800 log.debug("setOutputTable[{}]: deviceId={}, mode={}, serverIp={}",
801 install ? "add" : "remove", deviceId, mode, serverIp);
802
803 if (deviceId == null) {
804 return;
Jian Li614cb092018-07-03 22:41:42 +0900805 }
806
Jimo Jung14e87bf2018-09-03 16:28:13 +0900807 switch (mode) {
808 case GRE:
809 case VXLAN:
810 String tunnelName = getTunnelName(mode);
811 PortNumber vtapPort = portNumber(deviceId, tunnelName);
812 if (vtapPort != null) {
813 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
814 setOutputTableForTunnel(deviceId, VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX],
815 vtapPort, serverIp, install);
816 }
817 } else {
818 log.warn("Vtap tunnel port {} doesn't exist", tunnelName);
819 }
820 break;
821 default:
822 log.warn("Invalid vtap network mode {}", mode);
823 break;
824 }
825 }
826
827 /**
828 * Returns tunnel destination extension treatment object.
829 *
830 * @param deviceId device id to apply this treatment
831 * @param remoteIp tunnel destination ip address
832 * @return extension treatment
833 */
834 private ExtensionTreatment buildTunnelExtension(DeviceId deviceId, IpAddress remoteIp) {
835 Device device = deviceService.getDevice(deviceId);
836 if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
837 log.warn("Nicira extension treatment is not supported");
838 return null;
839 }
840
841 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
842 ExtensionTreatment treatment =
843 resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
844 try {
845 treatment.setPropertyValue(TUNNEL_DST_EXTENSION, remoteIp.getIp4Address());
846 return treatment;
847 } catch (ExtensionPropertyException e) {
848 log.error("Failed to set nicira tunnelDst extension treatment for {}", deviceId);
849 return null;
850 }
851 }
852
853 private ExtensionTreatment buildResubmitExtension(DeviceId deviceId, int tableId) {
854 Device device = deviceService.getDevice(deviceId);
855 if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
856 log.warn("Nicira extension treatment is not supported");
857 return null;
858 }
859
860 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
861 ExtensionTreatment treatment =
862 resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type());
863
864 try {
865 treatment.setPropertyValue(TABLE_EXTENSION, ((short) tableId));
866 return treatment;
867 } catch (ExtensionPropertyException e) {
868 log.error("Failed to set nicira resubmit extension treatment for {}", deviceId);
869 return null;
870 }
Jian Li614cb092018-07-03 22:41:42 +0900871 }
872
873 private void createGroupTable(DeviceId deviceId, int groupId,
874 List<Integer> tableIds, List<PortNumber> ports) {
875 List<GroupBucket> buckets = Lists.newArrayList();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900876 if (tableIds != null) {
877 tableIds.forEach(tableId -> {
878 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
879 .extension(buildResubmitExtension(deviceId, tableId), deviceId);
880 GroupBucket bucket = DefaultGroupBucket
881 .createAllGroupBucket(treatment.build());
882 buckets.add(bucket);
883 });
884 }
885 if (ports != null) {
886 ports.forEach(port -> {
887 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
888 .setOutput(port);
889 GroupBucket bucket = DefaultGroupBucket
890 .createAllGroupBucket(treatment.build());
891 buckets.add(bucket);
892 });
893 }
Jian Li614cb092018-07-03 22:41:42 +0900894
895 GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
896 GroupDescription.Type.ALL,
897 new GroupBuckets(buckets),
898 getGroupKey(groupId),
899 groupId,
900 appId);
901 groupService.addGroup(groupDescription);
902 }
903
Jimo Jung14e87bf2018-09-03 16:28:13 +0900904 private void removeGroupTable(DeviceId deviceId, int groupId) {
905 groupService.removeGroup(deviceId, getGroupKey(groupId), appId);
Jian Li26ef1302018-07-04 14:37:06 +0900906 }
907
Jimo Jung14e87bf2018-09-03 16:28:13 +0900908 /**
909 * Internal listener for device events.
910 */
Jian Li26ef1302018-07-04 14:37:06 +0900911 private class InternalDeviceListener implements DeviceListener {
Jian Li26ef1302018-07-04 14:37:06 +0900912
913 @Override
914 public void event(DeviceEvent event) {
915 DeviceEvent.Type type = event.type();
Jimo Jung14e87bf2018-09-03 16:28:13 +0900916 Device device = event.subject();
Jian Li26ef1302018-07-04 14:37:06 +0900917
918 switch (type) {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900919 case PORT_ADDED:
920 case PORT_UPDATED:
921 case PORT_REMOVED:
922 String portName = event.port().annotations().value(PORT_NAME);
923 if (portName.equals(getTunnelName(Mode.GRE)) ||
924 portName.equals(getTunnelName(Mode.VXLAN))) {
925 log.trace("InternalDeviceListener type={}, host={}", type, device);
926 synchronized (syncInterface) {
927 try {
928 syncInterface.notifyAll();
929 } catch (IllegalMonitorStateException e) {
930 log.warn("Already syncInterface exited");
931 }
932 }
933 }
Jian Li26ef1302018-07-04 14:37:06 +0900934 break;
935 default:
936 break;
937 }
938 }
939 }
940
Jimo Jung14e87bf2018-09-03 16:28:13 +0900941 /**
942 * Internal listener for openstack node events.
943 */
944 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
945
946 @Override
947 public boolean isRelevant(OpenstackNodeEvent event) {
948 // do not allow to proceed without leadership and compute node
949 NodeId leader = leadershipService.getLeader(appId.name());
950 OpenstackNode osNode = event.subject();
951
952 return Objects.equals(localNodeId, leader) && osNode.type() == COMPUTE;
953 }
954
955 @Override
956 public void event(OpenstackNodeEvent event) {
957 OpenstackNodeEvent.Type type = event.type();
958 OpenstackNode osNode = event.subject();
959 log.trace("InternalOpenstackNodeListener type={}, osNode={}", type, osNode);
960
961 eventExecutor.execute(() -> {
962 try {
963 switch (type) {
964 case OPENSTACK_NODE_COMPLETE:
965 initVtapForNode(osNode);
966 break;
967
968 case OPENSTACK_NODE_REMOVED:
969 clearVtapForNode(osNode);
970 break;
971
972 default:
973 break;
974 }
975 } catch (Exception e) {
976 dumpStackTrace(log, e);
977 }
978 });
979 }
980 }
981
982 /**
983 * Internal listener for host events.
984 */
Jian Li26ef1302018-07-04 14:37:06 +0900985 private class InternalHostListener implements HostListener {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900986
Jian Li26ef1302018-07-04 14:37:06 +0900987 @Override
988 public boolean isRelevant(HostEvent event) {
Jimo Jung14e87bf2018-09-03 16:28:13 +0900989 Host host = event.subject();
990 if (!isValidHost(host)) {
991 log.debug("Invalid host detected, ignore it {}", host);
992 return false;
993 }
994
Jian Li26ef1302018-07-04 14:37:06 +0900995 // do not allow to proceed without leadership
996 NodeId leader = leadershipService.getLeader(appId.name());
997 return Objects.equals(localNodeId, leader);
998 }
999
1000 @Override
1001 public void event(HostEvent event) {
1002 HostEvent.Type type = event.type();
1003 Host host = event.subject();
Jimo Jung14e87bf2018-09-03 16:28:13 +09001004 Host prevHost = event.prevSubject();
1005 log.trace("InternalHostListener {}: {} -> {}", type, prevHost, host);
Jian Li26ef1302018-07-04 14:37:06 +09001006
Jimo Jung14e87bf2018-09-03 16:28:13 +09001007 eventExecutor.execute(() -> {
1008 try {
1009 switch (event.type()) {
1010 case HOST_ADDED:
1011 updateHost(host, null);
1012 break;
Jian Li26ef1302018-07-04 14:37:06 +09001013
Jimo Jung14e87bf2018-09-03 16:28:13 +09001014 case HOST_REMOVED:
1015 updateHost(null, host);
1016 break;
Jian Li26ef1302018-07-04 14:37:06 +09001017
Jimo Jung14e87bf2018-09-03 16:28:13 +09001018 case HOST_MOVED:
1019 case HOST_UPDATED:
1020 updateHost(host, prevHost);
1021 break;
Jian Li26ef1302018-07-04 14:37:06 +09001022
Jimo Jung14e87bf2018-09-03 16:28:13 +09001023 default:
1024 break;
1025 }
1026 } catch (Exception e) {
1027 dumpStackTrace(log, e);
1028 }
1029 });
Jian Li26ef1302018-07-04 14:37:06 +09001030 }
1031 }
1032
1033 // Store delegate to re-post events emitted from the store.
1034 private class InternalStoreDelegate implements OpenstackVtapStoreDelegate {
Jimo Jung14e87bf2018-09-03 16:28:13 +09001035
Jian Li26ef1302018-07-04 14:37:06 +09001036 @Override
1037 public void notify(OpenstackVtapEvent event) {
1038 OpenstackVtapEvent.Type type = event.type();
Jimo Jung14e87bf2018-09-03 16:28:13 +09001039 log.trace("InternalStoreDelegate {}: {} -> {}", type, event.prevSubject(), event.subject());
Jian Li26ef1302018-07-04 14:37:06 +09001040
Jimo Jung14e87bf2018-09-03 16:28:13 +09001041 if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
1042 eventExecutor.execute(() -> {
1043 try {
1044 switch (type) {
1045 case VTAP_NETWORK_ADDED:
1046 case VTAP_NETWORK_UPDATED:
1047 case VTAP_NETWORK_REMOVED:
1048 // Update network
1049 updateVtapNetwork(event.openstackVtapNetwork(),
1050 event.prevOpenstackVtapNetwork());
1051 break;
Jian Li26ef1302018-07-04 14:37:06 +09001052
Jimo Jung14e87bf2018-09-03 16:28:13 +09001053 case VTAP_ADDED:
1054 case VTAP_UPDATED:
1055 case VTAP_REMOVED:
1056 // Update vtap rule
1057 updateVtap(event.openstackVtap(),
1058 event.prevOpenstackVtap());
1059 break;
Jian Li26ef1302018-07-04 14:37:06 +09001060
Jimo Jung14e87bf2018-09-03 16:28:13 +09001061 default:
1062 break;
1063 }
1064 } catch (Exception e) {
1065 dumpStackTrace(log, e);
1066 }
1067 });
Jian Li26ef1302018-07-04 14:37:06 +09001068 }
1069 post(event);
1070 }
Jian Li38e4d942018-07-03 22:19:16 +09001071 }
Jimo Jung14e87bf2018-09-03 16:28:13 +09001072
1073 private void applyVtap(OpenstackVtap vtap,
1074 OpenstackNode osNode,
1075 boolean install) {
1076 if (vtap == null || osNode == null) {
1077 return;
1078 }
1079
1080 log.debug("applyVtap vtap={}, osNode={}, install={}", vtap, osNode, install);
1081
1082 DeviceId deviceId = osNode.intgBridge();
1083 for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
1084 if ((idx == VTAP_TABLE_INBOUND_IDX &&
1085 vtap.type().isValid(Type.VTAP_TX) &&
1086 vtap.txDeviceIds().contains(deviceId)) ||
1087 (idx != VTAP_TABLE_INBOUND_IDX &&
1088 vtap.type().isValid(Type.VTAP_RX) &&
1089 vtap.rxDeviceIds().contains(deviceId))) {
1090 connectTables(deviceId,
1091 VTAP_TABLES[idx][VTAP_TABLE_INPUT_IDX],
1092 VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX],
1093 true,
1094 vtap.vtapCriterion(), PRIORITY_VTAP_RULE, install);
1095 }
1096 }
1097 }
1098
1099 private void updateVtap(OpenstackVtap vtap,
1100 OpenstackVtap prevVtap) {
1101 if (Objects.equals(vtap, prevVtap)) {
1102 return;
1103 }
1104
1105 Set<DeviceId> prevTxDeviceIds = (prevVtap != null ? prevVtap.txDeviceIds() : ImmutableSet.of());
1106 Set<DeviceId> txDeviceIds = (vtap != null ? vtap.txDeviceIds() : ImmutableSet.of());
1107 Set<DeviceId> prevRxDeviceIds = (prevVtap != null ? prevVtap.rxDeviceIds() : ImmutableSet.of());
1108 Set<DeviceId> rxDeviceIds = (vtap != null ? vtap.rxDeviceIds() : ImmutableSet.of());
1109
1110 // Remake all vtap rule
1111 if (prevVtap != null) {
1112 Set<DeviceId> deviceIds = Sets.newHashSet();
1113 deviceIds.addAll(Sets.difference(prevTxDeviceIds, txDeviceIds));
1114 deviceIds.addAll(Sets.difference(prevRxDeviceIds, rxDeviceIds));
1115 deviceIds.stream()
1116 .map(deviceId -> osNodeService.node(deviceId))
1117 .filter(osNode -> Objects.nonNull(osNode) &&
1118 osNode.type() == COMPUTE)
1119 .forEach(osNode -> applyVtap(prevVtap, osNode, false));
1120 }
1121 if (vtap != null) {
1122 Set<DeviceId> deviceIds = Sets.newHashSet();
1123 deviceIds.addAll(Sets.difference(txDeviceIds, prevTxDeviceIds));
1124 deviceIds.addAll(Sets.difference(rxDeviceIds, prevRxDeviceIds));
1125 deviceIds.stream()
1126 .map(deviceId -> osNodeService.node(deviceId))
1127 .filter(osNode -> Objects.nonNull(osNode) &&
1128 osNode.type() == COMPUTE && osNode.state() == COMPLETE)
1129 .forEach(osNode -> applyVtap(vtap, osNode, true));
1130 }
1131 }
1132
1133 // create/remove tunnel interface and output table
1134 private boolean applyVtapNetwork(OpenstackVtapNetwork vtapNetwork,
1135 OpenstackNode osNode,
1136 boolean install) {
1137 if (vtapNetwork == null || osNode == null) {
1138 return false;
1139 }
1140
1141 if (install) {
1142 if (setTunnelInterface(osNode, vtapNetwork, true)) {
1143 setOutputTable(osNode.intgBridge(), vtapNetwork.mode(), vtapNetwork.serverIp(), true);
1144 store.addDeviceToVtapNetwork(VTAP_NETWORK_KEY, osNode.intgBridge());
1145 return true;
1146 }
1147 } else {
1148 Set<DeviceId> deviceIds = getVtapNetworkDevices();
1149 if (deviceIds != null && deviceIds.contains(osNode.intgBridge())) {
1150 store.removeDeviceFromVtapNetwork(VTAP_NETWORK_KEY, osNode.intgBridge());
1151 setOutputTable(osNode.intgBridge(), vtapNetwork.mode(), vtapNetwork.serverIp(), false);
1152 setTunnelInterface(osNode, vtapNetwork, false);
1153 return true;
1154 }
1155 }
1156 return false;
1157 }
1158
1159 private void updateVtapNetwork(OpenstackVtapNetwork network,
1160 OpenstackVtapNetwork prevNetwork) {
1161 // Remake all output tables
1162 if (prevNetwork != null) {
1163 osNodeService.completeNodes(COMPUTE)
1164 .forEach(osNode -> applyVtapNetwork(prevNetwork, osNode, false));
1165 }
1166 if (network != null) {
1167 osNodeService.completeNodes(COMPUTE).stream()
1168 .filter(osNode -> osNode.state() == COMPLETE)
1169 .forEach(osNode -> applyVtapNetwork(network, osNode, true));
1170 }
1171 }
1172
Jian Li4f368e82018-07-02 14:22:22 +09001173}