blob: b9c0880f99f08dbe9c518fb8b7b82c515ba56465 [file] [log] [blame]
Jian Li4fe40e52021-01-06 03:29:58 +09001/*
2 * Copyright 2021-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.kubevirtnode.impl;
17
Jian Li72f3dac2021-01-28 16:14:54 +090018import com.google.common.collect.Lists;
19import org.onlab.packet.IpAddress;
Jian Li4fe40e52021-01-06 03:29:58 +090020import org.onlab.util.Tools;
21import org.onosproject.cfg.ComponentConfigService;
22import org.onosproject.cluster.ClusterService;
23import org.onosproject.cluster.LeadershipService;
24import org.onosproject.cluster.NodeId;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
Jian Li72f3dac2021-01-28 16:14:54 +090027import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
Jian Li4fe40e52021-01-06 03:29:58 +090028import org.onosproject.kubevirtnode.api.KubevirtNode;
29import org.onosproject.kubevirtnode.api.KubevirtNodeAdminService;
30import org.onosproject.kubevirtnode.api.KubevirtNodeEvent;
31import org.onosproject.kubevirtnode.api.KubevirtNodeHandler;
32import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
33import org.onosproject.kubevirtnode.api.KubevirtNodeState;
34import org.onosproject.kubevirtnode.api.KubevirtPhyInterface;
35import org.onosproject.net.Device;
36import org.onosproject.net.DeviceId;
37import org.onosproject.net.Port;
38import org.onosproject.net.behaviour.BridgeConfig;
39import org.onosproject.net.behaviour.BridgeDescription;
40import org.onosproject.net.behaviour.BridgeName;
41import org.onosproject.net.behaviour.ControllerInfo;
42import org.onosproject.net.behaviour.DefaultBridgeDescription;
43import org.onosproject.net.behaviour.DefaultPatchDescription;
44import org.onosproject.net.behaviour.DefaultTunnelDescription;
45import org.onosproject.net.behaviour.InterfaceConfig;
46import org.onosproject.net.behaviour.PatchDescription;
47import org.onosproject.net.behaviour.TunnelDescription;
48import org.onosproject.net.behaviour.TunnelEndPoints;
49import org.onosproject.net.behaviour.TunnelKey;
50import org.onosproject.net.device.DeviceAdminService;
51import org.onosproject.net.device.DeviceEvent;
52import org.onosproject.net.device.DeviceListener;
53import org.onosproject.net.device.DeviceService;
54import org.onosproject.ovsdb.controller.OvsdbClientService;
55import org.onosproject.ovsdb.controller.OvsdbController;
56import org.osgi.service.component.ComponentContext;
57import org.osgi.service.component.annotations.Activate;
58import org.osgi.service.component.annotations.Component;
59import org.osgi.service.component.annotations.Deactivate;
60import org.osgi.service.component.annotations.Modified;
61import org.osgi.service.component.annotations.Reference;
62import org.osgi.service.component.annotations.ReferenceCardinality;
63import org.slf4j.Logger;
64
65import java.util.Dictionary;
66import java.util.List;
67import java.util.Objects;
68import java.util.Set;
69import java.util.concurrent.ExecutorService;
70import java.util.stream.Collectors;
71
72import static java.lang.Thread.sleep;
73import static java.util.concurrent.Executors.newSingleThreadExecutor;
74import static org.onlab.packet.TpPort.tpPort;
75import static org.onlab.util.Tools.groupedThreads;
76import static org.onosproject.kubevirtnode.api.Constants.BRIDGE_PREFIX;
77import static org.onosproject.kubevirtnode.api.Constants.FLOW_KEY;
78import static org.onosproject.kubevirtnode.api.Constants.GENEVE;
79import static org.onosproject.kubevirtnode.api.Constants.GRE;
80import static org.onosproject.kubevirtnode.api.Constants.INTEGRATION_BRIDGE;
81import static org.onosproject.kubevirtnode.api.Constants.INTEGRATION_TO_PHYSICAL_PREFIX;
Daniel Parkf3136042021-03-10 07:49:11 +090082import static org.onosproject.kubevirtnode.api.Constants.INTEGRATION_TO_TUNNEL;
Jian Li4fe40e52021-01-06 03:29:58 +090083import static org.onosproject.kubevirtnode.api.Constants.PHYSICAL_TO_INTEGRATION_SUFFIX;
Jian Li556709c2021-02-03 17:54:28 +090084import static org.onosproject.kubevirtnode.api.Constants.TENANT_BRIDGE_PREFIX;
Jian Li4fe40e52021-01-06 03:29:58 +090085import static org.onosproject.kubevirtnode.api.Constants.TUNNEL_BRIDGE;
Daniel Parkf3136042021-03-10 07:49:11 +090086import static org.onosproject.kubevirtnode.api.Constants.TUNNEL_TO_INTEGRATION;
Jian Li4fe40e52021-01-06 03:29:58 +090087import static org.onosproject.kubevirtnode.api.Constants.VXLAN;
Daniel Parkf3136042021-03-10 07:49:11 +090088import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.GATEWAY;
Jian Li4fe40e52021-01-06 03:29:58 +090089import static org.onosproject.kubevirtnode.api.KubevirtNodeService.APP_ID;
90import static org.onosproject.kubevirtnode.api.KubevirtNodeState.COMPLETE;
91import static org.onosproject.kubevirtnode.api.KubevirtNodeState.DEVICE_CREATED;
92import static org.onosproject.kubevirtnode.api.KubevirtNodeState.INCOMPLETE;
93import static org.onosproject.kubevirtnode.api.KubevirtNodeState.INIT;
94import static org.onosproject.kubevirtnode.impl.OsgiPropertyConstants.AUTO_RECOVERY;
95import static org.onosproject.kubevirtnode.impl.OsgiPropertyConstants.AUTO_RECOVERY_DEFAULT;
96import static org.onosproject.kubevirtnode.impl.OsgiPropertyConstants.OVSDB_PORT;
97import static org.onosproject.kubevirtnode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
98import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.addOrRemoveSystemInterface;
99import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.getBooleanProperty;
100import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.getOvsdbClient;
101import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.isOvsdbConnected;
102import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.structurePortName;
103import static org.onosproject.net.AnnotationKeys.PORT_NAME;
104import static org.slf4j.LoggerFactory.getLogger;
105
106/**
107 * Service bootstraps kubernetes node based on its type.
108 */
109@Component(immediate = true,
110 property = {
111 OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT,
112 AUTO_RECOVERY + ":Boolean=" + AUTO_RECOVERY_DEFAULT
113 }
114)
115public class DefaultKubevirtNodeHandler implements KubevirtNodeHandler {
116
117 private final Logger log = getLogger(getClass());
118
119 private static final String DEFAULT_OF_PROTO = "tcp";
120 private static final int DEFAULT_OFPORT = 6653;
121 private static final int DPID_BEGIN = 3;
122 private static final int NETWORK_BEGIN = 3;
123 private static final long SLEEP_SHORT_MS = 1000; // we wait 1s
Jian Li4b249702021-02-19 18:13:10 +0900124 private static final long SLEEP_MID_MS = 2000; // we wait 2s
125 private static final long SLEEP_LONG_MS = 5000; // we wait 5s
Jian Li4fe40e52021-01-06 03:29:58 +0900126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
128 protected CoreService coreService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
131 protected LeadershipService leadershipService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
134 protected ClusterService clusterService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
137 protected DeviceService deviceService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
140 protected DeviceAdminService deviceAdminService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
143 protected OvsdbController ovsdbController;
144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
146 protected KubevirtNodeAdminService nodeAdminService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li72f3dac2021-01-28 16:14:54 +0900149 protected KubevirtApiConfigService apiConfigService;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li4fe40e52021-01-06 03:29:58 +0900152 protected ComponentConfigService componentConfigService;
153
154 /** OVSDB server listen port. */
155 private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
156
157 /** Indicates whether auto-recover kubernetes node status on switch re-conn event. */
158 private boolean autoRecovery = AUTO_RECOVERY_DEFAULT;
159
160 private final ExecutorService eventExecutor = newSingleThreadExecutor(
161 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
162
163 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
164 private final DeviceListener bridgeListener = new InternalBridgeListener();
165 private final KubevirtNodeListener kubevirtNodeListener = new InternalKubevirtNodeListener();
166
167 private ApplicationId appId;
168 private NodeId localNode;
169
170 @Activate
171 protected void activate() {
172 appId = coreService.getAppId(APP_ID);
173 localNode = clusterService.getLocalNode().id();
174
175 componentConfigService.registerProperties(getClass());
176 leadershipService.runForLeadership(appId.name());
177 deviceService.addListener(ovsdbListener);
178 deviceService.addListener(bridgeListener);
179 nodeAdminService.addListener(kubevirtNodeListener);
180
181 log.info("Started");
182 }
183
184 @Deactivate
185 protected void deactivate() {
186 nodeAdminService.removeListener(kubevirtNodeListener);
187 deviceService.removeListener(bridgeListener);
188 deviceService.removeListener(ovsdbListener);
189 componentConfigService.unregisterProperties(getClass(), false);
190 leadershipService.withdraw(appId.name());
191 eventExecutor.shutdown();
192
193 log.info("Stopped");
194 }
195
196 @Modified
197 protected void modified(ComponentContext context) {
198 readComponentConfiguration(context);
199
200 log.info("Modified");
201 }
202
203 @Override
204 public void processInitState(KubevirtNode node) {
205 if (!isOvsdbConnected(node, ovsdbPortNum, ovsdbController, deviceService)) {
206 ovsdbController.connect(node.managementIp(), tpPort(ovsdbPortNum));
207 return;
208 }
209 if (!deviceService.isAvailable(node.intgBridge())) {
210 createBridge(node, INTEGRATION_BRIDGE, node.intgBridge());
211 }
212
213 if (!deviceService.isAvailable(node.tunBridge())) {
214 createBridge(node, TUNNEL_BRIDGE, node.tunBridge());
215 }
216 }
217
218 @Override
219 public void processDeviceCreatedState(KubevirtNode node) {
220 try {
221 if (!isOvsdbConnected(node, ovsdbPortNum, ovsdbController, deviceService)) {
222 ovsdbController.connect(node.managementIp(), tpPort(ovsdbPortNum));
223 return;
224 }
225
226 // create patch ports between integration to other bridges
Jian Li858ccd72021-02-04 17:25:01 +0900227 // for now, we do not directly connect br-int with br-tun,
228 // as br-int only deals with FLAT and VLAN network
229 // createPatchInterfaces(node);
Jian Li4fe40e52021-01-06 03:29:58 +0900230
231 if (node.dataIp() != null && !isIntfEnabled(node, VXLAN)) {
232 createVxlanTunnelInterface(node);
233 }
234
235 if (node.dataIp() != null && !isIntfEnabled(node, GRE)) {
236 createGreTunnelInterface(node);
237 }
238
239 if (node.dataIp() != null && !isIntfEnabled(node, GENEVE)) {
240 createGeneveTunnelInterface(node);
241 }
Jian Li4fe40e52021-01-06 03:29:58 +0900242 } catch (Exception e) {
243 log.error("Exception occurred because of {}", e);
244 }
245 }
246
247 @Override
248 public void processCompleteState(KubevirtNode node) {
249 // do something if needed
250 }
251
252 @Override
253 public void processIncompleteState(KubevirtNode node) {
254 // do something if needed
255 }
256
257 @Override
258 public void processOnBoardedState(KubevirtNode node) {
259 // do something if needed
260 }
261
262 /**
263 * Extracts properties from the component configuration context.
264 *
265 * @param context the component context
266 */
267 private void readComponentConfiguration(ComponentContext context) {
268 Dictionary<?, ?> properties = context.getProperties();
269
270 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
271 if (ovsdbPortConfigured == null) {
272 ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
273 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPortNum);
274 } else {
275 ovsdbPortNum = ovsdbPortConfigured;
276 log.info("Configured. OVSDB port is {}", ovsdbPortNum);
277 }
278
279 Boolean autoRecoveryConfigured =
280 getBooleanProperty(properties, AUTO_RECOVERY);
281 if (autoRecoveryConfigured == null) {
282 autoRecovery = AUTO_RECOVERY_DEFAULT;
283 log.info("Auto recovery flag is NOT " +
284 "configured, default value is {}", autoRecovery);
285 } else {
286 autoRecovery = autoRecoveryConfigured;
287 log.info("Configured. Auto recovery flag is {}", autoRecovery);
288 }
289 }
290
291 /**
292 * Creates a bridge with a given name on a given kubernetes node.
293 *
294 * @param node kubevirt node
295 * @param bridgeName bridge name
296 * @param devId device identifier
297 */
298 private void createBridge(KubevirtNode node, String bridgeName, DeviceId devId) {
299 Device device = deviceService.getDevice(node.ovsdb());
300
Jian Li72f3dac2021-01-28 16:14:54 +0900301 IpAddress serverIp = apiConfigService.apiConfig().ipAddress();
302 ControllerInfo controlInfo = new ControllerInfo(serverIp, DEFAULT_OFPORT, DEFAULT_OF_PROTO);
303 List<ControllerInfo> controllers = Lists.newArrayList(controlInfo);
Jian Li4fe40e52021-01-06 03:29:58 +0900304
305 String dpid = devId.toString().substring(DPID_BEGIN);
306
307 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
308 .name(bridgeName)
309 .failMode(BridgeDescription.FailMode.SECURE)
310 .datapathId(dpid)
311 .disableInBand()
312 .controllers(controllers);
313
314 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
315 bridgeConfig.addBridge(builder.build());
316 }
317
318 /**
319 * Creates a VXLAN tunnel interface in a given kubevirt node.
320 *
321 * @param node kubevirt node
322 */
323 private void createVxlanTunnelInterface(KubevirtNode node) {
324 createTunnelInterface(node, VXLAN, VXLAN);
325 }
326
327 /**
328 * Creates a GRE tunnel interface in a given kubevirt node.
329 *
330 * @param node kubevirt node
331 */
332 private void createGreTunnelInterface(KubevirtNode node) {
333 createTunnelInterface(node, GRE, GRE);
334 }
335
336 /**
337 * Creates a GENEVE tunnel interface in a given kubevirt node.
338 *
339 * @param node kubevirt node
340 */
341 private void createGeneveTunnelInterface(KubevirtNode node) {
342 createTunnelInterface(node, GENEVE, GENEVE);
343 }
344
345 /**
346 * Creates a tunnel interface in a given kubernetes node.
347 *
348 * @param node kubevirt node
Jian Li4b249702021-02-19 18:13:10 +0900349 * @param type kubevirt type
350 * @param intfName tunnel interface name
Jian Li4fe40e52021-01-06 03:29:58 +0900351 */
352 private void createTunnelInterface(KubevirtNode node,
353 String type, String intfName) {
354 if (isIntfEnabled(node, intfName)) {
355 return;
356 }
357
358 Device device = deviceService.getDevice(node.ovsdb());
359 if (device == null || !device.is(InterfaceConfig.class)) {
360 log.error("Failed to create tunnel interface on {}", node.ovsdb());
361 return;
362 }
363
364 TunnelDescription tunnelDesc = buildTunnelDesc(type, intfName);
365
366 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
367 ifaceConfig.addTunnelMode(intfName, tunnelDesc);
368 }
369
370 /**
371 * Builds tunnel description according to the network type.
372 *
373 * @param type network type
Jian Li4b249702021-02-19 18:13:10 +0900374 * @param intfName tunnel interface
Jian Li4fe40e52021-01-06 03:29:58 +0900375 * @return tunnel description
376 */
377 private TunnelDescription buildTunnelDesc(String type, String intfName) {
378 TunnelKey<String> key = new TunnelKey<>(FLOW_KEY);
379 if (VXLAN.equals(type) || GRE.equals(type) || GENEVE.equals(type)) {
380 TunnelDescription.Builder tdBuilder =
381 DefaultTunnelDescription.builder()
382 .deviceId(TUNNEL_BRIDGE)
383 .ifaceName(intfName)
384 .remote(TunnelEndPoints.flowTunnelEndpoint())
385 .key(key);
386
387 switch (type) {
388 case VXLAN:
389 tdBuilder.type(TunnelDescription.Type.VXLAN);
390 break;
391 case GRE:
392 tdBuilder.type(TunnelDescription.Type.GRE);
393 break;
394 case GENEVE:
395 tdBuilder.type(TunnelDescription.Type.GENEVE);
396 break;
397 default:
398 return null;
399 }
400
401 return tdBuilder.build();
402 }
403 return null;
404 }
405
406 /**
407 * Checks whether a given network interface in a given kubernetes node
408 * is enabled or not.
409 *
410 * @param node kubevirt node
411 * @param intf network interface name
412 * @return true if the given interface is enabled, false otherwise
413 */
414 private boolean isIntfEnabled(KubevirtNode node, String intf) {
415 return deviceService.isAvailable(node.tunBridge()) &&
416 deviceService.getPorts(node.tunBridge()).stream()
417 .anyMatch(port -> Objects.equals(
418 port.annotations().value(PORT_NAME), intf) &&
419 port.isEnabled());
420 }
421
Jian Li4fe40e52021-01-06 03:29:58 +0900422 /**
423 * Bootstraps a new kubevirt node.
424 *
425 * @param node kubevirt node
426 */
427 private void bootstrapNode(KubevirtNode node) {
428 if (isCurrentStateDone(node)) {
429 setState(node, node.state().nextState());
430 } else {
431 log.trace("Processing {} state for {}", node.state(), node.hostname());
432 node.state().process(this, node);
433 }
434 }
435
436 /**
437 * Removes the existing kubevirt node.
438 *
439 * @param node kubevirt node
440 */
441 private void removeNode(KubevirtNode node) {
442 OvsdbClientService client = getOvsdbClient(node, ovsdbPortNum, ovsdbController);
443 if (client == null) {
444 log.info("Failed to get ovsdb client");
445 return;
446 }
447
448 // unprovision physical interfaces from the node
449 // this procedure includes detaching physical port from physical bridge,
450 // remove patch ports from br-int, removing physical bridge
451 unprovisionPhysicalInterfaces(node);
452
453 // delete tunnel bridge from the node
454 client.dropBridge(TUNNEL_BRIDGE);
455
456 // delete integration bridge from the node
457 client.dropBridge(INTEGRATION_BRIDGE);
458 }
459
460 /**
461 * Checks whether all requirements for this state are fulfilled or not.
462 *
463 * @param node kubevirt node
464 * @return true if all requirements are fulfilled, false otherwise
465 */
466 private boolean isCurrentStateDone(KubevirtNode node) {
467 switch (node.state()) {
468 case INIT:
469 return isInitStateDone(node);
470 case DEVICE_CREATED:
471 return isDeviceCreatedStateDone(node);
472 case COMPLETE:
473 case INCOMPLETE:
474 case ON_BOARDED:
475 // always return false
476 // run init CLI to re-trigger node bootstrap
477 return false;
478 default:
479 return true;
480 }
481 }
482
483 private boolean isInitStateDone(KubevirtNode node) {
484 if (!isOvsdbConnected(node, ovsdbPortNum,
485 ovsdbController, deviceService)) {
486 return false;
487 }
488
489 try {
490 // we need to wait a while, in case interfaces and bridges
491 // creation requires some time
492 sleep(SLEEP_SHORT_MS);
493 } catch (InterruptedException e) {
494 log.error("Exception caused during init state checking...");
495 }
496
497 cleanPhysicalInterfaces(node);
498
Jian Li4b249702021-02-19 18:13:10 +0900499 // provision new physical interfaces on the given node
500 // this includes creating physical bridge, attaching physical port
501 // to physical bridge, adding patch ports to both physical bridge and br-int
502 provisionPhysicalInterfaces(node);
503
Daniel Parkf3136042021-03-10 07:49:11 +0900504 if (node.type() == GATEWAY) {
505 createPatchInterfaceBetweenBrIntBrTun(node);
506 }
507
Jian Li4fe40e52021-01-06 03:29:58 +0900508 return node.intgBridge() != null && node.tunBridge() != null &&
509 deviceService.isAvailable(node.intgBridge()) &&
510 deviceService.isAvailable(node.tunBridge());
511 }
512
513 private boolean isDeviceCreatedStateDone(KubevirtNode node) {
514
515 try {
516 // we need to wait a while, in case tunneling ports
517 // creation requires some time
Jian Li4b249702021-02-19 18:13:10 +0900518 sleep(SLEEP_MID_MS);
Jian Li4fe40e52021-01-06 03:29:58 +0900519 } catch (InterruptedException e) {
520 log.error("Exception caused during init state checking...");
521 }
522
523 if (node.dataIp() != null && !isIntfEnabled(node, VXLAN)) {
Jian Li4b249702021-02-19 18:13:10 +0900524 log.warn("VXLAN interface is not enabled!");
Jian Li4fe40e52021-01-06 03:29:58 +0900525 return false;
526 }
527 if (node.dataIp() != null && !isIntfEnabled(node, GRE)) {
Jian Li4b249702021-02-19 18:13:10 +0900528 log.warn("GRE interface is not enabled!");
Jian Li4fe40e52021-01-06 03:29:58 +0900529 return false;
530 }
531 if (node.dataIp() != null && !isIntfEnabled(node, GENEVE)) {
Jian Li4b249702021-02-19 18:13:10 +0900532 log.warn("GENEVE interface is not enabled!");
Jian Li4fe40e52021-01-06 03:29:58 +0900533 return false;
534 }
535
536 for (KubevirtPhyInterface phyIntf : node.phyIntfs()) {
537 if (phyIntf == null) {
Jian Li4b249702021-02-19 18:13:10 +0900538 log.warn("Physnet interface is invalid");
Jian Li4fe40e52021-01-06 03:29:58 +0900539 return false;
540 }
541
Jian Li4b249702021-02-19 18:13:10 +0900542 try {
543 // we need to wait a while, in case tunneling ports
544 // creation requires some time
545 sleep(SLEEP_LONG_MS);
546 } catch (InterruptedException e) {
547 log.error("Exception caused during init state checking...");
548 }
549
Jian Li4fe40e52021-01-06 03:29:58 +0900550 String bridgeName = BRIDGE_PREFIX + phyIntf.network();
551 String patchPortName = structurePortName(
552 INTEGRATION_TO_PHYSICAL_PREFIX + phyIntf.network());
553
554 if (!(hasPhyBridge(node, bridgeName) &&
555 hasPhyPatchPort(node, patchPortName) &&
556 hasPhyIntf(node, phyIntf.intf()))) {
Jian Li4b249702021-02-19 18:13:10 +0900557 log.warn("PhyBridge {}", hasPhyBridge(node, bridgeName));
558 log.warn("hasPhyPatchPort {}", hasPhyPatchPort(node, patchPortName));
559 log.warn("hasPhyIntf {}", hasPhyIntf(node, phyIntf.intf()));
Jian Li4fe40e52021-01-06 03:29:58 +0900560 return false;
561 }
562 }
563
Daniel Parkf3136042021-03-10 07:49:11 +0900564 if (node.type() == GATEWAY) {
565 if (!(hasPhyIntf(node, INTEGRATION_TO_TUNNEL) &&
566 hasPhyIntf(node, TUNNEL_TO_INTEGRATION))) {
567 log.warn("IntToTunPort {}", hasPhyIntf(node, INTEGRATION_TO_TUNNEL));
568 log.warn("TunToIntPort {}", hasPhyIntf(node, TUNNEL_TO_INTEGRATION));
569 return false;
570 }
571 }
Jian Li4fe40e52021-01-06 03:29:58 +0900572 return true;
573 }
574
575 /**
576 * Configures the kubernetes node with new state.
577 *
578 * @param node kubevirt node
579 * @param newState a new state
580 */
581 private void setState(KubevirtNode node, KubevirtNodeState newState) {
582 if (node.state() == newState) {
583 return;
584 }
585 KubevirtNode updated = node.updateState(newState);
586 nodeAdminService.updateNode(updated);
587 log.info("Changed {} state: {}", node.hostname(), newState);
588 }
589
590 private void provisionPhysicalInterfaces(KubevirtNode node) {
591 node.phyIntfs().forEach(pi -> {
592 String bridgeName = BRIDGE_PREFIX + pi.network();
593 String patchPortName =
594 structurePortName(INTEGRATION_TO_PHYSICAL_PREFIX + pi.network());
595
596 if (!hasPhyBridge(node, bridgeName)) {
597 createPhysicalBridge(node, pi);
598 createPhysicalPatchPorts(node, pi);
599 attachPhysicalPort(node, pi);
Jian Li4b249702021-02-19 18:13:10 +0900600
601 log.info("Creating physnet bridge {}", bridgeName);
602 log.info("Creating patch ports for physnet {}", bridgeName);
Jian Li4fe40e52021-01-06 03:29:58 +0900603 } else {
Jian Li517597a2021-03-22 11:04:52 +0900604 // in case physical bridge exists, but patch port is missing,
605 // we will add patch port to connect br-physnet with physical bridge
Jian Li4fe40e52021-01-06 03:29:58 +0900606 if (!hasPhyPatchPort(node, patchPortName)) {
607 createPhysicalPatchPorts(node, pi);
Jian Li4b249702021-02-19 18:13:10 +0900608
609 log.info("Creating patch ports for physnet {}", bridgeName);
Jian Li4fe40e52021-01-06 03:29:58 +0900610 }
Jian Li517597a2021-03-22 11:04:52 +0900611
612 // in case physical bridge exists, but physnet interface is missing,
613 // we will add the physnet interface to connect br-physnet to the external
614 if (!hasPhyIntf(node, pi.intf())) {
615 attachPhysicalPort(node, pi);
616
617 log.info("Attaching external ports for physnet {}", bridgeName);
618 }
Jian Li4fe40e52021-01-06 03:29:58 +0900619 }
620 });
621 }
622
623 private void cleanPhysicalInterfaces(KubevirtNode node) {
624 Device device = deviceService.getDevice(node.ovsdb());
625
626 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
627
628 Set<String> bridgeNames = bridgeConfig.getBridges().stream()
629 .map(BridgeDescription::name).collect(Collectors.toSet());
630
631 Set<String> phyNetworkNames = node.phyIntfs().stream()
632 .map(pi -> BRIDGE_PREFIX + pi.network()).collect(Collectors.toSet());
633
634 // we remove existing physical bridges and patch ports, if the physical
635 // bridges are not defined in kubevirt node
636 for (String brName : bridgeNames) {
Jian Li4b249702021-02-19 18:13:10 +0900637 // integration bridge and tunnel bridge should NOT be treated as
638 // physical bridges
639 if (brName.equals(INTEGRATION_BRIDGE) ||
640 brName.equals(TUNNEL_BRIDGE) ||
641 brName.startsWith(TENANT_BRIDGE_PREFIX)) {
642 continue;
643 }
644
Jian Li4fe40e52021-01-06 03:29:58 +0900645 if (!phyNetworkNames.contains(brName)) {
Jian Li4fe40e52021-01-06 03:29:58 +0900646 removePhysicalPatchPorts(node, brName.substring(NETWORK_BEGIN));
647 removePhysicalBridge(node, brName.substring(NETWORK_BEGIN));
Jian Li4b249702021-02-19 18:13:10 +0900648 log.info("Removing physical bridge {}...", brName);
Jian Li4fe40e52021-01-06 03:29:58 +0900649 }
650 }
651 }
652
Daniel Parkf3136042021-03-10 07:49:11 +0900653
654 private void createPatchInterfaceBetweenBrIntBrTun(KubevirtNode node) {
655 Device device = deviceService.getDevice(node.ovsdb());
656
657 if (device == null || !device.is(InterfaceConfig.class)) {
658 log.error("Failed to create patch interface on {}", node.ovsdb());
659 return;
660 }
661
662 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
663
664 // int bridge -> tunnel bridge
665 PatchDescription brIntTunPatchDesc =
666 DefaultPatchDescription.builder()
667 .deviceId(INTEGRATION_BRIDGE)
668 .ifaceName(INTEGRATION_TO_TUNNEL)
669 .peer(TUNNEL_TO_INTEGRATION)
670 .build();
671
672 ifaceConfig.addPatchMode(INTEGRATION_TO_TUNNEL, brIntTunPatchDesc);
673
674 // tunnel bridge -> int bridge
675 PatchDescription brTunIntPatchDesc =
676 DefaultPatchDescription.builder()
677 .deviceId(TUNNEL_BRIDGE)
678 .ifaceName(TUNNEL_TO_INTEGRATION)
679 .peer(INTEGRATION_TO_TUNNEL)
680 .build();
681
682 ifaceConfig.addPatchMode(TUNNEL_TO_INTEGRATION, brTunIntPatchDesc);
683 }
684
Jian Li4fe40e52021-01-06 03:29:58 +0900685 private void unprovisionPhysicalInterfaces(KubevirtNode node) {
686 node.phyIntfs().forEach(pi -> {
687 detachPhysicalPort(node, pi.network(), pi.intf());
688 removePhysicalPatchPorts(node, pi.network());
689 removePhysicalBridge(node, pi.network());
690 });
691 }
692
693 private boolean hasPhyBridge(KubevirtNode node, String bridgeName) {
694 BridgeConfig bridgeConfig =
695 deviceService.getDevice(node.ovsdb()).as(BridgeConfig.class);
696 return bridgeConfig.getBridges().stream()
697 .anyMatch(br -> br.name().equals(bridgeName));
698 }
699
700 private boolean hasPhyPatchPort(KubevirtNode node, String patchPortName) {
701 List<Port> ports = deviceService.getPorts(node.intgBridge());
702 return ports.stream().anyMatch(p ->
703 p.annotations().value(PORT_NAME).equals(patchPortName));
704 }
705
706 private boolean hasPhyIntf(KubevirtNode node, String intfName) {
707 BridgeConfig bridgeConfig =
708 deviceService.getDevice(node.ovsdb()).as(BridgeConfig.class);
709 return bridgeConfig.getPorts().stream()
710 .anyMatch(p -> p.annotations().value(PORT_NAME).equals(intfName));
711 }
712
713 private void createPhysicalBridge(KubevirtNode osNode,
714 KubevirtPhyInterface phyInterface) {
715 Device device = deviceService.getDevice(osNode.ovsdb());
716
717 String bridgeName = BRIDGE_PREFIX + phyInterface.network();
718
719 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
720 .name(bridgeName)
721 .mcastSnoopingEnable();
722
723 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
724 bridgeConfig.addBridge(builder.build());
725 }
726
727 private void removePhysicalBridge(KubevirtNode node, String network) {
728 Device device = deviceService.getDevice(node.ovsdb());
729
730 BridgeName bridgeName = BridgeName.bridgeName(BRIDGE_PREFIX + network);
731
732 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
733 bridgeConfig.deleteBridge(bridgeName);
734 }
735
736 private void createPhysicalPatchPorts(KubevirtNode node,
737 KubevirtPhyInterface phyInterface) {
738 Device device = deviceService.getDevice(node.ovsdb());
739
740 if (device == null || !device.is(InterfaceConfig.class)) {
741 log.error("Failed to create patch interface on {}", node.ovsdb());
742 return;
743 }
744
745 String physicalDeviceId = BRIDGE_PREFIX + phyInterface.network();
746
747 String intToPhyPatchPort = structurePortName(
748 INTEGRATION_TO_PHYSICAL_PREFIX + phyInterface.network());
749 String phyToIntPatchPort = structurePortName(
750 phyInterface.network() + PHYSICAL_TO_INTEGRATION_SUFFIX);
751
752 // integration bridge -> physical bridge
753 PatchDescription intToPhyPatchDesc =
754 DefaultPatchDescription.builder()
755 .deviceId(INTEGRATION_BRIDGE)
756 .ifaceName(intToPhyPatchPort)
757 .peer(phyToIntPatchPort)
758 .build();
759
760 // physical bridge -> integration bridge
761 PatchDescription phyToIntPatchDesc =
762 DefaultPatchDescription.builder()
763 .deviceId(physicalDeviceId)
764 .ifaceName(phyToIntPatchPort)
765 .peer(intToPhyPatchPort)
766 .build();
767
768 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
769 ifaceConfig.addPatchMode(INTEGRATION_TO_PHYSICAL_PREFIX +
770 phyInterface.network(), intToPhyPatchDesc);
771 ifaceConfig.addPatchMode(phyInterface.network() +
772 PHYSICAL_TO_INTEGRATION_SUFFIX, phyToIntPatchDesc);
773
774 addOrRemoveSystemInterface(node, physicalDeviceId,
775 phyInterface.intf(), deviceService, true);
776 }
777
778 private void removePhysicalPatchPorts(KubevirtNode node, String network) {
779 Device device = deviceService.getDevice(node.ovsdb());
780
781 if (device == null || !device.is(InterfaceConfig.class)) {
782 log.error("Failed to remove patch interface on {}", node.ovsdb());
783 return;
784 }
785
786 String intToPhyPatchPort = structurePortName(
787 INTEGRATION_TO_PHYSICAL_PREFIX + network);
788
789 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
790 ifaceConfig.removePatchMode(intToPhyPatchPort);
791 }
792
793 private void attachPhysicalPort(KubevirtNode node,
794 KubevirtPhyInterface phyInterface) {
795
796 String physicalDeviceId = BRIDGE_PREFIX + phyInterface.network();
797
798 addOrRemoveSystemInterface(node, physicalDeviceId,
799 phyInterface.intf(), deviceService, true);
800 }
801
802 private void detachPhysicalPort(KubevirtNode node, String network, String portName) {
803 String physicalDeviceId = BRIDGE_PREFIX + network;
804
805 addOrRemoveSystemInterface(node, physicalDeviceId, portName, deviceService, false);
806 }
807
808 /**
809 * An internal OVSDB listener. This listener is used for listening the
810 * network facing events from OVSDB device. If a new OVSDB device is detected,
811 * ONOS tries to bootstrap the kubernetes node.
812 */
813 private class InternalOvsdbListener implements DeviceListener {
814
815 @Override
816 public boolean isRelevant(DeviceEvent event) {
817 return event.subject().type() == Device.Type.CONTROLLER;
818 }
819
820 private boolean isRelevantHelper() {
821 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
822 }
823
824 @Override
825 public void event(DeviceEvent event) {
826 Device device = event.subject();
827
828 switch (event.type()) {
829 case DEVICE_AVAILABILITY_CHANGED:
830 case DEVICE_ADDED:
831 eventExecutor.execute(() -> {
832
833 if (!isRelevantHelper()) {
834 return;
835 }
836
837 KubevirtNode node = nodeAdminService.node(device.id());
838
839 if (node == null) {
840 return;
841 }
842
843 if (deviceService.isAvailable(device.id())) {
844 log.debug("OVSDB {} detected", device.id());
845 bootstrapNode(node);
846 }
847 });
848 break;
849 case PORT_ADDED:
850 case PORT_REMOVED:
851 case DEVICE_REMOVED:
852 default:
853 // do nothing
854 break;
855 }
856 }
857 }
858
859 /**
860 * An internal integration bridge listener. This listener is used for
861 * listening the events from integration bridge. To listen the events from
862 * other types of bridge such as provider bridge or tunnel bridge, we need
863 * to augment KubevirtNodeService.node() method.
864 */
865 private class InternalBridgeListener implements DeviceListener {
866
867 @Override
868 public boolean isRelevant(DeviceEvent event) {
869 return event.subject().type() == Device.Type.SWITCH;
870 }
871
872 private boolean isRelevantHelper() {
873 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
874 }
875
876 @Override
877 public void event(DeviceEvent event) {
878 Device device = event.subject();
879 Port port = event.port();
880
881 switch (event.type()) {
882 case DEVICE_AVAILABILITY_CHANGED:
883 case DEVICE_ADDED:
884 eventExecutor.execute(() -> processDeviceAddition(device));
885 break;
886 case PORT_UPDATED:
887 case PORT_ADDED:
888 eventExecutor.execute(() -> processPortAddition(device, port));
889 break;
890 case PORT_REMOVED:
891 eventExecutor.execute(() -> processPortRemoval(device, port));
892 break;
893 case DEVICE_REMOVED:
894 default:
895 // do nothing
896 break;
897 }
898 }
899
900 void processDeviceAddition(Device device) {
901 if (!isRelevantHelper()) {
902 return;
903 }
904
905 KubevirtNode node = nodeAdminService.node(device.id());
906
907 if (node == null) {
908 return;
909 }
910
911 if (deviceService.isAvailable(device.id())) {
912 log.debug("Bridge created on {}", node.hostname());
913 bootstrapNode(node);
914 } else if (node.state() == COMPLETE) {
915 log.info("Device {} disconnected", device.id());
916 setState(node, INCOMPLETE);
917 }
918
919 if (autoRecovery) {
920 if (node.state() == INCOMPLETE || node.state() == DEVICE_CREATED) {
921 log.info("Device {} is reconnected", device.id());
922 nodeAdminService.updateNode(node.updateState(INIT));
923 }
924 }
925 }
926
927 void processPortAddition(Device device, Port port) {
928 if (!isRelevantHelper()) {
929 return;
930 }
931
932 KubevirtNode node = nodeAdminService.nodeByTunBridge(device.id());
933
934 if (node == null) {
935 return;
936 }
937
938 String portName = port.annotations().value(PORT_NAME);
939 if (node.state() == DEVICE_CREATED && (
940 Objects.equals(portName, VXLAN) ||
941 Objects.equals(portName, GRE) ||
942 Objects.equals(portName, GENEVE))) {
943 log.info("Interface {} added or updated to {}",
944 portName, device.id());
945 bootstrapNode(node);
946 }
947 }
948
949 void processPortRemoval(Device device, Port port) {
950 if (!isRelevantHelper()) {
951 return;
952 }
953
954 KubevirtNode node = nodeAdminService.node(device.id());
955
956 if (node == null) {
957 return;
958 }
959
960 String portName = port.annotations().value(PORT_NAME);
961 if (node.state() == COMPLETE && (
962 Objects.equals(portName, VXLAN) ||
963 Objects.equals(portName, GRE) ||
964 Objects.equals(portName, GENEVE))) {
965 log.warn("Interface {} removed from {}", portName, device.id());
966 setState(node, INCOMPLETE);
967 }
968 }
969 }
970
971 /**
972 * An internal kubevirt node listener.
973 * The notification is triggered by KubevirtNodeStore.
974 */
975 private class InternalKubevirtNodeListener implements KubevirtNodeListener {
976
977 private boolean isRelevantHelper() {
978 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
979 }
980
981 @Override
982 public void event(KubevirtNodeEvent event) {
983 switch (event.type()) {
984 case KUBEVIRT_NODE_CREATED:
985 case KUBEVIRT_NODE_UPDATED:
986 eventExecutor.execute(() -> {
987 if (!isRelevantHelper()) {
988 return;
989 }
Jian Li517597a2021-03-22 11:04:52 +0900990 if (event.subject() == null) {
991 return;
992 }
Jian Li4fe40e52021-01-06 03:29:58 +0900993 bootstrapNode(event.subject());
994 });
995 break;
996 case KUBEVIRT_NODE_REMOVED:
997 eventExecutor.execute(() -> {
998 if (!isRelevantHelper()) {
999 return;
1000 }
1001 removeNode(event.subject());
1002 });
1003 break;
1004 case KUBEVIRT_NODE_INCOMPLETE:
1005 default:
1006 break;
1007 }
1008 }
1009 }
1010}