blob: e3ca07084191253ac5d675a34c93265745f49c32 [file] [log] [blame]
Jian Lif16e8852019-01-22 22:55:31 +09001/*
2 * Copyright 2019-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.k8snode.impl;
17
18import org.onlab.util.Tools;
19import org.onosproject.cfg.ComponentConfigService;
20import org.onosproject.cluster.ClusterService;
21import org.onosproject.cluster.LeadershipService;
22import org.onosproject.cluster.NodeId;
23import org.onosproject.core.ApplicationId;
24import org.onosproject.core.CoreService;
25import org.onosproject.k8snode.api.K8sNode;
26import org.onosproject.k8snode.api.K8sNodeAdminService;
27import org.onosproject.k8snode.api.K8sNodeEvent;
28import org.onosproject.k8snode.api.K8sNodeHandler;
29import org.onosproject.k8snode.api.K8sNodeListener;
30import org.onosproject.k8snode.api.K8sNodeService;
31import org.onosproject.k8snode.api.K8sNodeState;
32import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.Port;
35import org.onosproject.net.behaviour.BridgeConfig;
36import org.onosproject.net.behaviour.BridgeDescription;
37import org.onosproject.net.behaviour.ControllerInfo;
38import org.onosproject.net.behaviour.DefaultBridgeDescription;
Jian Libf562c22019-04-15 18:07:14 +090039import org.onosproject.net.behaviour.DefaultPatchDescription;
Jian Lif16e8852019-01-22 22:55:31 +090040import org.onosproject.net.behaviour.DefaultTunnelDescription;
41import org.onosproject.net.behaviour.InterfaceConfig;
Jian Libf562c22019-04-15 18:07:14 +090042import org.onosproject.net.behaviour.PatchDescription;
Jian Lif16e8852019-01-22 22:55:31 +090043import org.onosproject.net.behaviour.TunnelDescription;
44import org.onosproject.net.behaviour.TunnelEndPoints;
45import org.onosproject.net.behaviour.TunnelKeys;
46import org.onosproject.net.device.DeviceAdminService;
47import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceListener;
49import org.onosproject.net.device.DeviceService;
50import org.onosproject.ovsdb.controller.OvsdbClientService;
51import org.onosproject.ovsdb.controller.OvsdbController;
52import org.osgi.service.component.ComponentContext;
53import org.osgi.service.component.annotations.Activate;
54import org.osgi.service.component.annotations.Component;
55import org.osgi.service.component.annotations.Deactivate;
56import org.osgi.service.component.annotations.Modified;
57import org.osgi.service.component.annotations.Reference;
58import org.osgi.service.component.annotations.ReferenceCardinality;
59import org.slf4j.Logger;
60
61import java.util.Dictionary;
62import java.util.List;
63import java.util.Objects;
64import java.util.concurrent.ExecutorService;
65import java.util.stream.Collectors;
66
Jian Li4604b7f2020-01-03 18:42:30 +090067import static java.lang.Thread.sleep;
Jian Lif16e8852019-01-22 22:55:31 +090068import static java.util.concurrent.Executors.newSingleThreadExecutor;
69import static org.onlab.packet.TpPort.tpPort;
70import static org.onlab.util.Tools.groupedThreads;
Jian Libf562c22019-04-15 18:07:14 +090071import static org.onosproject.k8snode.api.Constants.EXTERNAL_BRIDGE;
Jian Lif16e8852019-01-22 22:55:31 +090072import static org.onosproject.k8snode.api.Constants.GENEVE;
73import static org.onosproject.k8snode.api.Constants.GENEVE_TUNNEL;
74import static org.onosproject.k8snode.api.Constants.GRE;
75import static org.onosproject.k8snode.api.Constants.GRE_TUNNEL;
76import static org.onosproject.k8snode.api.Constants.INTEGRATION_BRIDGE;
Jian Libf562c22019-04-15 18:07:14 +090077import static org.onosproject.k8snode.api.Constants.INTEGRATION_TO_EXTERNAL_BRIDGE;
Jian Li1a2eb5d2019-08-27 02:07:05 +090078import static org.onosproject.k8snode.api.Constants.INTEGRATION_TO_LOCAL_BRIDGE;
79import static org.onosproject.k8snode.api.Constants.LOCAL_BRIDGE;
80import static org.onosproject.k8snode.api.Constants.LOCAL_TO_INTEGRATION_BRIDGE;
Jian Libf562c22019-04-15 18:07:14 +090081import static org.onosproject.k8snode.api.Constants.PHYSICAL_EXTERNAL_BRIDGE;
Jian Lif16e8852019-01-22 22:55:31 +090082import static org.onosproject.k8snode.api.Constants.VXLAN;
83import static org.onosproject.k8snode.api.Constants.VXLAN_TUNNEL;
84import static org.onosproject.k8snode.api.K8sNodeService.APP_ID;
85import static org.onosproject.k8snode.api.K8sNodeState.COMPLETE;
86import static org.onosproject.k8snode.api.K8sNodeState.DEVICE_CREATED;
87import static org.onosproject.k8snode.api.K8sNodeState.INCOMPLETE;
88import static org.onosproject.k8snode.impl.OsgiPropertyConstants.AUTO_RECOVERY;
89import static org.onosproject.k8snode.impl.OsgiPropertyConstants.AUTO_RECOVERY_DEFAULT;
90import static org.onosproject.k8snode.impl.OsgiPropertyConstants.OVSDB_PORT;
91import static org.onosproject.k8snode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
92import static org.onosproject.k8snode.util.K8sNodeUtil.getBooleanProperty;
93import static org.onosproject.k8snode.util.K8sNodeUtil.getOvsdbClient;
94import static org.onosproject.k8snode.util.K8sNodeUtil.isOvsdbConnected;
95import static org.onosproject.net.AnnotationKeys.PORT_NAME;
96import static org.slf4j.LoggerFactory.getLogger;
97
98/**
99 * Service bootstraps kubernetes node based on its type.
100 */
101@Component(immediate = true,
102 property = {
103 OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT,
104 AUTO_RECOVERY + ":Boolean=" + AUTO_RECOVERY_DEFAULT
105 }
106)
107public class DefaultK8sNodeHandler implements K8sNodeHandler {
108
109 private final Logger log = getLogger(getClass());
110
111 private static final String DEFAULT_OF_PROTO = "tcp";
112 private static final int DEFAULT_OFPORT = 6653;
113 private static final int DPID_BEGIN = 3;
Jian Li4604b7f2020-01-03 18:42:30 +0900114 private static final long SLEEP_MS = 3000; // we wait 3s
Jian Lif16e8852019-01-22 22:55:31 +0900115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
117 protected CoreService coreService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
120 protected LeadershipService leadershipService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
123 protected ClusterService clusterService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
126 protected DeviceService deviceService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
129 protected DeviceAdminService deviceAdminService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
132 protected OvsdbController ovsdbController;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
135 protected K8sNodeService k8sNodeService;
136
137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
138 protected K8sNodeAdminService k8sNodeAdminService;
139
140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
141 protected ComponentConfigService componentConfigService;
142
143 /** OVSDB server listen port. */
144 private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
145
146 /** Indicates whether auto-recover kubernetes node status on switch re-conn event. */
147 private boolean autoRecovery = AUTO_RECOVERY_DEFAULT;
148
149 private final ExecutorService eventExecutor = newSingleThreadExecutor(
150 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
151
152 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
153 private final DeviceListener bridgeListener = new InternalBridgeListener();
154 private final K8sNodeListener k8sNodeListener = new InternalK8sNodeListener();
155
156 private ApplicationId appId;
157 private NodeId localNode;
158
159 @Activate
160 protected void activate() {
161 appId = coreService.getAppId(APP_ID);
162 localNode = clusterService.getLocalNode().id();
163
164 componentConfigService.registerProperties(getClass());
165 leadershipService.runForLeadership(appId.name());
166 deviceService.addListener(ovsdbListener);
167 deviceService.addListener(bridgeListener);
168 k8sNodeService.addListener(k8sNodeListener);
169
170 log.info("Started");
171 }
172
173 @Deactivate
174 protected void deactivate() {
175 k8sNodeService.removeListener(k8sNodeListener);
176 deviceService.removeListener(bridgeListener);
177 deviceService.removeListener(ovsdbListener);
178 componentConfigService.unregisterProperties(getClass(), false);
179 leadershipService.withdraw(appId.name());
180 eventExecutor.shutdown();
181
182 log.info("Stopped");
183 }
184
185 @Modified
186 protected void modified(ComponentContext context) {
187 readComponentConfiguration(context);
188
189 log.info("Modified");
190 }
191
192 @Override
193 public void processInitState(K8sNode k8sNode) {
194 if (!isOvsdbConnected(k8sNode, ovsdbPortNum, ovsdbController, deviceService)) {
195 ovsdbController.connect(k8sNode.managementIp(), tpPort(ovsdbPortNum));
196 return;
197 }
198 if (!deviceService.isAvailable(k8sNode.intgBridge())) {
199 createBridge(k8sNode, INTEGRATION_BRIDGE, k8sNode.intgBridge());
200 }
Jian Libf562c22019-04-15 18:07:14 +0900201 if (!deviceService.isAvailable(k8sNode.extBridge())) {
202 createBridge(k8sNode, EXTERNAL_BRIDGE, k8sNode.extBridge());
203 }
Jian Li1a2eb5d2019-08-27 02:07:05 +0900204 if (!deviceService.isAvailable(k8sNode.localBridge())) {
205 createBridge(k8sNode, LOCAL_BRIDGE, k8sNode.localBridge());
206 }
Jian Lif16e8852019-01-22 22:55:31 +0900207 }
208
209 @Override
210 public void processDeviceCreatedState(K8sNode k8sNode) {
211 try {
212 if (!isOvsdbConnected(k8sNode, ovsdbPortNum, ovsdbController, deviceService)) {
213 ovsdbController.connect(k8sNode.managementIp(), tpPort(ovsdbPortNum));
214 return;
215 }
216
Jian Libf562c22019-04-15 18:07:14 +0900217 // create patch ports between integration and external bridges
218 createPatchInterfaces(k8sNode);
219
Jian Lif16e8852019-01-22 22:55:31 +0900220 if (k8sNode.dataIp() != null &&
221 !isIntfEnabled(k8sNode, VXLAN_TUNNEL)) {
222 createVxlanTunnelInterface(k8sNode);
223 }
224
225 if (k8sNode.dataIp() != null &&
226 !isIntfEnabled(k8sNode, GRE_TUNNEL)) {
227 createGreTunnelInterface(k8sNode);
228 }
229
230 if (k8sNode.dataIp() != null &&
231 !isIntfEnabled(k8sNode, GENEVE_TUNNEL)) {
232 createGeneveTunnelInterface(k8sNode);
233 }
234 } catch (Exception e) {
235 log.error("Exception occurred because of {}", e);
236 }
237 }
238
239 @Override
240 public void processCompleteState(K8sNode k8sNode) {
241 // do something if needed
242 }
243
244 @Override
245 public void processIncompleteState(K8sNode k8sNode) {
246 // do something if needed
247 }
248
Jian Li0ee8d0e2019-12-18 11:35:05 +0900249 @Override
250 public void processPreOnBoardState(K8sNode k8sNode) {
251 processInitState(k8sNode);
252 processDeviceCreatedState(k8sNode);
253 }
254
255 @Override
256 public void processOnBoardedState(K8sNode k8sNode) {
257 // do something if needed
258 }
259
Jian Li3b640af2020-01-02 23:57:13 +0900260 @Override
261 public void processPostOnBoardState(K8sNode k8sNode) {
262 // do something if needed
263 }
264
Jian Lif16e8852019-01-22 22:55:31 +0900265 /**
266 * Extracts properties from the component configuration context.
267 *
268 * @param context the component context
269 */
270 private void readComponentConfiguration(ComponentContext context) {
271 Dictionary<?, ?> properties = context.getProperties();
272
273 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
274 if (ovsdbPortConfigured == null) {
275 ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
276 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPortNum);
277 } else {
278 ovsdbPortNum = ovsdbPortConfigured;
279 log.info("Configured. OVSDB port is {}", ovsdbPortNum);
280 }
281
282 Boolean autoRecoveryConfigured =
283 getBooleanProperty(properties, AUTO_RECOVERY);
284 if (autoRecoveryConfigured == null) {
285 autoRecovery = AUTO_RECOVERY_DEFAULT;
286 log.info("Auto recovery flag is NOT " +
287 "configured, default value is {}", autoRecovery);
288 } else {
289 autoRecovery = autoRecoveryConfigured;
290 log.info("Configured. Auto recovery flag is {}", autoRecovery);
291 }
292 }
293
294 /**
295 * Creates a bridge with a given name on a given kubernetes node.
296 *
297 * @param k8sNode kubernetes node
298 * @param bridgeName bridge name
299 * @param devId device identifier
300 */
301 private void createBridge(K8sNode k8sNode, String bridgeName, DeviceId devId) {
302 Device device = deviceService.getDevice(k8sNode.ovsdb());
303
304 List<ControllerInfo> controllers = clusterService.getNodes().stream()
305 .map(n -> new ControllerInfo(n.ip(), DEFAULT_OFPORT, DEFAULT_OF_PROTO))
306 .collect(Collectors.toList());
307
308 String dpid = devId.toString().substring(DPID_BEGIN);
309
310 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
311 .name(bridgeName)
312 .failMode(BridgeDescription.FailMode.SECURE)
313 .datapathId(dpid)
314 .disableInBand()
315 .controllers(controllers);
316
317 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
318 bridgeConfig.addBridge(builder.build());
319 }
320
321 /**
322 * Creates a VXLAN tunnel interface in a given kubernetes node.
323 *
324 * @param k8sNode kubernetes node
325 */
326 private void createVxlanTunnelInterface(K8sNode k8sNode) {
327 createTunnelInterface(k8sNode, VXLAN, VXLAN_TUNNEL);
328 }
329
330 /**
331 * Creates a GRE tunnel interface in a given kubernetes node.
332 *
333 * @param k8sNode kubernetes node
334 */
335 private void createGreTunnelInterface(K8sNode k8sNode) {
336 createTunnelInterface(k8sNode, GRE, GRE_TUNNEL);
337 }
338
339 /**
340 * Creates a GENEVE tunnel interface in a given kubernetes node.
341 *
342 * @param k8sNode kubernetes node
343 */
344 private void createGeneveTunnelInterface(K8sNode k8sNode) {
345 createTunnelInterface(k8sNode, GENEVE, GENEVE_TUNNEL);
346 }
347
Jian Libf562c22019-04-15 18:07:14 +0900348 private void createPatchInterfaces(K8sNode k8sNode) {
349 Device device = deviceService.getDevice(k8sNode.ovsdb());
350 if (device == null || !device.is(InterfaceConfig.class)) {
351 log.error("Failed to create patch interface on {}", k8sNode.ovsdb());
352 return;
353 }
354
Jian Li1a2eb5d2019-08-27 02:07:05 +0900355 // integration bridge -> external bridge
356 PatchDescription brIntExtPatchDesc =
Jian Libf562c22019-04-15 18:07:14 +0900357 DefaultPatchDescription.builder()
358 .deviceId(INTEGRATION_BRIDGE)
359 .ifaceName(INTEGRATION_TO_EXTERNAL_BRIDGE)
360 .peer(PHYSICAL_EXTERNAL_BRIDGE)
361 .build();
362
Jian Li1a2eb5d2019-08-27 02:07:05 +0900363 // external bridge -> integration bridge
364 PatchDescription brExtIntPatchDesc =
Jian Libf562c22019-04-15 18:07:14 +0900365 DefaultPatchDescription.builder()
366 .deviceId(EXTERNAL_BRIDGE)
367 .ifaceName(PHYSICAL_EXTERNAL_BRIDGE)
368 .peer(INTEGRATION_TO_EXTERNAL_BRIDGE)
369 .build();
370
Jian Li1a2eb5d2019-08-27 02:07:05 +0900371 // integration bridge -> local bridge
372 PatchDescription brIntLocalPatchDesc =
373 DefaultPatchDescription.builder()
374 .deviceId(INTEGRATION_BRIDGE)
375 .ifaceName(INTEGRATION_TO_LOCAL_BRIDGE)
376 .peer(LOCAL_TO_INTEGRATION_BRIDGE)
377 .build();
378
379 // local bridge -> integration bridge
380 PatchDescription brLocalIntPatchDesc =
381 DefaultPatchDescription.builder()
382 .deviceId(LOCAL_BRIDGE)
383 .ifaceName(LOCAL_TO_INTEGRATION_BRIDGE)
384 .peer(INTEGRATION_TO_LOCAL_BRIDGE)
385 .build();
386
Jian Libf562c22019-04-15 18:07:14 +0900387 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Jian Li1a2eb5d2019-08-27 02:07:05 +0900388 ifaceConfig.addPatchMode(INTEGRATION_TO_EXTERNAL_BRIDGE, brIntExtPatchDesc);
389 ifaceConfig.addPatchMode(PHYSICAL_EXTERNAL_BRIDGE, brExtIntPatchDesc);
390 ifaceConfig.addPatchMode(INTEGRATION_TO_LOCAL_BRIDGE, brIntLocalPatchDesc);
391 ifaceConfig.addPatchMode(LOCAL_TO_INTEGRATION_BRIDGE, brLocalIntPatchDesc);
Jian Libf562c22019-04-15 18:07:14 +0900392 }
393
Jian Lif16e8852019-01-22 22:55:31 +0900394 /**
395 * Creates a tunnel interface in a given kubernetes node.
396 *
397 * @param k8sNode kubernetes node
398 */
399 private void createTunnelInterface(K8sNode k8sNode,
400 String type, String intfName) {
401 if (isIntfEnabled(k8sNode, intfName)) {
402 return;
403 }
404
405 Device device = deviceService.getDevice(k8sNode.ovsdb());
406 if (device == null || !device.is(InterfaceConfig.class)) {
407 log.error("Failed to create tunnel interface on {}", k8sNode.ovsdb());
408 return;
409 }
410
411 TunnelDescription tunnelDesc = buildTunnelDesc(type, intfName);
412
413 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
414 ifaceConfig.addTunnelMode(intfName, tunnelDesc);
415 }
416
417 /**
418 * Builds tunnel description according to the network type.
419 *
420 * @param type network type
421 * @return tunnel description
422 */
423 private TunnelDescription buildTunnelDesc(String type, String intfName) {
424 if (VXLAN.equals(type) || GRE.equals(type) || GENEVE.equals(type)) {
425 TunnelDescription.Builder tdBuilder =
426 DefaultTunnelDescription.builder()
427 .deviceId(INTEGRATION_BRIDGE)
428 .ifaceName(intfName)
429 .remote(TunnelEndPoints.flowTunnelEndpoint())
430 .key(TunnelKeys.flowTunnelKey());
431
432 switch (type) {
433 case VXLAN:
434 tdBuilder.type(TunnelDescription.Type.VXLAN);
435 break;
436 case GRE:
437 tdBuilder.type(TunnelDescription.Type.GRE);
438 break;
439 case GENEVE:
440 tdBuilder.type(TunnelDescription.Type.GENEVE);
441 break;
442 default:
443 return null;
444 }
445
446 return tdBuilder.build();
447 }
448 return null;
449 }
450
451 /**
452 * Checks whether a given network interface in a given kubernetes node
453 * is enabled or not.
454 *
455 * @param k8sNode kubernetes node
456 * @param intf network interface name
457 * @return true if the given interface is enabled, false otherwise
458 */
459 private boolean isIntfEnabled(K8sNode k8sNode, String intf) {
460 return deviceService.isAvailable(k8sNode.intgBridge()) &&
461 deviceService.getPorts(k8sNode.intgBridge()).stream()
462 .anyMatch(port -> Objects.equals(
463 port.annotations().value(PORT_NAME), intf) &&
464 port.isEnabled());
465 }
466
467 /**
468 * Checks whether all requirements for this state are fulfilled or not.
469 *
470 * @param k8sNode kubernetes node
471 * @return true if all requirements are fulfilled, false otherwise
472 */
473 private boolean isCurrentStateDone(K8sNode k8sNode) {
474 switch (k8sNode.state()) {
475 case INIT:
Jian Li0ee8d0e2019-12-18 11:35:05 +0900476 return isInitStateDone(k8sNode);
Jian Lif16e8852019-01-22 22:55:31 +0900477 case DEVICE_CREATED:
Jian Li0ee8d0e2019-12-18 11:35:05 +0900478 return isDeviceCreatedStateDone(k8sNode);
479 case PRE_ON_BOARD:
480 return isInitStateDone(k8sNode) && isDeviceCreatedStateDone(k8sNode);
Jian Lif16e8852019-01-22 22:55:31 +0900481 case COMPLETE:
482 case INCOMPLETE:
Jian Li0ee8d0e2019-12-18 11:35:05 +0900483 case ON_BOARDED:
Jian Li4604b7f2020-01-03 18:42:30 +0900484 case POST_ON_BOARD:
Jian Lif16e8852019-01-22 22:55:31 +0900485 // always return false
486 // run init CLI to re-trigger node bootstrap
487 return false;
488 default:
489 return true;
490 }
491 }
492
Jian Li0ee8d0e2019-12-18 11:35:05 +0900493 private boolean isInitStateDone(K8sNode k8sNode) {
494 if (!isOvsdbConnected(k8sNode, ovsdbPortNum,
495 ovsdbController, deviceService)) {
496 return false;
497 }
498
Jian Li4604b7f2020-01-03 18:42:30 +0900499 try {
500 // we need to wait a while, in case interface and bridge
501 // creation requires some time
502 sleep(SLEEP_MS);
503 } catch (InterruptedException e) {
504 log.error("Exception caused during init state checking...");
505 }
506
Jian Li0ee8d0e2019-12-18 11:35:05 +0900507 return k8sNode.intgBridge() != null && k8sNode.extBridge() != null &&
508 deviceService.isAvailable(k8sNode.intgBridge()) &&
509 deviceService.isAvailable(k8sNode.extBridge()) &&
510 deviceService.isAvailable(k8sNode.localBridge());
511 }
512
513 private boolean isDeviceCreatedStateDone(K8sNode k8sNode) {
Jian Li4604b7f2020-01-03 18:42:30 +0900514
515 try {
516 // we need to wait a while, in case interface and bridge
517 // creation requires some time
518 sleep(SLEEP_MS);
519 } catch (InterruptedException e) {
520 log.error("Exception caused during init state checking...");
521 }
522
Jian Li0ee8d0e2019-12-18 11:35:05 +0900523 if (k8sNode.dataIp() != null &&
524 !isIntfEnabled(k8sNode, VXLAN_TUNNEL)) {
525 return false;
526 }
527 if (k8sNode.dataIp() != null &&
528 !isIntfEnabled(k8sNode, GRE_TUNNEL)) {
529 return false;
530 }
531 if (k8sNode.dataIp() != null &&
532 !isIntfEnabled(k8sNode, GENEVE_TUNNEL)) {
533 return false;
534 }
535
536 return true;
537 }
538
Jian Lif16e8852019-01-22 22:55:31 +0900539 /**
540 * Configures the kubernetes node with new state.
541 *
542 * @param k8sNode kubernetes node
543 * @param newState a new state
544 */
545 private void setState(K8sNode k8sNode, K8sNodeState newState) {
546 if (k8sNode.state() == newState) {
547 return;
548 }
549 K8sNode updated = k8sNode.updateState(newState);
550 k8sNodeAdminService.updateNode(updated);
551 log.info("Changed {} state: {}", k8sNode.hostname(), newState);
552 }
553
554 /**
555 * Bootstraps a new kubernetes node.
556 *
557 * @param k8sNode kubernetes node
558 */
559 private void bootstrapNode(K8sNode k8sNode) {
560 if (isCurrentStateDone(k8sNode)) {
561 setState(k8sNode, k8sNode.state().nextState());
562 } else {
563 log.trace("Processing {} state for {}", k8sNode.state(),
564 k8sNode.hostname());
565 k8sNode.state().process(this, k8sNode);
566 }
567 }
568
569 private void processK8sNodeRemoved(K8sNode k8sNode) {
570 OvsdbClientService client = getOvsdbClient(k8sNode, ovsdbPortNum, ovsdbController);
571 if (client == null) {
572 log.info("Failed to get ovsdb client");
573 return;
574 }
575
576 // delete integration bridge from the node
577 client.dropBridge(INTEGRATION_BRIDGE);
578
Jian Libf562c22019-04-15 18:07:14 +0900579 // delete external bridge from the node
580 client.dropBridge(EXTERNAL_BRIDGE);
581
Jian Li1a2eb5d2019-08-27 02:07:05 +0900582 // delete local bridge from the node
583 client.dropBridge(LOCAL_BRIDGE);
584
Jian Lif16e8852019-01-22 22:55:31 +0900585 // disconnect ovsdb
586 client.disconnect();
587 }
588
589 /**
590 * An internal OVSDB listener. This listener is used for listening the
591 * network facing events from OVSDB device. If a new OVSDB device is detected,
592 * ONOS tries to bootstrap the kubernetes node.
593 */
594 private class InternalOvsdbListener implements DeviceListener {
595
596 @Override
597 public boolean isRelevant(DeviceEvent event) {
598 return event.subject().type() == Device.Type.CONTROLLER;
599 }
600
601 private boolean isRelevantHelper() {
602 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
603 }
604
605 @Override
606 public void event(DeviceEvent event) {
607 Device device = event.subject();
608
609 switch (event.type()) {
610 case DEVICE_AVAILABILITY_CHANGED:
611 case DEVICE_ADDED:
612 eventExecutor.execute(() -> {
613
614 if (!isRelevantHelper()) {
615 return;
616 }
617
618 K8sNode k8sNode = k8sNodeService.node(device.id());
619
620 if (k8sNode == null) {
621 return;
622 }
623
624 if (deviceService.isAvailable(device.id())) {
625 log.debug("OVSDB {} detected", device.id());
626 bootstrapNode(k8sNode);
627 }
628 });
629 break;
630 case PORT_ADDED:
631 case PORT_REMOVED:
632 case DEVICE_REMOVED:
633 default:
634 // do nothing
635 break;
636 }
637 }
638 }
639
640 /**
641 * An internal integration bridge listener. This listener is used for
642 * listening the events from integration bridge. To listen the events from
643 * other types of bridge such as provider bridge or tunnel bridge, we need
644 * to augment K8sNodeService.node() method.
645 */
646 private class InternalBridgeListener implements DeviceListener {
647
648 @Override
649 public boolean isRelevant(DeviceEvent event) {
650 return event.subject().type() == Device.Type.SWITCH;
651 }
652
653 private boolean isRelevantHelper() {
654 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
655 }
656
657 @Override
658 public void event(DeviceEvent event) {
659 Device device = event.subject();
660
661 switch (event.type()) {
662 case DEVICE_AVAILABILITY_CHANGED:
663 case DEVICE_ADDED:
664 eventExecutor.execute(() -> {
665
666 if (!isRelevantHelper()) {
667 return;
668 }
669
670 K8sNode k8sNode = k8sNodeService.node(device.id());
671
672 if (k8sNode == null) {
673 return;
674 }
675
Jian Libf562c22019-04-15 18:07:14 +0900676 // TODO: also need to check the external bridge's availability
Jian Li1a2eb5d2019-08-27 02:07:05 +0900677 // TODO: also need to check the local bridge's availability
Jian Lif16e8852019-01-22 22:55:31 +0900678 if (deviceService.isAvailable(device.id())) {
Jian Libf562c22019-04-15 18:07:14 +0900679 log.debug("Integration bridge created on {}",
680 k8sNode.hostname());
Jian Lif16e8852019-01-22 22:55:31 +0900681 bootstrapNode(k8sNode);
682 } else if (k8sNode.state() == COMPLETE) {
683 log.info("Device {} disconnected", device.id());
684 setState(k8sNode, INCOMPLETE);
685 }
686
687 if (autoRecovery) {
688 if (k8sNode.state() == INCOMPLETE ||
689 k8sNode.state() == DEVICE_CREATED) {
690 log.info("Device {} is reconnected", device.id());
691 k8sNodeAdminService.updateNode(
692 k8sNode.updateState(K8sNodeState.INIT));
693 }
694 }
695 });
696 break;
697 case PORT_UPDATED:
698 case PORT_ADDED:
699 eventExecutor.execute(() -> {
700
701 if (!isRelevantHelper()) {
702 return;
703 }
704
705 K8sNode k8sNode = k8sNodeService.node(device.id());
706
707 if (k8sNode == null) {
708 return;
709 }
710
711 Port port = event.port();
712 String portName = port.annotations().value(PORT_NAME);
713 if (k8sNode.state() == DEVICE_CREATED && (
714 Objects.equals(portName, VXLAN_TUNNEL) ||
715 Objects.equals(portName, GRE_TUNNEL) ||
716 Objects.equals(portName, GENEVE_TUNNEL))) {
717 log.info("Interface {} added or updated to {}",
718 portName, device.id());
719 bootstrapNode(k8sNode);
720 }
721 });
722 break;
723 case PORT_REMOVED:
724 eventExecutor.execute(() -> {
725
726 if (!isRelevantHelper()) {
727 return;
728 }
729
730 K8sNode k8sNode = k8sNodeService.node(device.id());
731
732 if (k8sNode == null) {
733 return;
734 }
735
736 Port port = event.port();
737 String portName = port.annotations().value(PORT_NAME);
738 if (k8sNode.state() == COMPLETE && (
739 Objects.equals(portName, VXLAN_TUNNEL) ||
740 Objects.equals(portName, GRE_TUNNEL) ||
741 Objects.equals(portName, GENEVE_TUNNEL))) {
742 log.warn("Interface {} removed from {}",
743 portName, event.subject().id());
744 setState(k8sNode, INCOMPLETE);
745 }
746 });
747 break;
748 case DEVICE_REMOVED:
749 default:
750 // do nothing
751 break;
752 }
753 }
754 }
755
756 /**
757 * An internal kubernetes node listener.
758 * The notification is triggered by KubernetesNodeStore.
759 */
760 private class InternalK8sNodeListener implements K8sNodeListener {
761
762 private boolean isRelevantHelper() {
763 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
764 }
765
766 @Override
767 public void event(K8sNodeEvent event) {
768 switch (event.type()) {
769 case K8S_NODE_CREATED:
770 case K8S_NODE_UPDATED:
771 eventExecutor.execute(() -> {
772
773 if (!isRelevantHelper()) {
774 return;
775 }
776
777 bootstrapNode(event.subject());
778 });
779 break;
780 case K8S_NODE_REMOVED:
781 eventExecutor.execute(() -> {
782
783 if (!isRelevantHelper()) {
784 return;
785 }
786
787 processK8sNodeRemoved(event.subject());
788 });
789 break;
790 case K8S_NODE_INCOMPLETE:
791 default:
792 break;
793 }
794 }
795 }
796}