blob: 76c01976b0d1b899761f336ac6f68d86db5c3424 [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;
21import org.apache.felix.scr.annotations.Modified;
22import org.apache.felix.scr.annotations.Property;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onlab.util.Tools;
26import org.onosproject.cfg.ComponentConfigService;
27import org.onosproject.cluster.ClusterService;
28import org.onosproject.cluster.LeadershipService;
29import org.onosproject.cluster.NodeId;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.mastership.MastershipService;
Daniel Park95f73312018-07-31 15:48:34 +090033import org.onosproject.net.Device;
Daniel Parkc4d06402018-05-28 15:57:37 +090034import org.onosproject.net.DeviceId;
35import org.onosproject.net.device.DeviceService;
36import org.onosproject.openstacknetworking.api.InstancePort;
37import org.onosproject.openstacknetworking.api.InstancePortService;
38import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
39import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
40import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Daniel Park95f73312018-07-31 15:48:34 +090041import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
Daniel Parkc4d06402018-05-28 15:57:37 +090042import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Park95f73312018-07-31 15:48:34 +090043import org.onosproject.openstacknode.api.OpenstackNodeEvent;
44import org.onosproject.openstacknode.api.OpenstackNodeListener;
Daniel Parkc4d06402018-05-28 15:57:37 +090045import org.onosproject.openstacknode.api.OpenstackNodeService;
46import org.onosproject.ovsdb.controller.OvsdbController;
47import org.openstack4j.model.network.Port;
48import org.openstack4j.model.network.State;
49import org.osgi.service.component.ComponentContext;
50import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
53import java.util.Dictionary;
Daniel Park95f73312018-07-31 15:48:34 +090054import java.util.List;
Daniel Parkc4d06402018-05-28 15:57:37 +090055import java.util.Objects;
56import java.util.Optional;
Daniel Park95f73312018-07-31 15:48:34 +090057import java.util.stream.Collectors;
Daniel Parkc4d06402018-05-28 15:57:37 +090058
Daniel Park95f73312018-07-31 15:48:34 +090059import static java.lang.Thread.sleep;
Daniel Parkc4d06402018-05-28 15:57:37 +090060import static org.onosproject.openstacknetworking.api.Constants.DIRECT;
61import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
62import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getIntfNameFromPciAddress;
Daniel Park95f73312018-07-31 15:48:34 +090063import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.hasIntfAleadyInDevice;
Daniel Parkc4d06402018-05-28 15:57:37 +090064import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Daniel Park95f73312018-07-31 15:48:34 +090065import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Daniel Parkc4d06402018-05-28 15:57:37 +090066
67@Component(immediate = true)
68public final class OpenStackSwitchingDirectPortProvider {
69 private final Logger log = LoggerFactory.getLogger(getClass());
70
71 private static final String OVSDB_PORT = "ovsdbPortNum";
72 private static final int DEFAULT_OVSDB_PORT = 6640;
73 private static final String UNBOUND = "unbound";
74 private static final String PORT_NAME = "portName";
Daniel Park95f73312018-07-31 15:48:34 +090075 private static final long SLEEP_MS = 3000;
Daniel Parkc4d06402018-05-28 15:57:37 +090076
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected CoreService coreService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected OpenstackNetworkService osNetworkService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected OpenstackNodeService osNodeService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected LeadershipService leadershipService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected ClusterService clusterService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected OvsdbController ovsdbController;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected DeviceService deviceService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected InstancePortService instancePortService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected ComponentConfigService componentConfigService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected MastershipService mastershipService;
106
107 @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
108 label = "OVSDB server listen port")
109 private int ovsdbPort = DEFAULT_OVSDB_PORT;
110
111 private final OpenstackNetworkListener openstackNetworkListener = new InternalOpenstackNetworkListener();
Daniel Park95f73312018-07-31 15:48:34 +0900112 private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
Daniel Parkc4d06402018-05-28 15:57:37 +0900113
114 private NodeId localNodeId;
115 private ApplicationId appId;
116
117 @Activate
118 void activate() {
119 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
120 localNodeId = clusterService.getLocalNode().id();
121 leadershipService.runForLeadership(appId.name());
122 osNetworkService.addListener(openstackNetworkListener);
123 componentConfigService.registerProperties(getClass());
Daniel Park95f73312018-07-31 15:48:34 +0900124 osNodeService.addListener(internalNodeListener);
Daniel Parkc4d06402018-05-28 15:57:37 +0900125
126 log.info("Started");
127 }
128
129 @Deactivate
130 void deactivate() {
131 leadershipService.withdraw(appId.name());
132 osNetworkService.removeListener(openstackNetworkListener);
133 componentConfigService.unregisterProperties(getClass(), false);
Daniel Park95f73312018-07-31 15:48:34 +0900134 osNodeService.removeListener(internalNodeListener);
Daniel Parkc4d06402018-05-28 15:57:37 +0900135
136 log.info("Stopped");
137 }
138
139
140 @Modified
141 protected void modified(ComponentContext context) {
142 Dictionary<?, ?> properties = context.getProperties();
143 int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
144 if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
145 ovsdbPort = updatedOvsdbPort;
146 }
147
148 log.info("Modified");
149 }
150 private void processPortAdded(Port port) {
151 if (!port.getvNicType().equals(DIRECT)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900152 return;
153 } else if (!port.isAdminStateUp() || port.getVifType().equals(UNBOUND)) {
154 log.trace("processPortAdded skipped because of status: {}, adminStateUp: {}, vifType: {}",
155 port.getState(), port.isAdminStateUp(), port.getVifType());
156 return;
157 } else {
Daniel Parkc4d06402018-05-28 15:57:37 +0900158 Optional<OpenstackNode> osNode = osNodeService.completeNodes(COMPUTE).stream()
159 .filter(node -> node.hostname().equals(port.getHostId()))
160 .findAny();
161 if (!osNode.isPresent()) {
162 log.error("processPortAdded failed because openstackNode doesn't exist that matches hostname {}",
163 port.getHostId());
164 return;
165 }
166 log.trace("Retrieved openstackNode: {}", osNode.get().toString());
167
168 String intfName = getIntfNameFromPciAddress(port);
169 if (intfName == null) {
170 log.error("Failed to execute processPortAdded because of null interface name");
171 return;
172 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900173 log.trace("Retrieved interface name: {}", intfName);
174
Daniel Park95f73312018-07-31 15:48:34 +0900175 try {
176 //If a VF port has been already added to the device for some reason, we remove it first,
177 //and the add VF so that other handlers run their logic.
178 if (hasIntfAleadyInDevice(osNode.get().intgBridge(),
179 intfName, deviceService)) {
180 log.trace("Device {} has already has VF interface {}, so remove first.",
181 osNode.get().intgBridge(),
182 intfName);
183 osNodeService.removeVfPort(osNode.get(), intfName);
184 //we wait 3000ms because the ovsdb client can't deal with removal/add at the same time.
185 sleep(SLEEP_MS);
186 }
187 } catch (InterruptedException e) {
188 log.error("Exception occurred because of {}", e.toString());
189 }
190
Daniel Parkc4d06402018-05-28 15:57:37 +0900191 osNodeService.addVfPort(osNode.get(), intfName);
192 }
193 }
194
195 private void processPortRemoved(Port port) {
196 if (!port.getvNicType().equals(DIRECT)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900197 return;
198 } else if (instancePortService.instancePort(port.getId()) == null) {
199 log.trace("processPortRemoved skipped because no instance port exist for portId: {}", port.getId());
200 return;
201 } else {
202 InstancePort instancePort = instancePortService.instancePort(port.getId());
203 if (instancePort == null) {
204 return;
205 }
206 DeviceId deviceId = instancePort.deviceId();
207 if (deviceId == null) {
208 return;
209 }
210 OpenstackNode osNode = osNodeService.node(deviceId);
211 if (osNode == null) {
212 return;
213 }
214
215 Optional<org.onosproject.net.Port> removedPort = deviceService.getPorts(deviceId).stream()
216 .filter(p -> Objects.equals(p.number(), instancePort.portNumber()))
217 .findAny();
218
219 if (!removedPort.isPresent()) {
220 log.error("Failed to execute processPortAdded because port number doesn't exist");
221 return;
222 }
223
224 String intfName = removedPort.get().annotations().value(PORT_NAME);
225
226 if (intfName == null) {
227 log.error("Failed to execute processPortAdded because of null interface name");
228 return;
229 }
230 log.trace("Retrieved interface name: {}", intfName);
231
232 osNodeService.removeVfPort(osNode, intfName);
233 }
234 }
235
236 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
237 @Override
238 public boolean isRelevant(OpenstackNetworkEvent event) {
239 // do not allow to proceed without leadership
240 NodeId leader = leadershipService.getLeader(appId.name());
241 if (!Objects.equals(localNodeId, leader)) {
242 return false;
243 }
244 return true;
245 }
246
247 @Override
248 public void event(OpenstackNetworkEvent event) {
249 switch (event.type()) {
250 case OPENSTACK_PORT_UPDATED:
251 if (event.port().getState() == State.DOWN) {
252 processPortRemoved(event.port());
253 } else {
254 processPortAdded(event.port());
255 }
256 break;
257 case OPENSTACK_PORT_REMOVED:
258 processPortRemoved(event.port());
259 break;
260 default:
261 break;
262
263 }
264 }
265 }
Daniel Park95f73312018-07-31 15:48:34 +0900266
267 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
268
269 @Override
270 public boolean isRelevant(OpenstackNodeEvent event) {
271
272 if (event.subject().type() == CONTROLLER) {
273 return false;
274 }
275 // do not allow to proceed without mastership
276 Device device = deviceService.getDevice(event.subject().intgBridge());
277 if (device == null) {
278 return false;
279 }
280 return mastershipService.isLocalMaster(device.id());
281 }
282
283 @Override
284 public void event(OpenstackNodeEvent event) {
285 OpenstackNode osNode = event.subject();
286
287 switch (event.type()) {
288 case OPENSTACK_NODE_COMPLETE:
289 log.info("COMPLETE node {} is detected", osNode.hostname());
290 processComputeState(event.subject());
291
292 break;
293 case OPENSTACK_NODE_INCOMPLETE:
294 case OPENSTACK_NODE_CREATED:
295 case OPENSTACK_NODE_UPDATED:
296 case OPENSTACK_NODE_REMOVED:
297 // not reacts to the events other than complete and incomplete states
298 break;
299 default:
300 break;
301 }
302 }
303
304 private void processComputeState(OpenstackNode node) {
305 List<Port> ports = osNetworkService.ports().stream()
306 .filter(port -> port.getvNicType().equals(DIRECT))
307 .filter(port -> !port.getVifType().equals(UNBOUND))
308 .filter(port -> port.getHostId().equals(node.hostname()))
309 .collect(Collectors.toList());
310
311 ports.forEach(port -> {
312 addIntfToDevice(node, port);
313 });
314
315
316
317 }
318
319 private void addIntfToDevice(OpenstackNode node, Port port) {
320 String intfName = OpenstackNetworkingUtil.getIntfNameFromPciAddress(port);
321 if (intfName == null) {
322 log.error("Failed to retrieve interface name from a port {}", port.getId());
323 }
324
325 if (!hasIntfAleadyInDevice(node.intgBridge(), intfName, deviceService)) {
326 log.debug("Port {} is bound to the interface {} but not added in the bridge {}. Adding it..",
327 port.getId(),
328 intfName,
329 node.intgBridge());
330 osNodeService.addVfPort(node, intfName);
331 }
332 }
333 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900334}