blob: 846f44e4eecc3e96d772742f4a13fd688f36a5d8 [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;
Daniel Park5a6a7102018-09-06 23:58:33 +090064import static org.onosproject.openstacknode.util.OpenstackNodeUtil.addOrRemoveSystemInterface;
Jian Lie3141542018-08-13 18:05:43 +090065import static org.onosproject.openstacknode.util.OpenstackNodeUtil.genDpid;
Daniel Parkc4d06402018-05-28 15:57:37 +090066import static org.onosproject.openstacknode.util.OpenstackNodeUtil.isOvsdbConnected;
Hyunsun Moon0d457362017-06-27 17:19:41 +090067import static org.slf4j.LoggerFactory.getLogger;
68
69/**
70 * Service administering the inventory of openstack nodes.
71 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070072@Component(immediate = true, service = { OpenstackNodeService.class, OpenstackNodeAdminService.class })
Hyunsun Moon0d457362017-06-27 17:19:41 +090073public class OpenstackNodeManager extends ListenerRegistry<OpenstackNodeEvent, OpenstackNodeListener>
74 implements OpenstackNodeService, OpenstackNodeAdminService {
75
Jian Li5afbea42018-02-28 10:37:03 +090076 private final Logger log = getLogger(getClass());
Hyunsun Moon0d457362017-06-27 17:19:41 +090077
78 private static final String MSG_NODE = "OpenStack node %s %s";
79 private static final String MSG_CREATED = "created";
80 private static final String MSG_UPDATED = "updated";
81 private static final String MSG_REMOVED = "removed";
Daniel Parkc4d06402018-05-28 15:57:37 +090082 private static final String OVSDB_PORT = "ovsdbPortNum";
83 private static final int DEFAULT_OVSDB_PORT = 6640;
Hyunsun Moon0d457362017-06-27 17:19:41 +090084
Jian Lie3141542018-08-13 18:05:43 +090085 private static final String DEVICE_ID_COUNTER_NAME = "device-id-counter";
86
Hyunsun Moon0d457362017-06-27 17:19:41 +090087 private static final String ERR_NULL_NODE = "OpenStack node cannot be null";
88 private static final String ERR_NULL_HOSTNAME = "OpenStack node hostname cannot be null";
Jian Lie3141542018-08-13 18:05:43 +090089 private static final String ERR_NULL_DEVICE_ID = "OpenStack node device ID cannot be null";
90
91 private static final String NOT_DUPLICATED_MSG = "% cannot be duplicated";
Hyunsun Moon0d457362017-06-27 17:19:41 +090092
Ray Milkeyd84f89b2018-08-17 14:54:17 -070093 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +090094 protected OpenstackNodeStore osNodeStore;
95
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +090097 protected CoreService coreService;
98
Ray Milkeyd84f89b2018-08-17 14:54:17 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900100 protected ClusterService clusterService;
101
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900103 protected LeadershipService leadershipService;
104
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +0900106 protected StorageService storageService;
107
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +0900109 protected OvsdbController ovsdbController;
110
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkc4d06402018-05-28 15:57:37 +0900112 protected DeviceService deviceService;
113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 //@Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
115 // label = "OVSDB server listen port")
Daniel Parkc4d06402018-05-28 15:57:37 +0900116 private int ovsdbPort = DEFAULT_OVSDB_PORT;
117
Hyunsun Moon0d457362017-06-27 17:19:41 +0900118 private final ExecutorService eventExecutor = newSingleThreadExecutor(
119 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
120
Hyunsun Moon0d457362017-06-27 17:19:41 +0900121 private final OpenstackNodeStoreDelegate delegate = new InternalNodeStoreDelegate();
122
Jian Lie3141542018-08-13 18:05:43 +0900123 private AtomicCounter deviceIdCounter;
124
Hyunsun Moon0d457362017-06-27 17:19:41 +0900125 private ApplicationId appId;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900126
127 @Activate
128 protected void activate() {
129 appId = coreService.registerApplication(APP_ID);
130 osNodeStore.setDelegate(delegate);
131
Hyunsun Moon0d457362017-06-27 17:19:41 +0900132 leadershipService.runForLeadership(appId.name());
133
Jian Lie3141542018-08-13 18:05:43 +0900134 deviceIdCounter = storageService.getAtomicCounter(DEVICE_ID_COUNTER_NAME);
135
Hyunsun Moon0d457362017-06-27 17:19:41 +0900136 log.info("Started");
137 }
138
139 @Deactivate
140 protected void deactivate() {
141 osNodeStore.unsetDelegate(delegate);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900142
143 leadershipService.withdraw(appId.name());
144 eventExecutor.shutdown();
145
146 log.info("Stopped");
147 }
148
Daniel Parkc4d06402018-05-28 15:57:37 +0900149 @Modified
150 protected void modified(ComponentContext context) {
151 Dictionary<?, ?> properties = context.getProperties();
152 int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
153 if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
154 ovsdbPort = updatedOvsdbPort;
155 }
156
157 log.info("Modified");
158 }
159
Hyunsun Moon0d457362017-06-27 17:19:41 +0900160 @Override
161 public void createNode(OpenstackNode osNode) {
162 checkNotNull(osNode, ERR_NULL_NODE);
Jian Lie3141542018-08-13 18:05:43 +0900163
164 OpenstackNode updatedNode;
165
166 if (osNode.intgBridge() == null && osNode.type() != CONTROLLER) {
167 String deviceIdStr = genDpid(deviceIdCounter.incrementAndGet());
168 checkNotNull(deviceIdStr, ERR_NULL_DEVICE_ID);
169 updatedNode = osNode.updateIntbridge(DeviceId.deviceId(deviceIdStr));
170 checkArgument(!hasIntgBridge(updatedNode.intgBridge(), updatedNode.hostname()),
171 NOT_DUPLICATED_MSG, updatedNode.intgBridge());
172 } else {
173 updatedNode = osNode;
174 checkArgument(!hasIntgBridge(updatedNode.intgBridge(), updatedNode.hostname()),
175 NOT_DUPLICATED_MSG, updatedNode.intgBridge());
176 }
177
178 osNodeStore.createNode(updatedNode);
179
Hyunsun Moon0d457362017-06-27 17:19:41 +0900180 log.info(String.format(MSG_NODE, osNode.hostname(), MSG_CREATED));
181 }
182
183 @Override
184 public void updateNode(OpenstackNode osNode) {
185 checkNotNull(osNode, ERR_NULL_NODE);
Jian Lie3141542018-08-13 18:05:43 +0900186
187 OpenstackNode updatedNode;
188
Daniel Park5a6a7102018-09-06 23:58:33 +0900189 OpenstackNode existingNode = osNodeStore.node(osNode.hostname());
190 checkNotNull(existingNode, ERR_NULL_NODE);
Jian Lie3141542018-08-13 18:05:43 +0900191
192 DeviceId existDeviceId = osNodeStore.node(osNode.hostname()).intgBridge();
193
Daniel Park5a6a7102018-09-06 23:58:33 +0900194 if (vlanIntfChanged(existingNode, osNode) ||
195 physicalIntfChanged(existingNode, osNode) ||
196 dpdkIntfChanged(existingNode, osNode)) {
197
198 removeNode(osNode.hostname());
199
200 //we wait 1 second for ovsdb client completely to do removal job
201 try {
202 TimeUnit.MILLISECONDS.sleep(1000);
203 } catch (InterruptedException e) {
204 log.error("Exception occurred because of {}", e);
205 }
206
207 if (!intfsRemovedFromExistNode(existingNode)) {
208 log.error("Updated node failed because intfs of existingNode {} are not removed properly",
209 existingNode.toString());
210 return;
211 }
212
213 createNode(osNode);
214 return;
215 }
216
Jian Lie3141542018-08-13 18:05:43 +0900217 if (osNode.intgBridge() == null && osNode.type() != CONTROLLER) {
218 updatedNode = osNode.updateIntbridge(existDeviceId);
219 checkArgument(!hasIntgBridge(updatedNode.intgBridge(), updatedNode.hostname()),
220 NOT_DUPLICATED_MSG, updatedNode.intgBridge());
221 } else {
222 updatedNode = osNode;
223 checkArgument(!hasIntgBridge(updatedNode.intgBridge(), updatedNode.hostname()),
224 NOT_DUPLICATED_MSG, updatedNode.intgBridge());
225 }
226
227 osNodeStore.updateNode(updatedNode);
228
Hyunsun Moon0d457362017-06-27 17:19:41 +0900229 log.info(String.format(MSG_NODE, osNode.hostname(), MSG_UPDATED));
230 }
231
232 @Override
233 public OpenstackNode removeNode(String hostname) {
234 checkArgument(!Strings.isNullOrEmpty(hostname), ERR_NULL_HOSTNAME);
235 OpenstackNode osNode = osNodeStore.removeNode(hostname);
236 log.info(String.format(MSG_NODE, hostname, MSG_REMOVED));
237 return osNode;
238 }
239
240 @Override
241 public Set<OpenstackNode> nodes() {
242 return osNodeStore.nodes();
243 }
244
245 @Override
246 public Set<OpenstackNode> nodes(OpenstackNode.NodeType type) {
247 Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
248 .filter(osNode -> Objects.equals(osNode.type(), type))
249 .collect(Collectors.toSet());
250 return ImmutableSet.copyOf(osNodes);
251 }
252
253 @Override
254 public Set<OpenstackNode> completeNodes() {
255 Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
256 .filter(osNode -> Objects.equals(osNode.state(), COMPLETE))
257 .collect(Collectors.toSet());
258 return ImmutableSet.copyOf(osNodes);
259 }
260
261 @Override
262 public Set<OpenstackNode> completeNodes(OpenstackNode.NodeType type) {
263 Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
264 .filter(osNode -> osNode.type() == type &&
265 Objects.equals(osNode.state(), COMPLETE))
266 .collect(Collectors.toSet());
267 return ImmutableSet.copyOf(osNodes);
268 }
269
270 @Override
271 public OpenstackNode node(String hostname) {
272 return osNodeStore.node(hostname);
273 }
274
275 @Override
276 public OpenstackNode node(DeviceId deviceId) {
Jian Li5afbea42018-02-28 10:37:03 +0900277 return osNodeStore.nodes().stream()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900278 .filter(osNode -> Objects.equals(osNode.intgBridge(), deviceId) ||
daniel parkb18424c2018-02-05 15:43:43 +0900279 Objects.equals(osNode.ovsdb(), deviceId))
Hyunsun Moon0d457362017-06-27 17:19:41 +0900280 .findFirst().orElse(null);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900281 }
282
Daniel Parkc4d06402018-05-28 15:57:37 +0900283 @Override
284 public void addVfPort(OpenstackNode osNode, String portName) {
285 log.trace("addVfPort called");
286
Jian Lib7873422018-08-18 22:34:39 +0900287 connectSwitch(osNode);
Daniel Parkc4d06402018-05-28 15:57:37 +0900288
Daniel Park5a6a7102018-09-06 23:58:33 +0900289 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, portName, deviceService, true);
Daniel Parkc4d06402018-05-28 15:57:37 +0900290 }
291
292 @Override
293 public void removeVfPort(OpenstackNode osNode, String portName) {
294 log.trace("removeVfPort called");
295
Jian Lib7873422018-08-18 22:34:39 +0900296 connectSwitch(osNode);
297
Daniel Park5a6a7102018-09-06 23:58:33 +0900298 addOrRemoveSystemInterface(osNode, INTEGRATION_BRIDGE, portName, deviceService, false);
299 }
300
301 private boolean intfsRemovedFromExistNode(OpenstackNode osNode) {
302 if (osNode.vlanIntf() != null &&
303 !intfRemoved(osNode.vlanIntf(), osNode.intgBridge())) {
304 return false;
305 }
306
307 if (osNode.phyIntfs().stream().anyMatch(phyInterface ->
308 !intfRemoved(phyInterface.intf(), osNode.intgBridge()))) {
309 return false;
310 }
311
312 if (osNode.dpdkConfig() != null &&
313 osNode.dpdkConfig().dpdkIntfs().stream().anyMatch(dpdkInterface ->
314 !intfRemoved(dpdkInterface.intf(), osNode.intgBridge()))) {
315 return false;
316 }
317
318 return true;
319 }
320
321 private boolean intfRemoved(String intf, DeviceId deviceId) {
322 return !deviceService.getPorts(deviceId).stream()
323 .anyMatch(port -> port.annotations().value(PORT_NAME).equals(intf));
324 }
325
326 private boolean vlanIntfChanged(OpenstackNode oldNode, OpenstackNode newNode) {
327 return !Objects.equals(oldNode.vlanIntf(), newNode.vlanIntf());
328 }
329
330 private boolean physicalIntfChanged(OpenstackNode oldNode, OpenstackNode newNode) {
331 return !Objects.equals(oldNode.phyIntfs(), newNode.phyIntfs());
332 }
333
334 private boolean dpdkIntfChanged(OpenstackNode oldNode, OpenstackNode newNode) {
335 return !Objects.equals(oldNode.dpdkConfig(), newNode.dpdkConfig());
Jian Lib7873422018-08-18 22:34:39 +0900336 }
337
338 private void connectSwitch(OpenstackNode osNode) {
Daniel Parkc4d06402018-05-28 15:57:37 +0900339 if (!isOvsdbConnected(osNode, ovsdbPort, ovsdbController, deviceService)) {
340 log.warn("There's no ovsdb connection with the device {}. Try to connect the device...",
341 osNode.ovsdb().toString());
342 try {
343 ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
344 } catch (Exception e) {
345 log.error("Failed to connect to the openstackNode via ovsdb protocol because of exception {}",
346 e.toString());
347 }
348 }
Daniel Parkc4d06402018-05-28 15:57:37 +0900349 }
350
Jian Lie3141542018-08-13 18:05:43 +0900351 private boolean hasIntgBridge(DeviceId deviceId, String hostname) {
352 Optional<OpenstackNode> existNode = osNodeStore.nodes().stream()
353 .filter(n -> n.type() != CONTROLLER)
354 .filter(n -> !n.hostname().equals(hostname))
355 .filter(n -> n.intgBridge().equals(deviceId))
356 .findFirst();
357
358 return existNode.isPresent();
359 }
360
Hyunsun Moon0d457362017-06-27 17:19:41 +0900361 private class InternalNodeStoreDelegate implements OpenstackNodeStoreDelegate {
362
363 @Override
364 public void notify(OpenstackNodeEvent event) {
365 if (event != null) {
366 log.trace("send openstack node event {}", event);
367 process(event);
368 }
369 }
370 }
Hyunsun Moon0d457362017-06-27 17:19:41 +0900371}