blob: 4bb42a04af2dc461ff402b7509c9807ca34bb629 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002* Copyright 2016-present Open Networking Foundation
Hyunsun Moon44aac662017-02-18 02:07:01 +09003*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16
17package org.onosproject.openstacknetworking.impl;
18
19import com.google.common.base.Strings;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onlab.packet.Ethernet;
Frank Wangf9571662017-06-06 18:01:29 +080026import org.onlab.packet.MacAddress;
daniel parka792cf72017-04-14 16:25:35 +090027import org.onlab.packet.VlanId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090028import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.mastership.MastershipService;
31import org.onosproject.net.device.DeviceService;
32import org.onosproject.net.flow.DefaultTrafficSelector;
33import org.onosproject.net.flow.DefaultTrafficTreatment;
34import org.onosproject.net.flow.TrafficSelector;
35import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090036import org.onosproject.openstacknetworking.api.InstancePort;
37import org.onosproject.openstacknetworking.api.InstancePortEvent;
38import org.onosproject.openstacknetworking.api.InstancePortListener;
39import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090040import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Frank Wangf9571662017-06-06 18:01:29 +080041import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
42import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
Hyunsun Moon44aac662017-02-18 02:07:01 +090043import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090044import org.onosproject.openstacknode.api.OpenstackNode;
45import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090046import org.openstack4j.model.network.Network;
Frank Wangf9571662017-06-06 18:01:29 +080047import org.openstack4j.model.network.NetworkType;
48import org.openstack4j.model.network.Port;
Hyunsun Moon44aac662017-02-18 02:07:01 +090049import org.slf4j.Logger;
50
51import java.util.concurrent.ExecutorService;
52
53import static java.util.concurrent.Executors.newSingleThreadExecutor;
54import static org.onlab.util.Tools.groupedThreads;
Frank Wangf9571662017-06-06 18:01:29 +080055
sanghodc375372017-06-08 10:41:30 +090056import static org.onosproject.openstacknetworking.api.Constants.ACL_TABLE;
57import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
58import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Frank Wangf9571662017-06-06 18:01:29 +080059import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ADMIN_RULE;
sanghodc375372017-06-08 10:41:30 +090060import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SWITCHING_RULE;
61import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE;
62import static org.onosproject.openstacknetworking.api.Constants.SRC_VNI_TABLE;
Frank Wangf9571662017-06-06 18:01:29 +080063import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_CREATED;
64import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_REMOVED;
65import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_UPDATED;
66import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_CREATED;
67import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_REMOVED;
68import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_UPDATED;
Hyunsun Moon44aac662017-02-18 02:07:01 +090069import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090070import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090071import static org.slf4j.LoggerFactory.getLogger;
72
73
74/**
75 * Populates switching flow rules on OVS for the basic connectivity among the
76 * virtual instances in the same network.
77 */
78@Component(immediate = true)
79public final class OpenstackSwitchingHandler {
80
81 private final Logger log = getLogger(getClass());
82
83 private static final String ERR_SET_FLOWS = "Failed to set flows for %s: ";
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected CoreService coreService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected MastershipService mastershipService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected DeviceService deviceService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sanghodc375372017-06-08 10:41:30 +090095 protected OpenstackFlowRuleService osFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090096
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected InstancePortService instancePortService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected OpenstackNetworkService osNetworkService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected OpenstackNodeService osNodeService;
105
106 private final ExecutorService eventExecutor = newSingleThreadExecutor(
107 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
108 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
Frank Wangf9571662017-06-06 18:01:29 +0800109 private final InternalOpenstackNetworkListener osNetworkListener =
110 new InternalOpenstackNetworkListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900111 private ApplicationId appId;
112
113 @Activate
114 protected void activate() {
115 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
116 instancePortService.addListener(instancePortListener);
Frank Wangf9571662017-06-06 18:01:29 +0800117 osNetworkService.addListener(osNetworkListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900118
119 log.info("Started");
120 }
121
122 @Deactivate
123 protected void deactivate() {
124 instancePortService.removeListener(instancePortListener);
Frank Wangf9571662017-06-06 18:01:29 +0800125 osNetworkService.removeListener(osNetworkListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900126 eventExecutor.shutdown();
127
128 log.info("Stopped");
129 }
130
131 private void setNetworkRules(InstancePort instPort, boolean install) {
daniel parka792cf72017-04-14 16:25:35 +0900132 switch (osNetworkService.network(instPort.networkId()).getNetworkType()) {
133 case VXLAN:
134 setTunnelTagFlowRules(instPort, install);
135 setForwardingRules(instPort, install);
136 break;
137 case VLAN:
138 setVlanTagFlowRules(instPort, install);
139 setForwardingRulesForVlan(instPort, install);
140 break;
141 default:
142 break;
143 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900144 }
145
146 private void setForwardingRules(InstancePort instPort, boolean install) {
147 // switching rules for the instPorts in the same node
148 TrafficSelector selector = DefaultTrafficSelector.builder()
149 .matchEthType(Ethernet.TYPE_IPV4)
150 .matchIPDst(instPort.ipAddress().toIpPrefix())
151 .matchTunnelId(getVni(instPort))
152 .build();
153
154 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
155 .setEthDst(instPort.macAddress())
156 .setOutput(instPort.portNumber())
157 .build();
158
sanghodc375372017-06-08 10:41:30 +0900159 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900160 appId,
161 instPort.deviceId(),
162 selector,
163 treatment,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900164 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900165 FORWARDING_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900166 install);
167
168 // switching rules for the instPorts in the remote node
Hyunsun Moon0d457362017-06-27 17:19:41 +0900169 OpenstackNode localNode = osNodeService.node(instPort.deviceId());
170 if (localNode == null) {
171 final String error = String.format("Cannot find openstack node for %s",
172 instPort.deviceId());
173 throw new IllegalStateException(error);
174 }
175 osNodeService.completeNodes(COMPUTE).stream()
176 .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
177 .forEach(remoteNode -> {
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900178 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
179 .extension(buildExtension(
180 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900181 remoteNode.intgBridge(),
182 localNode.dataIp().getIp4Address()),
183 remoteNode.intgBridge())
184 .setOutput(remoteNode.tunnelPortNum())
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900185 .build();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900186
sanghodc375372017-06-08 10:41:30 +0900187 osFlowRuleService.setRule(
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900188 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900189 remoteNode.intgBridge(),
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900190 selector,
191 treatmentToRemote,
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900192 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900193 FORWARDING_TABLE,
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900194 install);
195 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900196 }
197
daniel parka792cf72017-04-14 16:25:35 +0900198 private void setForwardingRulesForVlan(InstancePort instPort, boolean install) {
199 // switching rules for the instPorts in the same node
200 TrafficSelector selector = DefaultTrafficSelector.builder()
201 .matchEthType(Ethernet.TYPE_IPV4)
202 .matchIPDst(instPort.ipAddress().toIpPrefix())
203 .matchVlanId(getVlanId(instPort))
204 .build();
205
206 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
207 .popVlan()
208 .setEthDst(instPort.macAddress())
209 .setOutput(instPort.portNumber())
210 .build();
211
sanghodc375372017-06-08 10:41:30 +0900212 osFlowRuleService.setRule(
daniel parka792cf72017-04-14 16:25:35 +0900213 appId,
214 instPort.deviceId(),
215 selector,
216 treatment,
daniel parka792cf72017-04-14 16:25:35 +0900217 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900218 FORWARDING_TABLE,
daniel parka792cf72017-04-14 16:25:35 +0900219 install);
220
221 // switching rules for the instPorts in the remote node
Hyunsun Moon0d457362017-06-27 17:19:41 +0900222 osNodeService.completeNodes(COMPUTE).stream()
223 .filter(remoteNode -> !remoteNode.intgBridge().equals(instPort.deviceId()) &&
224 remoteNode.vlanIntf() != null)
225 .forEach(remoteNode -> {
daniel parka792cf72017-04-14 16:25:35 +0900226 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900227 .setOutput(remoteNode.vlanPortNum())
daniel parka792cf72017-04-14 16:25:35 +0900228 .build();
229
sanghodc375372017-06-08 10:41:30 +0900230 osFlowRuleService.setRule(
daniel parka792cf72017-04-14 16:25:35 +0900231 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900232 remoteNode.intgBridge(),
daniel parka792cf72017-04-14 16:25:35 +0900233 selector,
234 treatmentToRemote,
daniel parka792cf72017-04-14 16:25:35 +0900235 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900236 FORWARDING_TABLE,
daniel parka792cf72017-04-14 16:25:35 +0900237 install);
238 });
daniel parka792cf72017-04-14 16:25:35 +0900239 }
240
Hyunsun Moon44aac662017-02-18 02:07:01 +0900241 private void setTunnelTagFlowRules(InstancePort instPort, boolean install) {
242 TrafficSelector selector = DefaultTrafficSelector.builder()
243 .matchEthType(Ethernet.TYPE_IPV4)
244 .matchInPort(instPort.portNumber())
245 .build();
246
247 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
248 .setTunnelId(getVni(instPort))
sanghodc375372017-06-08 10:41:30 +0900249 .transition(ACL_TABLE)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900250 .build();
251
sanghodc375372017-06-08 10:41:30 +0900252 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900253 appId,
254 instPort.deviceId(),
255 selector,
256 treatment,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900257 PRIORITY_TUNNEL_TAG_RULE,
sanghodc375372017-06-08 10:41:30 +0900258 SRC_VNI_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900259 install);
260 }
261
daniel parka792cf72017-04-14 16:25:35 +0900262 private void setVlanTagFlowRules(InstancePort instPort, boolean install) {
263 TrafficSelector selector = DefaultTrafficSelector.builder()
264 .matchEthType(Ethernet.TYPE_IPV4)
265 .matchInPort(instPort.portNumber())
266 .build();
267
268 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
269 .pushVlan()
270 .setVlanId(getVlanId(instPort))
sanghodc375372017-06-08 10:41:30 +0900271 .transition(ACL_TABLE)
daniel parka792cf72017-04-14 16:25:35 +0900272 .build();
273
sanghodc375372017-06-08 10:41:30 +0900274 osFlowRuleService.setRule(
daniel parka792cf72017-04-14 16:25:35 +0900275 appId,
276 instPort.deviceId(),
277 selector,
278 treatment,
daniel parka792cf72017-04-14 16:25:35 +0900279 PRIORITY_TUNNEL_TAG_RULE,
sanghodc375372017-06-08 10:41:30 +0900280 SRC_VNI_TABLE,
daniel parka792cf72017-04-14 16:25:35 +0900281 install);
282
283 }
284
Frank Wangf9571662017-06-06 18:01:29 +0800285 private void setNetworkAdminRules(Network network, boolean install) {
286 TrafficSelector selector;
287 if (network.getNetworkType() == NetworkType.VXLAN) {
288
289 selector = DefaultTrafficSelector.builder()
290 .matchTunnelId(Long.valueOf(network.getProviderSegID()))
291 .build();
292 } else {
293 selector = DefaultTrafficSelector.builder()
294 .matchVlanId(VlanId.vlanId(network.getProviderSegID()))
295 .build();
296 }
297
298 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
299 .drop()
300 .build();
301
302 osNodeService.completeNodes().stream()
303 .filter(osNode -> osNode.type() == COMPUTE)
304 .forEach(osNode -> {
305 osFlowRuleService.setRule(
306 appId,
307 osNode.intgBridge(),
308 selector,
309 treatment,
310 PRIORITY_ADMIN_RULE,
311 ACL_TABLE,
312 install);
313 });
314 }
315
316 private void setPortAdminRules(Port port, boolean install) {
317 InstancePort instancePort = instancePortService.instancePort(MacAddress.valueOf(port.getMacAddress()));
318 TrafficSelector selector = DefaultTrafficSelector.builder()
319 .matchInPort(instancePort.portNumber())
320 .build();
321
322 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
323 .drop()
324 .build();
325
326 osFlowRuleService.setRule(
327 appId,
328 instancePort.deviceId(),
329 selector,
330 treatment,
331 PRIORITY_ADMIN_RULE,
332 SRC_VNI_TABLE,
333 install);
334 }
335
daniel parka792cf72017-04-14 16:25:35 +0900336 private VlanId getVlanId(InstancePort instPort) {
337 Network osNet = osNetworkService.network(instPort.networkId());
338
339 if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
340 final String error = String.format(
341 ERR_SET_FLOWS + "Failed to get VNI for %s",
342 instPort, osNet == null ? "<none>" : osNet.getName());
343 throw new IllegalStateException(error);
344 }
345
346 return VlanId.vlanId(osNet.getProviderSegID());
347 }
348
349
Hyunsun Moon44aac662017-02-18 02:07:01 +0900350 private Long getVni(InstancePort instPort) {
351 Network osNet = osNetworkService.network(instPort.networkId());
352 if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
353 final String error = String.format(
354 ERR_SET_FLOWS + "Failed to get VNI for %s",
rohit8e5c8992017-03-29 19:15:30 +0530355 instPort, osNet == null ? "<none>" : osNet.getName());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900356 throw new IllegalStateException(error);
357 }
358 return Long.valueOf(osNet.getProviderSegID());
359 }
360
361 private class InternalInstancePortListener implements InstancePortListener {
362
363 @Override
364 public boolean isRelevant(InstancePortEvent event) {
365 InstancePort instPort = event.subject();
366 return mastershipService.isLocalMaster(instPort.deviceId());
367 }
368
369 @Override
370 public void event(InstancePortEvent event) {
371 InstancePort instPort = event.subject();
372 switch (event.type()) {
373 case OPENSTACK_INSTANCE_PORT_UPDATED:
374 case OPENSTACK_INSTANCE_PORT_DETECTED:
375 eventExecutor.execute(() -> {
376 log.info("Instance port detected MAC:{} IP:{}",
377 instPort.macAddress(),
378 instPort.ipAddress());
379 instPortDetected(event.subject());
380 });
381 break;
382 case OPENSTACK_INSTANCE_PORT_VANISHED:
383 eventExecutor.execute(() -> {
384 log.info("Instance port vanished MAC:{} IP:{}",
385 instPort.macAddress(),
386 instPort.ipAddress());
387 instPortRemoved(event.subject());
388 });
389 break;
390 default:
391 break;
392 }
393 }
394
395 private void instPortDetected(InstancePort instPort) {
396 setNetworkRules(instPort, true);
397 // TODO add something else if needed
398 }
399
400 private void instPortRemoved(InstancePort instPort) {
401 setNetworkRules(instPort, false);
402 // TODO add something else if needed
403 }
404 }
Frank Wangf9571662017-06-06 18:01:29 +0800405
406 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
407
408 @Override
409 public boolean isRelevant(OpenstackNetworkEvent event) {
410 return !(event.subject() == null && event.port() == null);
411 }
412
413 @Override
414 public void event(OpenstackNetworkEvent event) {
415
416 if ((event.type() == OPENSTACK_NETWORK_CREATED ||
417 event.type() == OPENSTACK_NETWORK_UPDATED) && !event.subject().isAdminStateUp()) {
418 setNetworkAdminRules(event.subject(), true);
419 } else if ((event.type() == OPENSTACK_NETWORK_UPDATED && event.subject().isAdminStateUp()) ||
420 (event.type() == OPENSTACK_NETWORK_REMOVED && !event.subject().isAdminStateUp())) {
421 setNetworkAdminRules(event.subject(), false);
422 }
423
424 if ((event.type() == OPENSTACK_PORT_CREATED ||
425 event.type() == OPENSTACK_PORT_UPDATED) && !event.port().isAdminStateUp()) {
426 setPortAdminRules(event.port(), true);
427 } else if ((event.type() == OPENSTACK_PORT_UPDATED && event.port().isAdminStateUp()) ||
428 (event.type() == OPENSTACK_PORT_REMOVED && !event.port().isAdminStateUp())) {
429 setPortAdminRules(event.port(), false);
430 }
431 }
432 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900433}