blob: de451515f287303c8c00a632b358efa47e097206 [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 Li94b6d162021-04-15 17:09:11 +0900306 IpAddress serverIp;
307 String serviceFqdn = apiConfigService.apiConfig().serviceFqdn();
308 IpAddress serviceIp = null;
309
310 if (serviceFqdn != null) {
311 serviceIp = resolveHostname(serviceFqdn);
312 }
313
314 if (serviceIp != null) {
315 serverIp = serviceIp;
316 } else {
317 serverIp = apiConfigService.apiConfig().ipAddress();
318 }
319
Jian Li72f3dac2021-01-28 16:14:54 +0900320 ControllerInfo controlInfo = new ControllerInfo(serverIp, DEFAULT_OFPORT, DEFAULT_OF_PROTO);
321 List<ControllerInfo> controllers = Lists.newArrayList(controlInfo);
Jian Li4fe40e52021-01-06 03:29:58 +0900322
323 String dpid = devId.toString().substring(DPID_BEGIN);
324
325 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
326 .name(bridgeName)
327 .failMode(BridgeDescription.FailMode.SECURE)
328 .datapathId(dpid)
329 .disableInBand()
330 .controllers(controllers);
331
332 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
333 bridgeConfig.addBridge(builder.build());
334 }
335
336 /**
337 * Creates a VXLAN tunnel interface in a given kubevirt node.
338 *
339 * @param node kubevirt node
340 */
341 private void createVxlanTunnelInterface(KubevirtNode node) {
342 createTunnelInterface(node, VXLAN, VXLAN);
343 }
344
345 /**
346 * Creates a GRE tunnel interface in a given kubevirt node.
347 *
348 * @param node kubevirt node
349 */
350 private void createGreTunnelInterface(KubevirtNode node) {
351 createTunnelInterface(node, GRE, GRE);
352 }
353
354 /**
355 * Creates a GENEVE tunnel interface in a given kubevirt node.
356 *
357 * @param node kubevirt node
358 */
359 private void createGeneveTunnelInterface(KubevirtNode node) {
360 createTunnelInterface(node, GENEVE, GENEVE);
361 }
362
363 /**
364 * Creates a tunnel interface in a given kubernetes node.
365 *
366 * @param node kubevirt node
Jian Li4b249702021-02-19 18:13:10 +0900367 * @param type kubevirt type
368 * @param intfName tunnel interface name
Jian Li4fe40e52021-01-06 03:29:58 +0900369 */
370 private void createTunnelInterface(KubevirtNode node,
371 String type, String intfName) {
372 if (isIntfEnabled(node, intfName)) {
373 return;
374 }
375
376 Device device = deviceService.getDevice(node.ovsdb());
377 if (device == null || !device.is(InterfaceConfig.class)) {
378 log.error("Failed to create tunnel interface on {}", node.ovsdb());
379 return;
380 }
381
382 TunnelDescription tunnelDesc = buildTunnelDesc(type, intfName);
383
384 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
385 ifaceConfig.addTunnelMode(intfName, tunnelDesc);
386 }
387
388 /**
389 * Builds tunnel description according to the network type.
390 *
391 * @param type network type
Jian Li4b249702021-02-19 18:13:10 +0900392 * @param intfName tunnel interface
Jian Li4fe40e52021-01-06 03:29:58 +0900393 * @return tunnel description
394 */
395 private TunnelDescription buildTunnelDesc(String type, String intfName) {
396 TunnelKey<String> key = new TunnelKey<>(FLOW_KEY);
397 if (VXLAN.equals(type) || GRE.equals(type) || GENEVE.equals(type)) {
398 TunnelDescription.Builder tdBuilder =
399 DefaultTunnelDescription.builder()
400 .deviceId(TUNNEL_BRIDGE)
401 .ifaceName(intfName)
402 .remote(TunnelEndPoints.flowTunnelEndpoint())
403 .key(key);
404
405 switch (type) {
406 case VXLAN:
407 tdBuilder.type(TunnelDescription.Type.VXLAN);
408 break;
409 case GRE:
410 tdBuilder.type(TunnelDescription.Type.GRE);
411 break;
412 case GENEVE:
413 tdBuilder.type(TunnelDescription.Type.GENEVE);
414 break;
415 default:
416 return null;
417 }
418
419 return tdBuilder.build();
420 }
421 return null;
422 }
423
424 /**
425 * Checks whether a given network interface in a given kubernetes node
426 * is enabled or not.
427 *
428 * @param node kubevirt node
429 * @param intf network interface name
430 * @return true if the given interface is enabled, false otherwise
431 */
432 private boolean isIntfEnabled(KubevirtNode node, String intf) {
433 return deviceService.isAvailable(node.tunBridge()) &&
434 deviceService.getPorts(node.tunBridge()).stream()
435 .anyMatch(port -> Objects.equals(
436 port.annotations().value(PORT_NAME), intf) &&
437 port.isEnabled());
438 }
439
Jian Li4fe40e52021-01-06 03:29:58 +0900440 /**
441 * Bootstraps a new kubevirt node.
442 *
443 * @param node kubevirt node
444 */
445 private void bootstrapNode(KubevirtNode node) {
446 if (isCurrentStateDone(node)) {
447 setState(node, node.state().nextState());
448 } else {
449 log.trace("Processing {} state for {}", node.state(), node.hostname());
450 node.state().process(this, node);
451 }
452 }
453
454 /**
455 * Removes the existing kubevirt node.
456 *
457 * @param node kubevirt node
458 */
459 private void removeNode(KubevirtNode node) {
460 OvsdbClientService client = getOvsdbClient(node, ovsdbPortNum, ovsdbController);
461 if (client == null) {
462 log.info("Failed to get ovsdb client");
463 return;
464 }
465
Daniel Parka5ba88d2021-05-28 15:46:46 +0900466 // purges all the flow rules installed on the node
467 flowRuleService.purgeFlowRules(node.intgBridge());
468 flowRuleService.purgeFlowRules(node.tunBridge());
469
Jian Li4fe40e52021-01-06 03:29:58 +0900470 // unprovision physical interfaces from the node
471 // this procedure includes detaching physical port from physical bridge,
472 // remove patch ports from br-int, removing physical bridge
473 unprovisionPhysicalInterfaces(node);
474
475 // delete tunnel bridge from the node
476 client.dropBridge(TUNNEL_BRIDGE);
477
478 // delete integration bridge from the node
479 client.dropBridge(INTEGRATION_BRIDGE);
480 }
481
482 /**
483 * Checks whether all requirements for this state are fulfilled or not.
484 *
485 * @param node kubevirt node
486 * @return true if all requirements are fulfilled, false otherwise
487 */
488 private boolean isCurrentStateDone(KubevirtNode node) {
489 switch (node.state()) {
490 case INIT:
491 return isInitStateDone(node);
492 case DEVICE_CREATED:
493 return isDeviceCreatedStateDone(node);
494 case COMPLETE:
495 case INCOMPLETE:
496 case ON_BOARDED:
497 // always return false
498 // run init CLI to re-trigger node bootstrap
499 return false;
500 default:
501 return true;
502 }
503 }
504
505 private boolean isInitStateDone(KubevirtNode node) {
506 if (!isOvsdbConnected(node, ovsdbPortNum,
507 ovsdbController, deviceService)) {
508 return false;
509 }
510
511 try {
512 // we need to wait a while, in case interfaces and bridges
513 // creation requires some time
514 sleep(SLEEP_SHORT_MS);
515 } catch (InterruptedException e) {
516 log.error("Exception caused during init state checking...");
517 }
518
519 cleanPhysicalInterfaces(node);
520
Jian Li4b249702021-02-19 18:13:10 +0900521 // provision new physical interfaces on the given node
522 // this includes creating physical bridge, attaching physical port
523 // to physical bridge, adding patch ports to both physical bridge and br-int
524 provisionPhysicalInterfaces(node);
525
Daniel Parkf3136042021-03-10 07:49:11 +0900526 if (node.type() == GATEWAY) {
527 createPatchInterfaceBetweenBrIntBrTun(node);
528 }
529
Jian Li4fe40e52021-01-06 03:29:58 +0900530 return node.intgBridge() != null && node.tunBridge() != null &&
531 deviceService.isAvailable(node.intgBridge()) &&
532 deviceService.isAvailable(node.tunBridge());
533 }
534
535 private boolean isDeviceCreatedStateDone(KubevirtNode node) {
536
537 try {
538 // we need to wait a while, in case tunneling ports
539 // creation requires some time
Jian Li4b249702021-02-19 18:13:10 +0900540 sleep(SLEEP_MID_MS);
Jian Li4fe40e52021-01-06 03:29:58 +0900541 } catch (InterruptedException e) {
542 log.error("Exception caused during init state checking...");
543 }
544
545 if (node.dataIp() != null && !isIntfEnabled(node, VXLAN)) {
Jian Li4b249702021-02-19 18:13:10 +0900546 log.warn("VXLAN interface is not enabled!");
Jian Li4fe40e52021-01-06 03:29:58 +0900547 return false;
548 }
549 if (node.dataIp() != null && !isIntfEnabled(node, GRE)) {
Jian Li4b249702021-02-19 18:13:10 +0900550 log.warn("GRE interface is not enabled!");
Jian Li4fe40e52021-01-06 03:29:58 +0900551 return false;
552 }
553 if (node.dataIp() != null && !isIntfEnabled(node, GENEVE)) {
Jian Li4b249702021-02-19 18:13:10 +0900554 log.warn("GENEVE interface is not enabled!");
Jian Li4fe40e52021-01-06 03:29:58 +0900555 return false;
556 }
557
558 for (KubevirtPhyInterface phyIntf : node.phyIntfs()) {
559 if (phyIntf == null) {
Jian Li4b249702021-02-19 18:13:10 +0900560 log.warn("Physnet interface is invalid");
Jian Li4fe40e52021-01-06 03:29:58 +0900561 return false;
562 }
563
Jian Li4b249702021-02-19 18:13:10 +0900564 try {
565 // we need to wait a while, in case tunneling ports
566 // creation requires some time
567 sleep(SLEEP_LONG_MS);
568 } catch (InterruptedException e) {
569 log.error("Exception caused during init state checking...");
570 }
571
Jian Li4fe40e52021-01-06 03:29:58 +0900572 String bridgeName = BRIDGE_PREFIX + phyIntf.network();
573 String patchPortName = structurePortName(
574 INTEGRATION_TO_PHYSICAL_PREFIX + phyIntf.network());
575
576 if (!(hasPhyBridge(node, bridgeName) &&
577 hasPhyPatchPort(node, patchPortName) &&
578 hasPhyIntf(node, phyIntf.intf()))) {
Jian Li4b249702021-02-19 18:13:10 +0900579 log.warn("PhyBridge {}", hasPhyBridge(node, bridgeName));
580 log.warn("hasPhyPatchPort {}", hasPhyPatchPort(node, patchPortName));
581 log.warn("hasPhyIntf {}", hasPhyIntf(node, phyIntf.intf()));
Jian Li4fe40e52021-01-06 03:29:58 +0900582 return false;
583 }
584 }
585
Daniel Parkf3136042021-03-10 07:49:11 +0900586 if (node.type() == GATEWAY) {
587 if (!(hasPhyIntf(node, INTEGRATION_TO_TUNNEL) &&
588 hasPhyIntf(node, TUNNEL_TO_INTEGRATION))) {
589 log.warn("IntToTunPort {}", hasPhyIntf(node, INTEGRATION_TO_TUNNEL));
590 log.warn("TunToIntPort {}", hasPhyIntf(node, TUNNEL_TO_INTEGRATION));
591 return false;
592 }
593 }
Jian Li4fe40e52021-01-06 03:29:58 +0900594 return true;
595 }
596
597 /**
598 * Configures the kubernetes node with new state.
599 *
600 * @param node kubevirt node
601 * @param newState a new state
602 */
603 private void setState(KubevirtNode node, KubevirtNodeState newState) {
604 if (node.state() == newState) {
605 return;
606 }
607 KubevirtNode updated = node.updateState(newState);
608 nodeAdminService.updateNode(updated);
609 log.info("Changed {} state: {}", node.hostname(), newState);
610 }
611
612 private void provisionPhysicalInterfaces(KubevirtNode node) {
613 node.phyIntfs().forEach(pi -> {
614 String bridgeName = BRIDGE_PREFIX + pi.network();
615 String patchPortName =
616 structurePortName(INTEGRATION_TO_PHYSICAL_PREFIX + pi.network());
617
618 if (!hasPhyBridge(node, bridgeName)) {
619 createPhysicalBridge(node, pi);
620 createPhysicalPatchPorts(node, pi);
621 attachPhysicalPort(node, pi);
Jian Li4b249702021-02-19 18:13:10 +0900622
623 log.info("Creating physnet bridge {}", bridgeName);
624 log.info("Creating patch ports for physnet {}", bridgeName);
Jian Li4fe40e52021-01-06 03:29:58 +0900625 } else {
Jian Li517597a2021-03-22 11:04:52 +0900626 // in case physical bridge exists, but patch port is missing,
627 // we will add patch port to connect br-physnet with physical bridge
Jian Li4fe40e52021-01-06 03:29:58 +0900628 if (!hasPhyPatchPort(node, patchPortName)) {
629 createPhysicalPatchPorts(node, pi);
Jian Li4b249702021-02-19 18:13:10 +0900630
631 log.info("Creating patch ports for physnet {}", bridgeName);
Jian Li4fe40e52021-01-06 03:29:58 +0900632 }
Jian Li517597a2021-03-22 11:04:52 +0900633
634 // in case physical bridge exists, but physnet interface is missing,
635 // we will add the physnet interface to connect br-physnet to the external
636 if (!hasPhyIntf(node, pi.intf())) {
637 attachPhysicalPort(node, pi);
638
639 log.info("Attaching external ports for physnet {}", bridgeName);
640 }
Jian Li4fe40e52021-01-06 03:29:58 +0900641 }
642 });
643 }
644
645 private void cleanPhysicalInterfaces(KubevirtNode node) {
646 Device device = deviceService.getDevice(node.ovsdb());
647
648 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
649
650 Set<String> bridgeNames = bridgeConfig.getBridges().stream()
651 .map(BridgeDescription::name).collect(Collectors.toSet());
652
653 Set<String> phyNetworkNames = node.phyIntfs().stream()
654 .map(pi -> BRIDGE_PREFIX + pi.network()).collect(Collectors.toSet());
655
656 // we remove existing physical bridges and patch ports, if the physical
657 // bridges are not defined in kubevirt node
658 for (String brName : bridgeNames) {
Jian Li4b249702021-02-19 18:13:10 +0900659 // integration bridge and tunnel bridge should NOT be treated as
660 // physical bridges
661 if (brName.equals(INTEGRATION_BRIDGE) ||
662 brName.equals(TUNNEL_BRIDGE) ||
663 brName.startsWith(TENANT_BRIDGE_PREFIX)) {
664 continue;
665 }
666
Jian Li4fe40e52021-01-06 03:29:58 +0900667 if (!phyNetworkNames.contains(brName)) {
Jian Li4fe40e52021-01-06 03:29:58 +0900668 removePhysicalPatchPorts(node, brName.substring(NETWORK_BEGIN));
669 removePhysicalBridge(node, brName.substring(NETWORK_BEGIN));
Jian Li4b249702021-02-19 18:13:10 +0900670 log.info("Removing physical bridge {}...", brName);
Jian Li4fe40e52021-01-06 03:29:58 +0900671 }
672 }
673 }
674
Daniel Parkf3136042021-03-10 07:49:11 +0900675
676 private void createPatchInterfaceBetweenBrIntBrTun(KubevirtNode node) {
677 Device device = deviceService.getDevice(node.ovsdb());
678
679 if (device == null || !device.is(InterfaceConfig.class)) {
680 log.error("Failed to create patch interface on {}", node.ovsdb());
681 return;
682 }
683
684 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
685
686 // int bridge -> tunnel bridge
687 PatchDescription brIntTunPatchDesc =
688 DefaultPatchDescription.builder()
689 .deviceId(INTEGRATION_BRIDGE)
690 .ifaceName(INTEGRATION_TO_TUNNEL)
691 .peer(TUNNEL_TO_INTEGRATION)
692 .build();
693
694 ifaceConfig.addPatchMode(INTEGRATION_TO_TUNNEL, brIntTunPatchDesc);
695
696 // tunnel bridge -> int bridge
697 PatchDescription brTunIntPatchDesc =
698 DefaultPatchDescription.builder()
699 .deviceId(TUNNEL_BRIDGE)
700 .ifaceName(TUNNEL_TO_INTEGRATION)
701 .peer(INTEGRATION_TO_TUNNEL)
702 .build();
703
704 ifaceConfig.addPatchMode(TUNNEL_TO_INTEGRATION, brTunIntPatchDesc);
705 }
706
Jian Li4fe40e52021-01-06 03:29:58 +0900707 private void unprovisionPhysicalInterfaces(KubevirtNode node) {
708 node.phyIntfs().forEach(pi -> {
709 detachPhysicalPort(node, pi.network(), pi.intf());
710 removePhysicalPatchPorts(node, pi.network());
711 removePhysicalBridge(node, pi.network());
712 });
713 }
714
715 private boolean hasPhyBridge(KubevirtNode node, String bridgeName) {
716 BridgeConfig bridgeConfig =
717 deviceService.getDevice(node.ovsdb()).as(BridgeConfig.class);
718 return bridgeConfig.getBridges().stream()
719 .anyMatch(br -> br.name().equals(bridgeName));
720 }
721
722 private boolean hasPhyPatchPort(KubevirtNode node, String patchPortName) {
723 List<Port> ports = deviceService.getPorts(node.intgBridge());
724 return ports.stream().anyMatch(p ->
725 p.annotations().value(PORT_NAME).equals(patchPortName));
726 }
727
728 private boolean hasPhyIntf(KubevirtNode node, String intfName) {
729 BridgeConfig bridgeConfig =
730 deviceService.getDevice(node.ovsdb()).as(BridgeConfig.class);
731 return bridgeConfig.getPorts().stream()
732 .anyMatch(p -> p.annotations().value(PORT_NAME).equals(intfName));
733 }
734
735 private void createPhysicalBridge(KubevirtNode osNode,
736 KubevirtPhyInterface phyInterface) {
737 Device device = deviceService.getDevice(osNode.ovsdb());
738
739 String bridgeName = BRIDGE_PREFIX + phyInterface.network();
740
741 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
742 .name(bridgeName)
743 .mcastSnoopingEnable();
744
745 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
746 bridgeConfig.addBridge(builder.build());
747 }
748
749 private void removePhysicalBridge(KubevirtNode node, String network) {
750 Device device = deviceService.getDevice(node.ovsdb());
751
752 BridgeName bridgeName = BridgeName.bridgeName(BRIDGE_PREFIX + network);
753
754 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
755 bridgeConfig.deleteBridge(bridgeName);
756 }
757
758 private void createPhysicalPatchPorts(KubevirtNode node,
759 KubevirtPhyInterface phyInterface) {
760 Device device = deviceService.getDevice(node.ovsdb());
761
762 if (device == null || !device.is(InterfaceConfig.class)) {
763 log.error("Failed to create patch interface on {}", node.ovsdb());
764 return;
765 }
766
767 String physicalDeviceId = BRIDGE_PREFIX + phyInterface.network();
768
769 String intToPhyPatchPort = structurePortName(
770 INTEGRATION_TO_PHYSICAL_PREFIX + phyInterface.network());
771 String phyToIntPatchPort = structurePortName(
772 phyInterface.network() + PHYSICAL_TO_INTEGRATION_SUFFIX);
773
774 // integration bridge -> physical bridge
775 PatchDescription intToPhyPatchDesc =
776 DefaultPatchDescription.builder()
777 .deviceId(INTEGRATION_BRIDGE)
778 .ifaceName(intToPhyPatchPort)
779 .peer(phyToIntPatchPort)
780 .build();
781
782 // physical bridge -> integration bridge
783 PatchDescription phyToIntPatchDesc =
784 DefaultPatchDescription.builder()
785 .deviceId(physicalDeviceId)
786 .ifaceName(phyToIntPatchPort)
787 .peer(intToPhyPatchPort)
788 .build();
789
790 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
791 ifaceConfig.addPatchMode(INTEGRATION_TO_PHYSICAL_PREFIX +
792 phyInterface.network(), intToPhyPatchDesc);
793 ifaceConfig.addPatchMode(phyInterface.network() +
794 PHYSICAL_TO_INTEGRATION_SUFFIX, phyToIntPatchDesc);
795
796 addOrRemoveSystemInterface(node, physicalDeviceId,
797 phyInterface.intf(), deviceService, true);
798 }
799
800 private void removePhysicalPatchPorts(KubevirtNode node, String network) {
801 Device device = deviceService.getDevice(node.ovsdb());
802
803 if (device == null || !device.is(InterfaceConfig.class)) {
804 log.error("Failed to remove patch interface on {}", node.ovsdb());
805 return;
806 }
807
808 String intToPhyPatchPort = structurePortName(
809 INTEGRATION_TO_PHYSICAL_PREFIX + network);
810
811 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
812 ifaceConfig.removePatchMode(intToPhyPatchPort);
813 }
814
815 private void attachPhysicalPort(KubevirtNode node,
816 KubevirtPhyInterface phyInterface) {
817
818 String physicalDeviceId = BRIDGE_PREFIX + phyInterface.network();
819
820 addOrRemoveSystemInterface(node, physicalDeviceId,
821 phyInterface.intf(), deviceService, true);
822 }
823
824 private void detachPhysicalPort(KubevirtNode node, String network, String portName) {
825 String physicalDeviceId = BRIDGE_PREFIX + network;
826
827 addOrRemoveSystemInterface(node, physicalDeviceId, portName, deviceService, false);
828 }
829
830 /**
831 * An internal OVSDB listener. This listener is used for listening the
832 * network facing events from OVSDB device. If a new OVSDB device is detected,
833 * ONOS tries to bootstrap the kubernetes node.
834 */
835 private class InternalOvsdbListener implements DeviceListener {
836
837 @Override
838 public boolean isRelevant(DeviceEvent event) {
839 return event.subject().type() == Device.Type.CONTROLLER;
840 }
841
842 private boolean isRelevantHelper() {
843 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
844 }
845
846 @Override
847 public void event(DeviceEvent event) {
848 Device device = event.subject();
849
850 switch (event.type()) {
851 case DEVICE_AVAILABILITY_CHANGED:
852 case DEVICE_ADDED:
853 eventExecutor.execute(() -> {
854
855 if (!isRelevantHelper()) {
856 return;
857 }
858
859 KubevirtNode node = nodeAdminService.node(device.id());
860
861 if (node == null) {
862 return;
863 }
864
865 if (deviceService.isAvailable(device.id())) {
866 log.debug("OVSDB {} detected", device.id());
867 bootstrapNode(node);
868 }
869 });
870 break;
871 case PORT_ADDED:
872 case PORT_REMOVED:
873 case DEVICE_REMOVED:
874 default:
875 // do nothing
876 break;
877 }
878 }
879 }
880
881 /**
882 * An internal integration bridge listener. This listener is used for
883 * listening the events from integration bridge. To listen the events from
884 * other types of bridge such as provider bridge or tunnel bridge, we need
885 * to augment KubevirtNodeService.node() method.
886 */
887 private class InternalBridgeListener implements DeviceListener {
888
889 @Override
890 public boolean isRelevant(DeviceEvent event) {
891 return event.subject().type() == Device.Type.SWITCH;
892 }
893
894 private boolean isRelevantHelper() {
895 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
896 }
897
898 @Override
899 public void event(DeviceEvent event) {
900 Device device = event.subject();
901 Port port = event.port();
902
903 switch (event.type()) {
904 case DEVICE_AVAILABILITY_CHANGED:
905 case DEVICE_ADDED:
906 eventExecutor.execute(() -> processDeviceAddition(device));
907 break;
908 case PORT_UPDATED:
909 case PORT_ADDED:
910 eventExecutor.execute(() -> processPortAddition(device, port));
911 break;
912 case PORT_REMOVED:
913 eventExecutor.execute(() -> processPortRemoval(device, port));
914 break;
915 case DEVICE_REMOVED:
916 default:
917 // do nothing
918 break;
919 }
920 }
921
922 void processDeviceAddition(Device device) {
923 if (!isRelevantHelper()) {
924 return;
925 }
926
927 KubevirtNode node = nodeAdminService.node(device.id());
928
929 if (node == null) {
930 return;
931 }
932
933 if (deviceService.isAvailable(device.id())) {
934 log.debug("Bridge created on {}", node.hostname());
935 bootstrapNode(node);
936 } else if (node.state() == COMPLETE) {
937 log.info("Device {} disconnected", device.id());
938 setState(node, INCOMPLETE);
939 }
940
941 if (autoRecovery) {
942 if (node.state() == INCOMPLETE || node.state() == DEVICE_CREATED) {
943 log.info("Device {} is reconnected", device.id());
944 nodeAdminService.updateNode(node.updateState(INIT));
945 }
946 }
947 }
948
949 void processPortAddition(Device device, Port port) {
950 if (!isRelevantHelper()) {
951 return;
952 }
953
954 KubevirtNode node = nodeAdminService.nodeByTunBridge(device.id());
955
956 if (node == null) {
957 return;
958 }
959
960 String portName = port.annotations().value(PORT_NAME);
961 if (node.state() == DEVICE_CREATED && (
962 Objects.equals(portName, VXLAN) ||
963 Objects.equals(portName, GRE) ||
964 Objects.equals(portName, GENEVE))) {
965 log.info("Interface {} added or updated to {}",
966 portName, device.id());
967 bootstrapNode(node);
968 }
969 }
970
971 void processPortRemoval(Device device, Port port) {
972 if (!isRelevantHelper()) {
973 return;
974 }
975
976 KubevirtNode node = nodeAdminService.node(device.id());
977
978 if (node == null) {
979 return;
980 }
981
982 String portName = port.annotations().value(PORT_NAME);
983 if (node.state() == COMPLETE && (
984 Objects.equals(portName, VXLAN) ||
985 Objects.equals(portName, GRE) ||
986 Objects.equals(portName, GENEVE))) {
987 log.warn("Interface {} removed from {}", portName, device.id());
988 setState(node, INCOMPLETE);
989 }
990 }
991 }
992
993 /**
994 * An internal kubevirt node listener.
995 * The notification is triggered by KubevirtNodeStore.
996 */
997 private class InternalKubevirtNodeListener implements KubevirtNodeListener {
998
999 private boolean isRelevantHelper() {
1000 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
1001 }
1002
1003 @Override
1004 public void event(KubevirtNodeEvent event) {
1005 switch (event.type()) {
1006 case KUBEVIRT_NODE_CREATED:
1007 case KUBEVIRT_NODE_UPDATED:
1008 eventExecutor.execute(() -> {
1009 if (!isRelevantHelper()) {
1010 return;
1011 }
Jian Li517597a2021-03-22 11:04:52 +09001012 if (event.subject() == null) {
1013 return;
1014 }
Jian Li4fe40e52021-01-06 03:29:58 +09001015 bootstrapNode(event.subject());
1016 });
1017 break;
1018 case KUBEVIRT_NODE_REMOVED:
1019 eventExecutor.execute(() -> {
1020 if (!isRelevantHelper()) {
1021 return;
1022 }
1023 removeNode(event.subject());
1024 });
1025 break;
1026 case KUBEVIRT_NODE_INCOMPLETE:
1027 default:
1028 break;
1029 }
1030 }
1031 }
1032}