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