blob: a73946f09310ec038defe3033b7f44f5e8e901b2 [file] [log] [blame]
Daniel Parkc4d06402018-05-28 15:57:37 +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.openstacknetworking.impl;
17
Daniel Parkc4d06402018-05-28 15:57:37 +090018import org.onosproject.cluster.ClusterService;
19import org.onosproject.cluster.LeadershipService;
20import org.onosproject.cluster.NodeId;
21import org.onosproject.core.ApplicationId;
22import org.onosproject.core.CoreService;
23import org.onosproject.mastership.MastershipService;
Daniel Park95f73312018-07-31 15:48:34 +090024import org.onosproject.net.Device;
Daniel Parkc4d06402018-05-28 15:57:37 +090025import org.onosproject.net.DeviceId;
26import org.onosproject.net.device.DeviceService;
27import org.onosproject.openstacknetworking.api.InstancePort;
28import org.onosproject.openstacknetworking.api.InstancePortService;
29import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
30import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
31import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Daniel Park95f73312018-07-31 15:48:34 +090032import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
Daniel Parkc4d06402018-05-28 15:57:37 +090033import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Park95f73312018-07-31 15:48:34 +090034import org.onosproject.openstacknode.api.OpenstackNodeEvent;
35import org.onosproject.openstacknode.api.OpenstackNodeListener;
Daniel Parkc4d06402018-05-28 15:57:37 +090036import org.onosproject.openstacknode.api.OpenstackNodeService;
Daniel Parkc4d06402018-05-28 15:57:37 +090037import org.openstack4j.model.network.Port;
38import org.openstack4j.model.network.State;
Jian Li34220ea2018-11-14 01:30:24 +090039import org.osgi.service.component.annotations.Activate;
40import org.osgi.service.component.annotations.Component;
41import org.osgi.service.component.annotations.Deactivate;
42import org.osgi.service.component.annotations.Reference;
43import org.osgi.service.component.annotations.ReferenceCardinality;
Daniel Parkc4d06402018-05-28 15:57:37 +090044import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46
Daniel Park95f73312018-07-31 15:48:34 +090047import java.util.List;
Daniel Parkc4d06402018-05-28 15:57:37 +090048import java.util.Objects;
49import java.util.Optional;
Jian Li34220ea2018-11-14 01:30:24 +090050import java.util.concurrent.ExecutorService;
51import java.util.concurrent.Executors;
Daniel Park95f73312018-07-31 15:48:34 +090052import java.util.stream.Collectors;
Daniel Parkc4d06402018-05-28 15:57:37 +090053
Daniel Park95f73312018-07-31 15:48:34 +090054import static java.lang.Thread.sleep;
Jian Li34220ea2018-11-14 01:30:24 +090055import static org.onlab.util.Tools.groupedThreads;
Daniel Parkc4d06402018-05-28 15:57:37 +090056import static org.onosproject.openstacknetworking.api.Constants.DIRECT;
57import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Daniel Parkec9d1132018-08-19 11:18:03 +090058import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Daniel Parkc4d06402018-05-28 15:57:37 +090059import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getIntfNameFromPciAddress;
Daniel Park95f73312018-07-31 15:48:34 +090060import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.hasIntfAleadyInDevice;
Jian Lia97cec42019-10-31 22:24:17 +090061import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isSmartNicCapable;
Daniel Parkc4d06402018-05-28 15:57:37 +090062import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Daniel Park95f73312018-07-31 15:48:34 +090063import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Daniel Parkc4d06402018-05-28 15:57:37 +090064
65@Component(immediate = true)
Jian Li6a47fd02018-11-27 21:51:03 +090066public class OpenStackSwitchingDirectPortProvider {
Daniel Parkc4d06402018-05-28 15:57:37 +090067 private final Logger log = LoggerFactory.getLogger(getClass());
68
Daniel Parkc4d06402018-05-28 15:57:37 +090069 private static final String UNBOUND = "unbound";
70 private static final String PORT_NAME = "portName";
Daniel Park95f73312018-07-31 15:48:34 +090071 private static final long SLEEP_MS = 3000;
Daniel Parkc4d06402018-05-28 15:57:37 +090072
Ray Milkeyd84f89b2018-08-17 14:54:17 -070073 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +090074 protected CoreService coreService;
75
Ray Milkeyd84f89b2018-08-17 14:54:17 -070076 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +090077 protected OpenstackNetworkService osNetworkService;
78
Ray Milkeyd84f89b2018-08-17 14:54:17 -070079 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +090080 protected OpenstackNodeService osNodeService;
81
Ray Milkeyd84f89b2018-08-17 14:54:17 -070082 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +090083 protected LeadershipService leadershipService;
84
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +090086 protected ClusterService clusterService;
87
Ray Milkeyd84f89b2018-08-17 14:54:17 -070088 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +090089 protected DeviceService deviceService;
90
Ray Milkeyd84f89b2018-08-17 14:54:17 -070091 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +090092 protected InstancePortService instancePortService;
93
Ray Milkeyd84f89b2018-08-17 14:54:17 -070094 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +090095 protected MastershipService mastershipService;
96
Jian Li5ecfd1a2018-12-10 11:41:03 +090097 private final ExecutorService executor = Executors.newSingleThreadExecutor(
98 groupedThreads(this.getClass().getSimpleName(), "direct-port-event"));
99 private final OpenstackNetworkListener
100 openstackNetworkListener = new InternalOpenstackNetworkListener();
101 private final InternalOpenstackNodeListener
102 internalNodeListener = new InternalOpenstackNodeListener();
Daniel Parkc4d06402018-05-28 15:57:37 +0900103
104 private NodeId localNodeId;
105 private ApplicationId appId;
106
107 @Activate
Jian Li6a47fd02018-11-27 21:51:03 +0900108 protected void activate() {
Daniel Parkc4d06402018-05-28 15:57:37 +0900109 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
110 localNodeId = clusterService.getLocalNode().id();
111 leadershipService.runForLeadership(appId.name());
112 osNetworkService.addListener(openstackNetworkListener);
Daniel Park95f73312018-07-31 15:48:34 +0900113 osNodeService.addListener(internalNodeListener);
Daniel Parkc4d06402018-05-28 15:57:37 +0900114
115 log.info("Started");
116 }
117
118 @Deactivate
Jian Li6a47fd02018-11-27 21:51:03 +0900119 protected void deactivate() {
Daniel Parkc4d06402018-05-28 15:57:37 +0900120 leadershipService.withdraw(appId.name());
121 osNetworkService.removeListener(openstackNetworkListener);
Daniel Park95f73312018-07-31 15:48:34 +0900122 osNodeService.removeListener(internalNodeListener);
Jian Li34220ea2018-11-14 01:30:24 +0900123 executor.shutdown();
Daniel Parkc4d06402018-05-28 15:57:37 +0900124
125 log.info("Stopped");
126 }
127
Daniel Parkc4d06402018-05-28 15:57:37 +0900128 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
Jian Li34220ea2018-11-14 01:30:24 +0900129 private boolean isRelevantHelper() {
130 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
Daniel Parkc4d06402018-05-28 15:57:37 +0900131 }
132
133 @Override
134 public void event(OpenstackNetworkEvent event) {
135 switch (event.type()) {
136 case OPENSTACK_PORT_UPDATED:
Jian Li6a47fd02018-11-27 21:51:03 +0900137 executor.execute(() -> processPortUpdate(event));
Daniel Parkc4d06402018-05-28 15:57:37 +0900138 break;
139 case OPENSTACK_PORT_REMOVED:
Jian Li6a47fd02018-11-27 21:51:03 +0900140 executor.execute(() -> processPortRemoval(event));
Daniel Parkc4d06402018-05-28 15:57:37 +0900141 break;
142 default:
143 break;
Daniel Parkc4d06402018-05-28 15:57:37 +0900144 }
145 }
Daniel Parkff178ba2018-11-23 15:57:24 +0900146
Jian Li6a47fd02018-11-27 21:51:03 +0900147 private void processPortUpdate(OpenstackNetworkEvent event) {
148 if (!isRelevantHelper()) {
149 return;
150 }
151
Jian Lia97cec42019-10-31 22:24:17 +0900152 if (!isSmartNicCapable(event.port())) {
153 return;
154 }
155
Jian Li6a47fd02018-11-27 21:51:03 +0900156 if (event.port().getState() == State.DOWN) {
157 removePort(event.port());
158 } else {
159 addPort(event.port());
160 }
161 }
162
163 private void processPortRemoval(OpenstackNetworkEvent event) {
164 if (!isRelevantHelper()) {
165 return;
166 }
167
Jian Lia97cec42019-10-31 22:24:17 +0900168 if (!isSmartNicCapable(event.port())) {
169 return;
170 }
171
Jian Li6a47fd02018-11-27 21:51:03 +0900172 removePort(event.port());
173 }
174
175 private void addPort(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900176 if (!port.getvNicType().equals(DIRECT)) {
177 return;
178 } else if (!port.isAdminStateUp() || port.getVifType().equals(UNBOUND)) {
Jian Li6a47fd02018-11-27 21:51:03 +0900179 log.trace("AddPort skipped because of status: {}, adminStateUp: {}, vifType: {}",
Daniel Parkff178ba2018-11-23 15:57:24 +0900180 port.getState(), port.isAdminStateUp(), port.getVifType());
181 return;
182 } else {
183 Optional<OpenstackNode> osNode = osNodeService.completeNodes(COMPUTE).stream()
184 .filter(node -> node.hostname().equals(port.getHostId()))
185 .findAny();
186 if (!osNode.isPresent()) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900187 log.error("AddPort failed because openstackNode doesn't " +
188 "exist that matches hostname {}",
Daniel Parkff178ba2018-11-23 15:57:24 +0900189 port.getHostId());
190 return;
191 }
192 log.trace("Retrieved openstackNode: {}", osNode.get().toString());
193
194 String intfName = getIntfNameFromPciAddress(port);
195 if (intfName == null) {
Jian Li6a47fd02018-11-27 21:51:03 +0900196 log.error("Failed to execute AddPort because of null interface name");
Daniel Parkff178ba2018-11-23 15:57:24 +0900197 return;
198 } else if (intfName.equals(UNSUPPORTED_VENDOR)) {
199 return;
200 }
201 log.trace("Retrieved interface name: {}", intfName);
202
203 try {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900204 // if the VF port has been already added to the device for
205 // some reason, we remove it first, and then add VF so that
206 // other handlers run their logic.
Daniel Parkff178ba2018-11-23 15:57:24 +0900207 if (hasIntfAleadyInDevice(osNode.get().intgBridge(),
208 intfName, deviceService)) {
209 log.trace("Device {} has already has VF interface {}, so remove first.",
210 osNode.get().intgBridge(),
211 intfName);
212 osNodeService.removeVfPort(osNode.get(), intfName);
Jian Li5ecfd1a2018-12-10 11:41:03 +0900213
214 // we wait 3000ms because the ovsdb client can't deal
215 // with removal/add at the same time.
Daniel Parkff178ba2018-11-23 15:57:24 +0900216 sleep(SLEEP_MS);
217 }
218 } catch (InterruptedException e) {
219 log.error("Exception occurred because of {}", e.toString());
220 }
221
222 osNodeService.addVfPort(osNode.get(), intfName);
223 }
224 }
225
Jian Li6a47fd02018-11-27 21:51:03 +0900226 private void removePort(Port port) {
Daniel Parkff178ba2018-11-23 15:57:24 +0900227 if (!port.getvNicType().equals(DIRECT)) {
228 return;
229 } else if (instancePortService.instancePort(port.getId()) == null) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900230 log.trace("RemovePort skipped because no instance port exist for portId: {}",
231 port.getId());
Daniel Parkff178ba2018-11-23 15:57:24 +0900232 return;
233 } else {
234 InstancePort instancePort = instancePortService.instancePort(port.getId());
235 if (instancePort == null) {
236 return;
237 }
238 DeviceId deviceId = instancePort.deviceId();
239 if (deviceId == null) {
240 return;
241 }
242 OpenstackNode osNode = osNodeService.node(deviceId);
243 if (osNode == null) {
244 return;
245 }
246
Jian Li5ecfd1a2018-12-10 11:41:03 +0900247 Optional<org.onosproject.net.Port> removedPort =
248 deviceService.getPorts(deviceId).stream()
Daniel Parkff178ba2018-11-23 15:57:24 +0900249 .filter(p -> Objects.equals(p.number(), instancePort.portNumber()))
250 .findAny();
251
252 if (!removedPort.isPresent()) {
Jian Li6a47fd02018-11-27 21:51:03 +0900253 log.error("Failed to execute RemovePort because port number doesn't exist");
Daniel Parkff178ba2018-11-23 15:57:24 +0900254 return;
255 }
256
257 String intfName = removedPort.get().annotations().value(PORT_NAME);
258
259 if (intfName == null) {
Jian Li6a47fd02018-11-27 21:51:03 +0900260 log.error("Failed to execute RemovePort because of null interface name");
Daniel Parkff178ba2018-11-23 15:57:24 +0900261 return;
262 }
263 log.trace("Retrieved interface name: {}", intfName);
264
265 osNodeService.removeVfPort(osNode, intfName);
266 }
267 }
268
Daniel Parkc4d06402018-05-28 15:57:37 +0900269 }
Daniel Park95f73312018-07-31 15:48:34 +0900270
271 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
272
273 @Override
274 public boolean isRelevant(OpenstackNodeEvent event) {
Jian Li34220ea2018-11-14 01:30:24 +0900275 return event.subject().type() != CONTROLLER;
276 }
Daniel Park95f73312018-07-31 15:48:34 +0900277
Jian Li34220ea2018-11-14 01:30:24 +0900278 private boolean isRelevantHelper(OpenstackNodeEvent event) {
Daniel Park95f73312018-07-31 15:48:34 +0900279 // do not allow to proceed without mastership
280 Device device = deviceService.getDevice(event.subject().intgBridge());
281 if (device == null) {
282 return false;
283 }
284 return mastershipService.isLocalMaster(device.id());
285 }
286
287 @Override
288 public void event(OpenstackNodeEvent event) {
289 OpenstackNode osNode = event.subject();
290
291 switch (event.type()) {
292 case OPENSTACK_NODE_COMPLETE:
Jian Li6a47fd02018-11-27 21:51:03 +0900293 executor.execute(() -> processNodeCompletion(event, osNode));
Daniel Park95f73312018-07-31 15:48:34 +0900294 break;
295 case OPENSTACK_NODE_INCOMPLETE:
296 case OPENSTACK_NODE_CREATED:
297 case OPENSTACK_NODE_UPDATED:
298 case OPENSTACK_NODE_REMOVED:
Daniel Park95f73312018-07-31 15:48:34 +0900299 default:
300 break;
301 }
302 }
303
Jian Li6a47fd02018-11-27 21:51:03 +0900304 private void processNodeCompletion(OpenstackNodeEvent event,
305 OpenstackNode osNode) {
306 log.info("COMPLETE node {} is detected", osNode.hostname());
307 if (!isRelevantHelper(event)) {
308 return;
309 }
310
311 processComputeState(event.subject());
312 }
313
Daniel Park95f73312018-07-31 15:48:34 +0900314 private void processComputeState(OpenstackNode node) {
315 List<Port> ports = osNetworkService.ports().stream()
316 .filter(port -> port.getvNicType().equals(DIRECT))
317 .filter(port -> !port.getVifType().equals(UNBOUND))
318 .filter(port -> port.getHostId().equals(node.hostname()))
Jian Lia97cec42019-10-31 22:24:17 +0900319 .filter(OpenstackNetworkingUtil::isSmartNicCapable)
Daniel Park95f73312018-07-31 15:48:34 +0900320 .collect(Collectors.toList());
321
Jian Li34220ea2018-11-14 01:30:24 +0900322 ports.forEach(port -> addIntfToDevice(node, port));
Daniel Park95f73312018-07-31 15:48:34 +0900323 }
324
325 private void addIntfToDevice(OpenstackNode node, Port port) {
326 String intfName = OpenstackNetworkingUtil.getIntfNameFromPciAddress(port);
327 if (intfName == null) {
328 log.error("Failed to retrieve interface name from a port {}", port.getId());
Daniel Parkc3e0c602018-08-30 21:27:25 +0900329 } else if (intfName.equals(UNSUPPORTED_VENDOR)) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900330 log.warn("Failed to retrieve interface name from a port {} " +
Jian Lia97cec42019-10-31 22:24:17 +0900331 "because of unsupported ovs-based sr-iov", port.getId());
Daniel Parkc3e0c602018-08-30 21:27:25 +0900332 return;
Daniel Park95f73312018-07-31 15:48:34 +0900333 }
334
335 if (!hasIntfAleadyInDevice(node.intgBridge(), intfName, deviceService)) {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900336 log.debug("Port {} is bound to the interface {} but not " +
337 "added in the bridge {}. Adding it..",
338 port.getId(),
339 intfName,
340 node.intgBridge());
Daniel Park95f73312018-07-31 15:48:34 +0900341 osNodeService.addVfPort(node, intfName);
342 }
343 }
344 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900345}