blob: d990fae9b8edd52b0217ad65dff1321291df814f [file] [log] [blame]
Hyunsun Moon0d457362017-06-27 17:19:41 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Hyunsun Moon0d457362017-06-27 17:19:41 +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 */
16package org.onosproject.openstacknode.impl;
17
18import com.google.common.base.Strings;
19import com.google.common.collect.ImmutableSet;
Daniel Parkc4d06402018-05-28 15:57:37 +090020import org.onlab.util.Tools;
Hyunsun Moon0d457362017-06-27 17:19:41 +090021import org.onosproject.cluster.ClusterService;
22import org.onosproject.cluster.LeadershipService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090023import org.onosproject.core.ApplicationId;
24import org.onosproject.core.CoreService;
25import org.onosproject.event.ListenerRegistry;
26import org.onosproject.net.DeviceId;
Daniel Parkc4d06402018-05-28 15:57:37 +090027import org.onosproject.net.device.DeviceService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090028import org.onosproject.openstacknode.api.OpenstackNode;
29import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
30import org.onosproject.openstacknode.api.OpenstackNodeEvent;
31import org.onosproject.openstacknode.api.OpenstackNodeListener;
32import org.onosproject.openstacknode.api.OpenstackNodeService;
33import org.onosproject.openstacknode.api.OpenstackNodeStore;
34import org.onosproject.openstacknode.api.OpenstackNodeStoreDelegate;
Daniel Parkc4d06402018-05-28 15:57:37 +090035import org.onosproject.ovsdb.controller.OvsdbController;
Jian Lie3141542018-08-13 18:05:43 +090036import org.onosproject.store.service.AtomicCounter;
Daniel Parkc4d06402018-05-28 15:57:37 +090037import org.onosproject.store.service.StorageService;
38import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070039import 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.Modified;
43import org.osgi.service.component.annotations.Reference;
44import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon0d457362017-06-27 17:19:41 +090045import org.slf4j.Logger;
46
Daniel Parkc4d06402018-05-28 15:57:37 +090047import java.util.Dictionary;
Hyunsun Moon0d457362017-06-27 17:19:41 +090048import java.util.Objects;
Jian Lie3141542018-08-13 18:05:43 +090049import java.util.Optional;
Hyunsun Moon0d457362017-06-27 17:19:41 +090050import java.util.Set;
51import java.util.concurrent.ExecutorService;
Daniel Park5a6a7102018-09-06 23:58:33 +090052import java.util.concurrent.TimeUnit;
Hyunsun Moon0d457362017-06-27 17:19:41 +090053import java.util.stream.Collectors;
54
55import static com.google.common.base.Preconditions.checkArgument;
56import static com.google.common.base.Preconditions.checkNotNull;
57import static java.util.concurrent.Executors.newSingleThreadExecutor;
Daniel Parkc4d06402018-05-28 15:57:37 +090058import static org.onlab.packet.TpPort.tpPort;
Hyunsun Moon0d457362017-06-27 17:19:41 +090059import static org.onlab.util.Tools.groupedThreads;
Daniel Park5a6a7102018-09-06 23:58:33 +090060import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Daniel Parkc4d06402018-05-28 15:57:37 +090061import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
Hyunsun Moon0d457362017-06-27 17:19:41 +090062import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
Jian Lie3141542018-08-13 18:05:43 +090063import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
Ray Milkey8e406512018-10-24 15:56:50 -070064import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT;
65import static org.onosproject.openstacknode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
Daniel Park5a6a7102018-09-06 23:58:33 +090066import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveSystemInterface;
Jian Lie3141542018-08-13 18:05:43 +090067import static org.onosproject.openstacknode.util.OpenstackNodeUtil.genDpid;
Daniel Parkc4d06402018-05-28 15:57:37 +090068import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +090069import static org.slf4j.LoggerFactory.getLogger;
70
71/**
72 * Service administering the inventory of openstack nodes.
73 */
Ray Milkey8e406512018-10-24 15:56:50 -070074@Component(
75 immediate = true,
76 service = { OpenstackNodeService.class, OpenstackNodeAdminService.class },
77 property = {
78 OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT
79 }
80)
Hyunsun Moon0d457362017-06-27 17:19:41 +090081public class OpenstackNodeManager extends ListenerRegistry<OpenstackNodeEvent, OpenstackNodeListener>
82 implements OpenstackNodeService, OpenstackNodeAdminService {
83
Jian Li5afbea42018-02-28 10:37:03 +090084 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +090085
86 private static final String MSG_NODE = "OpenStack node %s %s";
87 private static final String MSG_CREATED = "created";
88 private static final String MSG_UPDATED = "updated";
89 private static final String MSG_REMOVED = "removed";
90
Jian Lie3141542018-08-13 18:05:43 +090091 private static final String DEVICE_ID_COUNTER_NAME = "device-id-counter";
92
Hyunsun Moon0d457362017-06-27 17:19:41 +090093 private static final String ERR_NULL_NODE = "OpenStack node cannot be null";
94 private static final String ERR_NULL_HOSTNAME = "OpenStack node hostname cannot be null";
Jian Lie3141542018-08-13 18:05:43 +090095 private static final String ERR_NULL_DEVICE_ID = "OpenStack node device ID cannot be null";
96
97 private static final String NOT_DUPLICATED_MSG = "% cannot be duplicated";
Hyunsun Moon0d457362017-06-27 17:19:41 +090098
Ray Milkeyd84f89b2018-08-17 14:54:17 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900100 protected OpenstackNodeStore osNodeStore;
101
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900103 protected CoreService coreService;
104
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900106 protected ClusterService clusterService;
107
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900109 protected LeadershipService leadershipService;
110
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +0900112 protected StorageService storageService;
113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +0900115 protected OvsdbController ovsdbController;
116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +0900118 protected DeviceService deviceService;
119
Ray Milkey8e406512018-10-24 15:56:50 -0700120 /** OVSDB server listen port. */
121 private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
Daniel Parkc4d06402018-05-28 15:57:37 +0900122
Hyunsun Moon0d457362017-06-27 17:19:41 +0900123 private final ExecutorService eventExecutor = newSingleThreadExecutor(
124 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
125
Hyunsun Moon0d457362017-06-27 17:19:41 +0900126 private final OpenstackNodeStoreDelegate delegate = new InternalNodeStoreDelegate();
127
Jian Lie3141542018-08-13 18:05:43 +0900128 private AtomicCounter deviceIdCounter;
129
Hyunsun Moon0d457362017-06-27 17:19:41 +0900130 private ApplicationId appId;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900131
132 @Activate
133 protected void activate() {
134 appId = coreService.registerApplication(APP_ID);
135 osNodeStore.setDelegate(delegate);
136
Hyunsun Moon0d457362017-06-27 17:19:41 +0900137 leadershipService.runForLeadership(appId.name());
138
Jian Lie3141542018-08-13 18:05:43 +0900139 deviceIdCounter = storageService.getAtomicCounter(DEVICE_ID_COUNTER_NAME);
140
Hyunsun Moon0d457362017-06-27 17:19:41 +0900141 log.info("Started");
142 }
143
144 @Deactivate
145 protected void deactivate() {
146 osNodeStore.unsetDelegate(delegate);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900147
148 leadershipService.withdraw(appId.name());
149 eventExecutor.shutdown();
150
151 log.info("Stopped");
152 }
153
Daniel Parkc4d06402018-05-28 15:57:37 +0900154 @Modified
155 protected void modified(ComponentContext context) {
156 Dictionary<?, ?> properties = context.getProperties();
157 int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
Ray Milkey8e406512018-10-24 15:56:50 -0700158 if (!Objects.equals(updatedOvsdbPort, ovsdbPortNum)) {
159 ovsdbPortNum = updatedOvsdbPort;
Daniel Parkc4d06402018-05-28 15:57:37 +0900160 }
161
162 log.info("Modified");
163 }
164
Hyunsun Moon0d457362017-06-27 17:19:41 +0900165 @Override
166 public void createNode(OpenstackNode osNode) {
167 checkNotNull(osNode, ERR_NULL_NODE);
Jian Lie3141542018-08-13 18:05:43 +0900168
169 OpenstackNode updatedNode;
170
171 if (osNode.intgBridge() == null && osNode.type() != CONTROLLER) {
172 String deviceIdStr = genDpid(deviceIdCounter.incrementAndGet());
173 checkNotNull(deviceIdStr, ERR_NULL_DEVICE_ID);
174 updatedNode = osNode.updateIntbridge(DeviceId.deviceId(deviceIdStr));
175 checkArgument(!hasIntgBridge(updatedNode.intgBridge(), updatedNode.hostname()),
176 NOT_DUPLICATED_MSG, updatedNode.intgBridge());
177 } else {
178 updatedNode = osNode;
179 checkArgument(!hasIntgBridge(updatedNode.intgBridge(), updatedNode.hostname()),
180 NOT_DUPLICATED_MSG, updatedNode.intgBridge());
181 }
182
183 osNodeStore.createNode(updatedNode);
184
Hyunsun Moon0d457362017-06-27 17:19:41 +0900185 log.info(String.format(MSG_NODE, osNode.hostname(), MSG_CREATED));
186 }
187
188 @Override
189 public void updateNode(OpenstackNode osNode) {
190 checkNotNull(osNode, ERR_NULL_NODE);
Jian Lie3141542018-08-13 18:05:43 +0900191
192 OpenstackNode updatedNode;
193
Daniel Park5a6a7102018-09-06 23:58:33 +0900194 OpenstackNode existingNode = osNodeStore.node(osNode.hostname());
195 checkNotNull(existingNode, ERR_NULL_NODE);
Jian Lie3141542018-08-13 18:05:43 +0900196
197 DeviceId existDeviceId = osNodeStore.node(osNode.hostname()).intgBridge();
198
Daniel Park5a6a7102018-09-06 23:58:33 +0900199 if (vlanIntfChanged(existingNode, osNode) ||
200 physicalIntfChanged(existingNode, osNode) ||
201 dpdkIntfChanged(existingNode, osNode)) {
202
203 removeNode(osNode.hostname());
204
205 //we wait 1 second for ovsdb client completely to do removal job
206 try {
207 TimeUnit.MILLISECONDS.sleep(1000);
208 } catch (InterruptedException e) {
209 log.error("Exception occurred because of {}", e);
210 }
211
212 if (!intfsRemovedFromExistNode(existingNode)) {
213 log.error("Updated node failed because intfs of existingNode {} are not removed properly",
214 existingNode.toString());
215 return;
216 }
217
218 createNode(osNode);
219 return;
220 }
221
Jian Lie3141542018-08-13 18:05:43 +0900222 if (osNode.intgBridge() == null && osNode.type() != CONTROLLER) {
223 updatedNode = osNode.updateIntbridge(existDeviceId);
224 checkArgument(!hasIntgBridge(updatedNode.intgBridge(), updatedNode.hostname()),
225 NOT_DUPLICATED_MSG, updatedNode.intgBridge());
226 } else {
227 updatedNode = osNode;
228 checkArgument(!hasIntgBridge(updatedNode.intgBridge(), updatedNode.hostname()),
229 NOT_DUPLICATED_MSG, updatedNode.intgBridge());
230 }
231
232 osNodeStore.updateNode(updatedNode);
233
Hyunsun Moon0d457362017-06-27 17:19:41 +0900234 log.info(String.format(MSG_NODE, osNode.hostname(), MSG_UPDATED));
235 }
236
237 @Override
238 public OpenstackNode removeNode(String hostname) {
239 checkArgument(!Strings.isNullOrEmpty(hostname), ERR_NULL_HOSTNAME);
240 OpenstackNode osNode = osNodeStore.removeNode(hostname);
241 log.info(String.format(MSG_NODE, hostname, MSG_REMOVED));
242 return osNode;
243 }
244
245 @Override
246 public Set<OpenstackNode> nodes() {
247 return osNodeStore.nodes();
248 }
249
250 @Override
251 public Set<OpenstackNode> nodes(OpenstackNode.NodeType type) {
252 Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
253 .filter(osNode -> Objects.equals(osNode.type(), type))
254 .collect(Collectors.toSet());
255 return ImmutableSet.copyOf(osNodes);
256 }
257
258 @Override
259 public Set<OpenstackNode> completeNodes() {
260 Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
261 .filter(osNode -> Objects.equals(osNode.state(), COMPLETE))
262 .collect(Collectors.toSet());
263 return ImmutableSet.copyOf(osNodes);
264 }
265
266 @Override
267 public Set<OpenstackNode> completeNodes(OpenstackNode.NodeType type) {
268 Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
269 .filter(osNode -> osNode.type() == type &&
270 Objects.equals(osNode.state(), COMPLETE))
271 .collect(Collectors.toSet());
272 return ImmutableSet.copyOf(osNodes);
273 }
274
275 @Override
276 public OpenstackNode node(String hostname) {
277 return osNodeStore.node(hostname);
278 }
279
280 @Override
281 public OpenstackNode node(DeviceId deviceId) {
Jian Li5afbea42018-02-28 10:37:03 +0900282 return osNodeStore.nodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900283 .filter(osNode -> Objects.equals(osNode.intgBridge(), deviceId) ||
daniel parkb18424c2018-02-05 15:43:43 +0900284 Objects.equals(osNode.ovsdb(), deviceId))
Hyunsun Moon0d457362017-06-27 17:19:41 +0900285 .findFirst().orElse(null);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900286 }
287
Daniel Parkc4d06402018-05-28 15:57:37 +0900288 @Override
289 public void addVfPort(OpenstackNode osNode, String portName) {
290 log.trace("addVfPort called");
291
Jian Lib7873422018-08-18 22:34:39 +0900292 connectSwitch(osNode);
Daniel Parkc4d06402018-05-28 15:57:37 +0900293
Daniel Park5a6a7102018-09-06 23:58:33 +0900294 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, portName, deviceService, true);
Daniel Parkc4d06402018-05-28 15:57:37 +0900295 }
296
297 @Override
298 public void removeVfPort(OpenstackNode osNode, String portName) {
299 log.trace("removeVfPort called");
300
Jian Lib7873422018-08-18 22:34:39 +0900301 connectSwitch(osNode);
302
Daniel Park5a6a7102018-09-06 23:58:33 +0900303 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, portName, deviceService, false);
304 }
305
306 private boolean intfsRemovedFromExistNode(OpenstackNode osNode) {
307 if (osNode.vlanIntf() != null &&
308 !intfRemoved(osNode.vlanIntf(), osNode.intgBridge())) {
309 return false;
310 }
311
312 if (osNode.phyIntfs().stream().anyMatch(phyInterface ->
313 !intfRemoved(phyInterface.intf(), osNode.intgBridge()))) {
314 return false;
315 }
316
317 if (osNode.dpdkConfig() != null &&
318 osNode.dpdkConfig().dpdkIntfs().stream().anyMatch(dpdkInterface ->
319 !intfRemoved(dpdkInterface.intf(), osNode.intgBridge()))) {
320 return false;
321 }
322
323 return true;
324 }
325
326 private boolean intfRemoved(String intf, DeviceId deviceId) {
327 return !deviceService.getPorts(deviceId).stream()
328 .anyMatch(port -> port.annotations().value(PORT_NAME).equals(intf));
329 }
330
331 private boolean vlanIntfChanged(OpenstackNode oldNode, OpenstackNode newNode) {
332 return !Objects.equals(oldNode.vlanIntf(), newNode.vlanIntf());
333 }
334
335 private boolean physicalIntfChanged(OpenstackNode oldNode, OpenstackNode newNode) {
336 return !Objects.equals(oldNode.phyIntfs(), newNode.phyIntfs());
337 }
338
339 private boolean dpdkIntfChanged(OpenstackNode oldNode, OpenstackNode newNode) {
340 return !Objects.equals(oldNode.dpdkConfig(), newNode.dpdkConfig());
Jian Lib7873422018-08-18 22:34:39 +0900341 }
342
343 private void connectSwitch(OpenstackNode osNode) {
Ray Milkey8e406512018-10-24 15:56:50 -0700344 if (!isOvsdbConnected(osNode, ovsdbPortNum, ovsdbController, deviceService)) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900345 log.warn("There's no ovsdb connection with the device {}. Try to connect the device...",
346 osNode.ovsdb().toString());
347 try {
Ray Milkey8e406512018-10-24 15:56:50 -0700348 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPortNum));
Daniel Parkc4d06402018-05-28 15:57:37 +0900349 } catch (Exception e) {
350 log.error("Failed to connect to the openstackNode via ovsdb protocol because of exception {}",
351 e.toString());
352 }
353 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900354 }
355
Jian Lie3141542018-08-13 18:05:43 +0900356 private boolean hasIntgBridge(DeviceId deviceId, String hostname) {
357 Optional<OpenstackNode> existNode = osNodeStore.nodes().stream()
358 .filter(n -> n.type() != CONTROLLER)
359 .filter(n -> !n.hostname().equals(hostname))
360 .filter(n -> n.intgBridge().equals(deviceId))
361 .findFirst();
362
363 return existNode.isPresent();
364 }
365
Hyunsun Moon0d457362017-06-27 17:19:41 +0900366 private class InternalNodeStoreDelegate implements OpenstackNodeStoreDelegate {
367
368 @Override
369 public void notify(OpenstackNodeEvent event) {
370 if (event != null) {
371 log.trace("send openstack node event {}", event);
372 process(event);
373 }
374 }
375 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900376}