blob: f661f5aa226ba2cb4d0cd6b8106ea44c571a2bb7 [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
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
Daniel Parkc4d06402018-05-28 15:57:37 +090021import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
Daniel Parkc4d06402018-05-28 15:57:37 +090023import org.onosproject.cfg.ComponentConfigService;
24import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.LeadershipService;
26import org.onosproject.cluster.NodeId;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.mastership.MastershipService;
Daniel Park95f73312018-07-31 15:48:34 +090030import org.onosproject.net.Device;
Daniel Parkc4d06402018-05-28 15:57:37 +090031import org.onosproject.net.DeviceId;
32import org.onosproject.net.device.DeviceService;
33import org.onosproject.openstacknetworking.api.InstancePort;
34import org.onosproject.openstacknetworking.api.InstancePortService;
35import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
36import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
37import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Daniel Park95f73312018-07-31 15:48:34 +090038import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
Daniel Parkc4d06402018-05-28 15:57:37 +090039import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Park95f73312018-07-31 15:48:34 +090040import org.onosproject.openstacknode.api.OpenstackNodeEvent;
41import org.onosproject.openstacknode.api.OpenstackNodeListener;
Daniel Parkc4d06402018-05-28 15:57:37 +090042import org.onosproject.openstacknode.api.OpenstackNodeService;
43import org.onosproject.ovsdb.controller.OvsdbController;
44import org.openstack4j.model.network.Port;
45import org.openstack4j.model.network.State;
Daniel Parkc4d06402018-05-28 15:57:37 +090046import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
48
Daniel Park95f73312018-07-31 15:48:34 +090049import java.util.List;
Daniel Parkc4d06402018-05-28 15:57:37 +090050import java.util.Objects;
51import java.util.Optional;
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;
Daniel Parkc4d06402018-05-28 15:57:37 +090055import static org.onosproject.openstacknetworking.api.Constants.DIRECT;
56import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Daniel Parkec9d1132018-08-19 11:18:03 +090057import static org.onosproject.openstacknetworking.api.Constants.UNSUPPORTED_VENDOR;
Daniel Parkc4d06402018-05-28 15:57:37 +090058import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getIntfNameFromPciAddress;
Daniel Park95f73312018-07-31 15:48:34 +090059import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.hasIntfAleadyInDevice;
Daniel Parkc4d06402018-05-28 15:57:37 +090060import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Daniel Park95f73312018-07-31 15:48:34 +090061import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Daniel Parkc4d06402018-05-28 15:57:37 +090062
63@Component(immediate = true)
64public final class OpenStackSwitchingDirectPortProvider {
65 private final Logger log = LoggerFactory.getLogger(getClass());
66
Daniel Parkc4d06402018-05-28 15:57:37 +090067 private static final String UNBOUND = "unbound";
68 private static final String PORT_NAME = "portName";
Daniel Park95f73312018-07-31 15:48:34 +090069 private static final long SLEEP_MS = 3000;
Daniel Parkc4d06402018-05-28 15:57:37 +090070
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected CoreService coreService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected OpenstackNetworkService osNetworkService;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected OpenstackNodeService osNodeService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected LeadershipService leadershipService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected ClusterService clusterService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected OvsdbController ovsdbController;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected DeviceService deviceService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected InstancePortService instancePortService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected ComponentConfigService componentConfigService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected MastershipService mastershipService;
100
Daniel Parkc4d06402018-05-28 15:57:37 +0900101 private final OpenstackNetworkListener openstackNetworkListener = new InternalOpenstackNetworkListener();
Daniel Park95f73312018-07-31 15:48:34 +0900102 private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
Daniel Parkc4d06402018-05-28 15:57:37 +0900103
104 private NodeId localNodeId;
105 private ApplicationId appId;
106
107 @Activate
108 void activate() {
109 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
110 localNodeId = clusterService.getLocalNode().id();
111 leadershipService.runForLeadership(appId.name());
112 osNetworkService.addListener(openstackNetworkListener);
113 componentConfigService.registerProperties(getClass());
Daniel Park95f73312018-07-31 15:48:34 +0900114 osNodeService.addListener(internalNodeListener);
Daniel Parkc4d06402018-05-28 15:57:37 +0900115
116 log.info("Started");
117 }
118
119 @Deactivate
120 void deactivate() {
121 leadershipService.withdraw(appId.name());
122 osNetworkService.removeListener(openstackNetworkListener);
123 componentConfigService.unregisterProperties(getClass(), false);
Daniel Park95f73312018-07-31 15:48:34 +0900124 osNodeService.removeListener(internalNodeListener);
Daniel Parkc4d06402018-05-28 15:57:37 +0900125
126 log.info("Stopped");
127 }
128
Daniel Parkc4d06402018-05-28 15:57:37 +0900129 private void processPortAdded(Port port) {
130 if (!port.getvNicType().equals(DIRECT)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900131 return;
132 } else if (!port.isAdminStateUp() || port.getVifType().equals(UNBOUND)) {
133 log.trace("processPortAdded skipped because of status: {}, adminStateUp: {}, vifType: {}",
134 port.getState(), port.isAdminStateUp(), port.getVifType());
135 return;
136 } else {
Daniel Parkc4d06402018-05-28 15:57:37 +0900137 Optional<OpenstackNode> osNode = osNodeService.completeNodes(COMPUTE).stream()
138 .filter(node -> node.hostname().equals(port.getHostId()))
139 .findAny();
140 if (!osNode.isPresent()) {
141 log.error("processPortAdded failed because openstackNode doesn't exist that matches hostname {}",
142 port.getHostId());
143 return;
144 }
145 log.trace("Retrieved openstackNode: {}", osNode.get().toString());
146
147 String intfName = getIntfNameFromPciAddress(port);
148 if (intfName == null) {
149 log.error("Failed to execute processPortAdded because of null interface name");
150 return;
Daniel Parkec9d1132018-08-19 11:18:03 +0900151 } else if (intfName.equals(UNSUPPORTED_VENDOR)) {
152 log.warn("Failed to execute processPortAdded because of unsupported vendor for ovs-based sr-iov");
153 return;
Daniel Parkc4d06402018-05-28 15:57:37 +0900154 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900155 log.trace("Retrieved interface name: {}", intfName);
156
Daniel Park95f73312018-07-31 15:48:34 +0900157 try {
158 //If a VF port has been already added to the device for some reason, we remove it first,
159 //and the add VF so that other handlers run their logic.
160 if (hasIntfAleadyInDevice(osNode.get().intgBridge(),
161 intfName, deviceService)) {
162 log.trace("Device {} has already has VF interface {}, so remove first.",
163 osNode.get().intgBridge(),
164 intfName);
165 osNodeService.removeVfPort(osNode.get(), intfName);
166 //we wait 3000ms because the ovsdb client can't deal with removal/add at the same time.
167 sleep(SLEEP_MS);
168 }
169 } catch (InterruptedException e) {
170 log.error("Exception occurred because of {}", e.toString());
171 }
172
Daniel Parkc4d06402018-05-28 15:57:37 +0900173 osNodeService.addVfPort(osNode.get(), intfName);
174 }
175 }
176
177 private void processPortRemoved(Port port) {
178 if (!port.getvNicType().equals(DIRECT)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900179 return;
180 } else if (instancePortService.instancePort(port.getId()) == null) {
181 log.trace("processPortRemoved skipped because no instance port exist for portId: {}", port.getId());
182 return;
183 } else {
184 InstancePort instancePort = instancePortService.instancePort(port.getId());
185 if (instancePort == null) {
186 return;
187 }
188 DeviceId deviceId = instancePort.deviceId();
189 if (deviceId == null) {
190 return;
191 }
192 OpenstackNode osNode = osNodeService.node(deviceId);
193 if (osNode == null) {
194 return;
195 }
196
197 Optional<org.onosproject.net.Port> removedPort = deviceService.getPorts(deviceId).stream()
198 .filter(p -> Objects.equals(p.number(), instancePort.portNumber()))
199 .findAny();
200
201 if (!removedPort.isPresent()) {
202 log.error("Failed to execute processPortAdded because port number doesn't exist");
203 return;
204 }
205
206 String intfName = removedPort.get().annotations().value(PORT_NAME);
207
208 if (intfName == null) {
209 log.error("Failed to execute processPortAdded because of null interface name");
210 return;
211 }
212 log.trace("Retrieved interface name: {}", intfName);
213
214 osNodeService.removeVfPort(osNode, intfName);
215 }
216 }
217
218 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
219 @Override
220 public boolean isRelevant(OpenstackNetworkEvent event) {
221 // do not allow to proceed without leadership
222 NodeId leader = leadershipService.getLeader(appId.name());
223 if (!Objects.equals(localNodeId, leader)) {
224 return false;
225 }
226 return true;
227 }
228
229 @Override
230 public void event(OpenstackNetworkEvent event) {
231 switch (event.type()) {
232 case OPENSTACK_PORT_UPDATED:
233 if (event.port().getState() == State.DOWN) {
234 processPortRemoved(event.port());
235 } else {
236 processPortAdded(event.port());
237 }
238 break;
239 case OPENSTACK_PORT_REMOVED:
240 processPortRemoved(event.port());
241 break;
242 default:
243 break;
244
245 }
246 }
247 }
Daniel Park95f73312018-07-31 15:48:34 +0900248
249 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
250
251 @Override
252 public boolean isRelevant(OpenstackNodeEvent event) {
253
254 if (event.subject().type() == CONTROLLER) {
255 return false;
256 }
257 // do not allow to proceed without mastership
258 Device device = deviceService.getDevice(event.subject().intgBridge());
259 if (device == null) {
260 return false;
261 }
262 return mastershipService.isLocalMaster(device.id());
263 }
264
265 @Override
266 public void event(OpenstackNodeEvent event) {
267 OpenstackNode osNode = event.subject();
268
269 switch (event.type()) {
270 case OPENSTACK_NODE_COMPLETE:
271 log.info("COMPLETE node {} is detected", osNode.hostname());
272 processComputeState(event.subject());
273
274 break;
275 case OPENSTACK_NODE_INCOMPLETE:
276 case OPENSTACK_NODE_CREATED:
277 case OPENSTACK_NODE_UPDATED:
278 case OPENSTACK_NODE_REMOVED:
279 // not reacts to the events other than complete and incomplete states
280 break;
281 default:
282 break;
283 }
284 }
285
286 private void processComputeState(OpenstackNode node) {
287 List<Port> ports = osNetworkService.ports().stream()
288 .filter(port -> port.getvNicType().equals(DIRECT))
289 .filter(port -> !port.getVifType().equals(UNBOUND))
290 .filter(port -> port.getHostId().equals(node.hostname()))
291 .collect(Collectors.toList());
292
293 ports.forEach(port -> {
294 addIntfToDevice(node, port);
295 });
Daniel Park95f73312018-07-31 15:48:34 +0900296 }
297
298 private void addIntfToDevice(OpenstackNode node, Port port) {
299 String intfName = OpenstackNetworkingUtil.getIntfNameFromPciAddress(port);
300 if (intfName == null) {
301 log.error("Failed to retrieve interface name from a port {}", port.getId());
302 }
303
304 if (!hasIntfAleadyInDevice(node.intgBridge(), intfName, deviceService)) {
305 log.debug("Port {} is bound to the interface {} but not added in the bridge {}. Adding it..",
306 port.getId(),
307 intfName,
308 node.intgBridge());
309 osNodeService.addVfPort(node, intfName);
310 }
311 }
312 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900313}