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