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