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