blob: 62db27d3f516cdd9516a41cc06ba530e4b5cd1f0 [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;
Jian Lie2a04ce2020-07-01 19:07:02 +090045import org.onosproject.net.behaviour.TunnelKey;
Jian Lif16e8852019-01-22 22:55:31 +090046import 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;
71import static org.onosproject.k8snode.api.Constants.GENEVE;
Jian Lif16e8852019-01-22 22:55:31 +090072import static org.onosproject.k8snode.api.Constants.GRE;
Jian Lif16e8852019-01-22 22:55:31 +090073import static org.onosproject.k8snode.api.Constants.VXLAN;
Jian Lie2a04ce2020-07-01 19:07:02 +090074import static org.onosproject.k8snode.api.K8sApiConfig.Mode.NORMAL;
Jian Lif16e8852019-01-22 22:55:31 +090075import static org.onosproject.k8snode.api.K8sNodeService.APP_ID;
76import static org.onosproject.k8snode.api.K8sNodeState.COMPLETE;
77import static org.onosproject.k8snode.api.K8sNodeState.DEVICE_CREATED;
78import static org.onosproject.k8snode.api.K8sNodeState.INCOMPLETE;
79import static org.onosproject.k8snode.impl.OsgiPropertyConstants.AUTO_RECOVERY;
80import static org.onosproject.k8snode.impl.OsgiPropertyConstants.AUTO_RECOVERY_DEFAULT;
81import static org.onosproject.k8snode.impl.OsgiPropertyConstants.OVSDB_PORT;
82import static org.onosproject.k8snode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
83import static org.onosproject.k8snode.util.K8sNodeUtil.getBooleanProperty;
84import static org.onosproject.k8snode.util.K8sNodeUtil.getOvsdbClient;
85import static org.onosproject.k8snode.util.K8sNodeUtil.isOvsdbConnected;
86import static org.onosproject.net.AnnotationKeys.PORT_NAME;
87import static org.slf4j.LoggerFactory.getLogger;
88
89/**
90 * Service bootstraps kubernetes node based on its type.
91 */
92@Component(immediate = true,
93 property = {
94 OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT,
95 AUTO_RECOVERY + ":Boolean=" + AUTO_RECOVERY_DEFAULT
96 }
97)
98public class DefaultK8sNodeHandler implements K8sNodeHandler {
99
100 private final Logger log = getLogger(getClass());
101
102 private static final String DEFAULT_OF_PROTO = "tcp";
103 private static final int DEFAULT_OFPORT = 6653;
104 private static final int DPID_BEGIN = 3;
Jian Li4604b7f2020-01-03 18:42:30 +0900105 private static final long SLEEP_MS = 3000; // we wait 3s
Jian Lif16e8852019-01-22 22:55:31 +0900106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
108 protected CoreService coreService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
111 protected LeadershipService leadershipService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
114 protected ClusterService clusterService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
117 protected DeviceService deviceService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
120 protected DeviceAdminService deviceAdminService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
123 protected OvsdbController ovsdbController;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
126 protected K8sNodeService k8sNodeService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
129 protected K8sNodeAdminService k8sNodeAdminService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
132 protected ComponentConfigService componentConfigService;
133
134 /** OVSDB server listen port. */
135 private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
136
137 /** Indicates whether auto-recover kubernetes node status on switch re-conn event. */
138 private boolean autoRecovery = AUTO_RECOVERY_DEFAULT;
139
140 private final ExecutorService eventExecutor = newSingleThreadExecutor(
141 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
142
143 private final DeviceListener ovsdbListener = new InternalOvsdbListener();
144 private final DeviceListener bridgeListener = new InternalBridgeListener();
145 private final K8sNodeListener k8sNodeListener = new InternalK8sNodeListener();
146
147 private ApplicationId appId;
148 private NodeId localNode;
149
150 @Activate
151 protected void activate() {
152 appId = coreService.getAppId(APP_ID);
153 localNode = clusterService.getLocalNode().id();
154
155 componentConfigService.registerProperties(getClass());
156 leadershipService.runForLeadership(appId.name());
157 deviceService.addListener(ovsdbListener);
158 deviceService.addListener(bridgeListener);
159 k8sNodeService.addListener(k8sNodeListener);
160
161 log.info("Started");
162 }
163
164 @Deactivate
165 protected void deactivate() {
166 k8sNodeService.removeListener(k8sNodeListener);
167 deviceService.removeListener(bridgeListener);
168 deviceService.removeListener(ovsdbListener);
169 componentConfigService.unregisterProperties(getClass(), false);
170 leadershipService.withdraw(appId.name());
171 eventExecutor.shutdown();
172
173 log.info("Stopped");
174 }
175
176 @Modified
177 protected void modified(ComponentContext context) {
178 readComponentConfiguration(context);
179
180 log.info("Modified");
181 }
182
183 @Override
184 public void processInitState(K8sNode k8sNode) {
185 if (!isOvsdbConnected(k8sNode, ovsdbPortNum, ovsdbController, deviceService)) {
186 ovsdbController.connect(k8sNode.managementIp(), tpPort(ovsdbPortNum));
187 return;
188 }
189 if (!deviceService.isAvailable(k8sNode.intgBridge())) {
Jian Lie2a04ce2020-07-01 19:07:02 +0900190 createBridge(k8sNode, k8sNode.intgBridgeName(), k8sNode.intgBridge());
Jian Lif16e8852019-01-22 22:55:31 +0900191 }
Jian Libf562c22019-04-15 18:07:14 +0900192 if (!deviceService.isAvailable(k8sNode.extBridge())) {
Jian Lie2a04ce2020-07-01 19:07:02 +0900193 createBridge(k8sNode, k8sNode.extBridgeName(), k8sNode.extBridge());
Jian Libf562c22019-04-15 18:07:14 +0900194 }
Jian Li1a2eb5d2019-08-27 02:07:05 +0900195 if (!deviceService.isAvailable(k8sNode.localBridge())) {
Jian Lie2a04ce2020-07-01 19:07:02 +0900196 createBridge(k8sNode, k8sNode.localBridgeName(), k8sNode.localBridge());
197 }
198
199 if (k8sNode.mode() == NORMAL) {
200 if (!deviceService.isAvailable(k8sNode.tunBridge())) {
201 createBridge(k8sNode, k8sNode.tunBridgeName(), k8sNode.tunBridge());
202 }
Jian Li1a2eb5d2019-08-27 02:07:05 +0900203 }
Jian Lif16e8852019-01-22 22:55:31 +0900204 }
205
206 @Override
207 public void processDeviceCreatedState(K8sNode k8sNode) {
208 try {
209 if (!isOvsdbConnected(k8sNode, ovsdbPortNum, ovsdbController, deviceService)) {
210 ovsdbController.connect(k8sNode.managementIp(), tpPort(ovsdbPortNum));
211 return;
212 }
213
Jian Libf562c22019-04-15 18:07:14 +0900214 // create patch ports between integration and external bridges
215 createPatchInterfaces(k8sNode);
216
Jian Lie2a04ce2020-07-01 19:07:02 +0900217 if (k8sNode.mode() == NORMAL) {
218 if (k8sNode.dataIp() != null &&
219 !isIntfEnabled(k8sNode, k8sNode.vxlanPortName())) {
220 createVxlanTunnelInterface(k8sNode);
221 }
Jian Lif16e8852019-01-22 22:55:31 +0900222
Jian Lie2a04ce2020-07-01 19:07:02 +0900223 if (k8sNode.dataIp() != null &&
224 !isIntfEnabled(k8sNode, k8sNode.grePortName())) {
225 createGreTunnelInterface(k8sNode);
226 }
Jian Lif16e8852019-01-22 22:55:31 +0900227
Jian Lie2a04ce2020-07-01 19:07:02 +0900228 if (k8sNode.dataIp() != null &&
229 !isIntfEnabled(k8sNode, k8sNode.genevePortName())) {
230 createGeneveTunnelInterface(k8sNode);
231 }
Jian Lif16e8852019-01-22 22:55:31 +0900232 }
233 } catch (Exception e) {
234 log.error("Exception occurred because of {}", e);
235 }
236 }
237
238 @Override
239 public void processCompleteState(K8sNode k8sNode) {
240 // do something if needed
241 }
242
243 @Override
244 public void processIncompleteState(K8sNode k8sNode) {
245 // do something if needed
246 }
247
Jian Li0ee8d0e2019-12-18 11:35:05 +0900248 @Override
249 public void processPreOnBoardState(K8sNode k8sNode) {
250 processInitState(k8sNode);
251 processDeviceCreatedState(k8sNode);
252 }
253
254 @Override
255 public void processOnBoardedState(K8sNode k8sNode) {
256 // do something if needed
257 }
258
Jian Li3b640af2020-01-02 23:57:13 +0900259 @Override
260 public void processPostOnBoardState(K8sNode k8sNode) {
261 // do something if needed
262 }
263
Jian Lif16e8852019-01-22 22:55:31 +0900264 /**
265 * Extracts properties from the component configuration context.
266 *
267 * @param context the component context
268 */
269 private void readComponentConfiguration(ComponentContext context) {
270 Dictionary<?, ?> properties = context.getProperties();
271
272 Integer ovsdbPortConfigured = Tools.getIntegerProperty(properties, OVSDB_PORT);
273 if (ovsdbPortConfigured == null) {
274 ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
275 log.info("OVSDB port is NOT configured, default value is {}", ovsdbPortNum);
276 } else {
277 ovsdbPortNum = ovsdbPortConfigured;
278 log.info("Configured. OVSDB port is {}", ovsdbPortNum);
279 }
280
281 Boolean autoRecoveryConfigured =
282 getBooleanProperty(properties, AUTO_RECOVERY);
283 if (autoRecoveryConfigured == null) {
284 autoRecovery = AUTO_RECOVERY_DEFAULT;
285 log.info("Auto recovery flag is NOT " +
286 "configured, default value is {}", autoRecovery);
287 } else {
288 autoRecovery = autoRecoveryConfigured;
289 log.info("Configured. Auto recovery flag is {}", autoRecovery);
290 }
291 }
292
293 /**
294 * Creates a bridge with a given name on a given kubernetes node.
295 *
296 * @param k8sNode kubernetes node
297 * @param bridgeName bridge name
298 * @param devId device identifier
299 */
300 private void createBridge(K8sNode k8sNode, String bridgeName, DeviceId devId) {
301 Device device = deviceService.getDevice(k8sNode.ovsdb());
302
303 List<ControllerInfo> controllers = clusterService.getNodes().stream()
304 .map(n -> new ControllerInfo(n.ip(), DEFAULT_OFPORT, DEFAULT_OF_PROTO))
305 .collect(Collectors.toList());
306
307 String dpid = devId.toString().substring(DPID_BEGIN);
308
309 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
310 .name(bridgeName)
311 .failMode(BridgeDescription.FailMode.SECURE)
312 .datapathId(dpid)
313 .disableInBand()
314 .controllers(controllers);
315
316 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
317 bridgeConfig.addBridge(builder.build());
318 }
319
320 /**
321 * Creates a VXLAN tunnel interface in a given kubernetes node.
322 *
323 * @param k8sNode kubernetes node
324 */
325 private void createVxlanTunnelInterface(K8sNode k8sNode) {
Jian Lie2a04ce2020-07-01 19:07:02 +0900326 createTunnelInterface(k8sNode, VXLAN, k8sNode.vxlanPortName());
Jian Lif16e8852019-01-22 22:55:31 +0900327 }
328
329 /**
330 * Creates a GRE tunnel interface in a given kubernetes node.
331 *
332 * @param k8sNode kubernetes node
333 */
334 private void createGreTunnelInterface(K8sNode k8sNode) {
Jian Lie2a04ce2020-07-01 19:07:02 +0900335 createTunnelInterface(k8sNode, GRE, k8sNode.grePortName());
Jian Lif16e8852019-01-22 22:55:31 +0900336 }
337
338 /**
339 * Creates a GENEVE tunnel interface in a given kubernetes node.
340 *
341 * @param k8sNode kubernetes node
342 */
343 private void createGeneveTunnelInterface(K8sNode k8sNode) {
Jian Lie2a04ce2020-07-01 19:07:02 +0900344 createTunnelInterface(k8sNode, GENEVE, k8sNode.genevePortName());
Jian Lif16e8852019-01-22 22:55:31 +0900345 }
346
Jian Libf562c22019-04-15 18:07:14 +0900347 private void createPatchInterfaces(K8sNode k8sNode) {
348 Device device = deviceService.getDevice(k8sNode.ovsdb());
349 if (device == null || !device.is(InterfaceConfig.class)) {
350 log.error("Failed to create patch interface on {}", k8sNode.ovsdb());
351 return;
352 }
353
Jian Li1a2eb5d2019-08-27 02:07:05 +0900354 // integration bridge -> external bridge
355 PatchDescription brIntExtPatchDesc =
Jian Libf562c22019-04-15 18:07:14 +0900356 DefaultPatchDescription.builder()
Jian Li732c3422020-09-07 17:01:11 +0900357 .deviceId(k8sNode.intgBridgeName())
358 .ifaceName(k8sNode.intgToExtPatchPortName())
359 .peer(k8sNode.extToIntgPatchPortName())
360 .build();
Jian Lie2a04ce2020-07-01 19:07:02 +0900361
362 // integration bridge -> tunnel bridge
363 PatchDescription brIntTunPatchDesc =
364 DefaultPatchDescription.builder()
Jian Li732c3422020-09-07 17:01:11 +0900365 .deviceId(k8sNode.intgBridgeName())
366 .ifaceName(k8sNode.intgToTunPatchPortName())
367 .peer(k8sNode.tunToIntgPatchPortName())
368 .build();
Jian Libf562c22019-04-15 18:07:14 +0900369
Jian Li1a2eb5d2019-08-27 02:07:05 +0900370 // external bridge -> integration bridge
371 PatchDescription brExtIntPatchDesc =
Jian Libf562c22019-04-15 18:07:14 +0900372 DefaultPatchDescription.builder()
Jian Li732c3422020-09-07 17:01:11 +0900373 .deviceId(k8sNode.extBridgeName())
374 .ifaceName(k8sNode.extToIntgPatchPortName())
375 .peer(k8sNode.intgToExtPatchPortName())
376 .build();
Jian Libf562c22019-04-15 18:07:14 +0900377
Jian Li1a2eb5d2019-08-27 02:07:05 +0900378 // integration bridge -> local bridge
379 PatchDescription brIntLocalPatchDesc =
380 DefaultPatchDescription.builder()
Jian Li732c3422020-09-07 17:01:11 +0900381 .deviceId(k8sNode.intgBridgeName())
382 .ifaceName(k8sNode.intgToLocalPatchPortName())
383 .peer(k8sNode.localToIntgPatchPortName())
384 .build();
Jian Li1a2eb5d2019-08-27 02:07:05 +0900385
386 // local bridge -> integration bridge
387 PatchDescription brLocalIntPatchDesc =
388 DefaultPatchDescription.builder()
Jian Li732c3422020-09-07 17:01:11 +0900389 .deviceId(k8sNode.localBridgeName())
390 .ifaceName(k8sNode.localToIntgPatchPortName())
391 .peer(k8sNode.intgToLocalPatchPortName())
392 .build();
Jian Li1a2eb5d2019-08-27 02:07:05 +0900393
Jian Libf562c22019-04-15 18:07:14 +0900394 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
Jian Lie2a04ce2020-07-01 19:07:02 +0900395 ifaceConfig.addPatchMode(k8sNode.intgToExtPatchPortName(), brIntExtPatchDesc);
396 ifaceConfig.addPatchMode(k8sNode.extToIntgPatchPortName(), brExtIntPatchDesc);
397 ifaceConfig.addPatchMode(k8sNode.intgToLocalPatchPortName(), brIntLocalPatchDesc);
398 ifaceConfig.addPatchMode(k8sNode.localToIntgPatchPortName(), brLocalIntPatchDesc);
399 ifaceConfig.addPatchMode(k8sNode.intgToTunPatchPortName(), brIntTunPatchDesc);
400
401 if (k8sNode.mode() == NORMAL) {
402 // tunnel bridge -> integration bridge
403 PatchDescription brTunIntPatchDesc =
404 DefaultPatchDescription.builder()
Jian Li732c3422020-09-07 17:01:11 +0900405 .deviceId(k8sNode.tunBridgeName())
406 .ifaceName(k8sNode.tunToIntgPatchPortName())
407 .peer(k8sNode.intgToTunPatchPortName())
408 .build();
Jian Lie2a04ce2020-07-01 19:07:02 +0900409
410 ifaceConfig.addPatchMode(k8sNode.tunToIntgPatchPortName(), brTunIntPatchDesc);
Jian Li019ce6a2020-09-09 10:23:21 +0900411 } else {
412 // k8s integration bridge -> openstack integration bridge
413 PatchDescription k8sIntOsIntPatchDesc =
414 DefaultPatchDescription.builder()
415 .deviceId(k8sNode.intgBridgeName())
416 .ifaceName(k8sNode.k8sToOsIntgPatchPortName())
417 .peer(k8sNode.osToK8sIntgPatchPortName())
418 .build();
419 ifaceConfig.addPatchMode(k8sNode.k8sToOsIntgPatchPortName(), k8sIntOsIntPatchDesc);
420
421 // external bridge -> router bridge
422 PatchDescription extRouterPatchDesc =
423 DefaultPatchDescription.builder()
424 .deviceId(k8sNode.extBridgeName())
425 .ifaceName(k8sNode.extToRouterPatchPortName())
426 .peer(k8sNode.routerToExtPatchPortName())
427 .build();
428 ifaceConfig.addPatchMode(k8sNode.extToRouterPatchPortName(), extRouterPatchDesc);
Jian Lie2a04ce2020-07-01 19:07:02 +0900429 }
Jian Libf562c22019-04-15 18:07:14 +0900430 }
431
Jian Lif16e8852019-01-22 22:55:31 +0900432 /**
433 * Creates a tunnel interface in a given kubernetes node.
434 *
435 * @param k8sNode kubernetes node
436 */
437 private void createTunnelInterface(K8sNode k8sNode,
438 String type, String intfName) {
439 if (isIntfEnabled(k8sNode, intfName)) {
440 return;
441 }
442
443 Device device = deviceService.getDevice(k8sNode.ovsdb());
444 if (device == null || !device.is(InterfaceConfig.class)) {
445 log.error("Failed to create tunnel interface on {}", k8sNode.ovsdb());
446 return;
447 }
448
Jian Lie2a04ce2020-07-01 19:07:02 +0900449 TunnelDescription tunnelDesc = buildTunnelDesc(k8sNode, type, intfName);
Jian Lif16e8852019-01-22 22:55:31 +0900450
451 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
452 ifaceConfig.addTunnelMode(intfName, tunnelDesc);
453 }
454
455 /**
456 * Builds tunnel description according to the network type.
457 *
458 * @param type network type
459 * @return tunnel description
460 */
Jian Lie2a04ce2020-07-01 19:07:02 +0900461 private TunnelDescription buildTunnelDesc(K8sNode k8sNode,
462 String type, String intfName) {
463 TunnelKey<String> key = new TunnelKey<>(k8sNode.tunnelKey());
Jian Lif16e8852019-01-22 22:55:31 +0900464 if (VXLAN.equals(type) || GRE.equals(type) || GENEVE.equals(type)) {
465 TunnelDescription.Builder tdBuilder =
466 DefaultTunnelDescription.builder()
Jian Lie2a04ce2020-07-01 19:07:02 +0900467 .deviceId(k8sNode.tunBridgeName())
Jian Lif16e8852019-01-22 22:55:31 +0900468 .ifaceName(intfName)
469 .remote(TunnelEndPoints.flowTunnelEndpoint())
Jian Lie2a04ce2020-07-01 19:07:02 +0900470 .key(key);
Jian Lif16e8852019-01-22 22:55:31 +0900471
472 switch (type) {
473 case VXLAN:
474 tdBuilder.type(TunnelDescription.Type.VXLAN);
475 break;
476 case GRE:
477 tdBuilder.type(TunnelDescription.Type.GRE);
478 break;
479 case GENEVE:
480 tdBuilder.type(TunnelDescription.Type.GENEVE);
481 break;
482 default:
483 return null;
484 }
485
486 return tdBuilder.build();
487 }
488 return null;
489 }
490
491 /**
492 * Checks whether a given network interface in a given kubernetes node
493 * is enabled or not.
494 *
495 * @param k8sNode kubernetes node
496 * @param intf network interface name
497 * @return true if the given interface is enabled, false otherwise
498 */
499 private boolean isIntfEnabled(K8sNode k8sNode, String intf) {
Jian Lie2a04ce2020-07-01 19:07:02 +0900500 return deviceService.isAvailable(k8sNode.tunBridge()) &&
501 deviceService.getPorts(k8sNode.tunBridge()).stream()
Jian Lif16e8852019-01-22 22:55:31 +0900502 .anyMatch(port -> Objects.equals(
503 port.annotations().value(PORT_NAME), intf) &&
504 port.isEnabled());
505 }
506
507 /**
508 * Checks whether all requirements for this state are fulfilled or not.
509 *
510 * @param k8sNode kubernetes node
511 * @return true if all requirements are fulfilled, false otherwise
512 */
513 private boolean isCurrentStateDone(K8sNode k8sNode) {
514 switch (k8sNode.state()) {
515 case INIT:
Jian Li0ee8d0e2019-12-18 11:35:05 +0900516 return isInitStateDone(k8sNode);
Jian Lif16e8852019-01-22 22:55:31 +0900517 case DEVICE_CREATED:
Jian Li0ee8d0e2019-12-18 11:35:05 +0900518 return isDeviceCreatedStateDone(k8sNode);
519 case PRE_ON_BOARD:
520 return isInitStateDone(k8sNode) && isDeviceCreatedStateDone(k8sNode);
Jian Lif16e8852019-01-22 22:55:31 +0900521 case COMPLETE:
522 case INCOMPLETE:
Jian Li0ee8d0e2019-12-18 11:35:05 +0900523 case ON_BOARDED:
Jian Li4604b7f2020-01-03 18:42:30 +0900524 case POST_ON_BOARD:
Jian Lif16e8852019-01-22 22:55:31 +0900525 // always return false
526 // run init CLI to re-trigger node bootstrap
527 return false;
528 default:
529 return true;
530 }
531 }
532
Jian Li0ee8d0e2019-12-18 11:35:05 +0900533 private boolean isInitStateDone(K8sNode k8sNode) {
534 if (!isOvsdbConnected(k8sNode, ovsdbPortNum,
535 ovsdbController, deviceService)) {
536 return false;
537 }
538
Jian Li4604b7f2020-01-03 18:42:30 +0900539 try {
540 // we need to wait a while, in case interface and bridge
541 // creation requires some time
542 sleep(SLEEP_MS);
543 } catch (InterruptedException e) {
544 log.error("Exception caused during init state checking...");
545 }
546
Jian Lie2a04ce2020-07-01 19:07:02 +0900547 boolean result = k8sNode.intgBridge() != null && k8sNode.extBridge() != null &&
Jian Li0ee8d0e2019-12-18 11:35:05 +0900548 deviceService.isAvailable(k8sNode.intgBridge()) &&
549 deviceService.isAvailable(k8sNode.extBridge()) &&
550 deviceService.isAvailable(k8sNode.localBridge());
Jian Lie2a04ce2020-07-01 19:07:02 +0900551
552 if (k8sNode.mode() == NORMAL) {
553 return result && deviceService.isAvailable(k8sNode.tunBridge());
554 } else {
555 return result;
556 }
Jian Li0ee8d0e2019-12-18 11:35:05 +0900557 }
558
559 private boolean isDeviceCreatedStateDone(K8sNode k8sNode) {
Jian Li4604b7f2020-01-03 18:42:30 +0900560
561 try {
562 // we need to wait a while, in case interface and bridge
563 // creation requires some time
564 sleep(SLEEP_MS);
565 } catch (InterruptedException e) {
566 log.error("Exception caused during init state checking...");
567 }
568
Jian Lie2a04ce2020-07-01 19:07:02 +0900569 if (k8sNode.mode() == NORMAL) {
570 if (k8sNode.dataIp() != null &&
571 !isIntfEnabled(k8sNode, k8sNode.vxlanPortName())) {
572 return false;
573 }
574 if (k8sNode.dataIp() != null &&
575 !isIntfEnabled(k8sNode, k8sNode.grePortName())) {
576 return false;
577 }
578 if (k8sNode.dataIp() != null &&
579 !isIntfEnabled(k8sNode, k8sNode.genevePortName())) {
580 return false;
581 }
Jian Li0ee8d0e2019-12-18 11:35:05 +0900582 }
583
584 return true;
585 }
586
Jian Lif16e8852019-01-22 22:55:31 +0900587 /**
588 * Configures the kubernetes node with new state.
589 *
590 * @param k8sNode kubernetes node
591 * @param newState a new state
592 */
593 private void setState(K8sNode k8sNode, K8sNodeState newState) {
594 if (k8sNode.state() == newState) {
595 return;
596 }
597 K8sNode updated = k8sNode.updateState(newState);
598 k8sNodeAdminService.updateNode(updated);
599 log.info("Changed {} state: {}", k8sNode.hostname(), newState);
600 }
601
602 /**
603 * Bootstraps a new kubernetes node.
604 *
605 * @param k8sNode kubernetes node
606 */
607 private void bootstrapNode(K8sNode k8sNode) {
608 if (isCurrentStateDone(k8sNode)) {
609 setState(k8sNode, k8sNode.state().nextState());
610 } else {
611 log.trace("Processing {} state for {}", k8sNode.state(),
612 k8sNode.hostname());
613 k8sNode.state().process(this, k8sNode);
614 }
615 }
616
617 private void processK8sNodeRemoved(K8sNode k8sNode) {
618 OvsdbClientService client = getOvsdbClient(k8sNode, ovsdbPortNum, ovsdbController);
619 if (client == null) {
620 log.info("Failed to get ovsdb client");
621 return;
622 }
623
624 // delete integration bridge from the node
Jian Lie2a04ce2020-07-01 19:07:02 +0900625 client.dropBridge(k8sNode.intgBridgeName());
Jian Lif16e8852019-01-22 22:55:31 +0900626
Jian Libf562c22019-04-15 18:07:14 +0900627 // delete external bridge from the node
Jian Lie2a04ce2020-07-01 19:07:02 +0900628 client.dropBridge(k8sNode.extBridgeName());
Jian Libf562c22019-04-15 18:07:14 +0900629
Jian Li1a2eb5d2019-08-27 02:07:05 +0900630 // delete local bridge from the node
Jian Lie2a04ce2020-07-01 19:07:02 +0900631 client.dropBridge(k8sNode.localBridgeName());
632
633 if (k8sNode.mode() == NORMAL) {
634 // delete tunnel bridge from the node
635 client.dropBridge(k8sNode.tunBridgeName());
636 }
Jian Li1a2eb5d2019-08-27 02:07:05 +0900637
Jian Lif16e8852019-01-22 22:55:31 +0900638 // disconnect ovsdb
639 client.disconnect();
640 }
641
642 /**
643 * An internal OVSDB listener. This listener is used for listening the
644 * network facing events from OVSDB device. If a new OVSDB device is detected,
645 * ONOS tries to bootstrap the kubernetes node.
646 */
647 private class InternalOvsdbListener implements DeviceListener {
648
649 @Override
650 public boolean isRelevant(DeviceEvent event) {
651 return event.subject().type() == Device.Type.CONTROLLER;
652 }
653
654 private boolean isRelevantHelper() {
655 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
656 }
657
658 @Override
659 public void event(DeviceEvent event) {
660 Device device = event.subject();
661
662 switch (event.type()) {
663 case DEVICE_AVAILABILITY_CHANGED:
664 case DEVICE_ADDED:
665 eventExecutor.execute(() -> {
666
667 if (!isRelevantHelper()) {
668 return;
669 }
670
671 K8sNode k8sNode = k8sNodeService.node(device.id());
672
673 if (k8sNode == null) {
674 return;
675 }
676
677 if (deviceService.isAvailable(device.id())) {
678 log.debug("OVSDB {} detected", device.id());
679 bootstrapNode(k8sNode);
680 }
681 });
682 break;
683 case PORT_ADDED:
684 case PORT_REMOVED:
685 case DEVICE_REMOVED:
686 default:
687 // do nothing
688 break;
689 }
690 }
691 }
692
693 /**
694 * An internal integration bridge listener. This listener is used for
695 * listening the events from integration bridge. To listen the events from
696 * other types of bridge such as provider bridge or tunnel bridge, we need
697 * to augment K8sNodeService.node() method.
698 */
699 private class InternalBridgeListener implements DeviceListener {
700
701 @Override
702 public boolean isRelevant(DeviceEvent event) {
703 return event.subject().type() == Device.Type.SWITCH;
704 }
705
706 private boolean isRelevantHelper() {
707 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
708 }
709
710 @Override
711 public void event(DeviceEvent event) {
712 Device device = event.subject();
713
714 switch (event.type()) {
715 case DEVICE_AVAILABILITY_CHANGED:
716 case DEVICE_ADDED:
717 eventExecutor.execute(() -> {
718
719 if (!isRelevantHelper()) {
720 return;
721 }
722
723 K8sNode k8sNode = k8sNodeService.node(device.id());
724
725 if (k8sNode == null) {
726 return;
727 }
728
Jian Libf562c22019-04-15 18:07:14 +0900729 // TODO: also need to check the external bridge's availability
Jian Li1a2eb5d2019-08-27 02:07:05 +0900730 // TODO: also need to check the local bridge's availability
Jian Lif16e8852019-01-22 22:55:31 +0900731 if (deviceService.isAvailable(device.id())) {
Jian Libf562c22019-04-15 18:07:14 +0900732 log.debug("Integration bridge created on {}",
733 k8sNode.hostname());
Jian Lif16e8852019-01-22 22:55:31 +0900734 bootstrapNode(k8sNode);
735 } else if (k8sNode.state() == COMPLETE) {
736 log.info("Device {} disconnected", device.id());
737 setState(k8sNode, INCOMPLETE);
738 }
739
740 if (autoRecovery) {
741 if (k8sNode.state() == INCOMPLETE ||
742 k8sNode.state() == DEVICE_CREATED) {
743 log.info("Device {} is reconnected", device.id());
744 k8sNodeAdminService.updateNode(
745 k8sNode.updateState(K8sNodeState.INIT));
746 }
747 }
748 });
749 break;
750 case PORT_UPDATED:
751 case PORT_ADDED:
752 eventExecutor.execute(() -> {
753
754 if (!isRelevantHelper()) {
755 return;
756 }
757
758 K8sNode k8sNode = k8sNodeService.node(device.id());
759
760 if (k8sNode == null) {
761 return;
762 }
763
764 Port port = event.port();
765 String portName = port.annotations().value(PORT_NAME);
766 if (k8sNode.state() == DEVICE_CREATED && (
Jian Lie2a04ce2020-07-01 19:07:02 +0900767 Objects.equals(portName, k8sNode.vxlanPortName()) ||
768 Objects.equals(portName, k8sNode.grePortName()) ||
769 Objects.equals(portName, k8sNode.genevePortName()))) {
Jian Lif16e8852019-01-22 22:55:31 +0900770 log.info("Interface {} added or updated to {}",
771 portName, device.id());
772 bootstrapNode(k8sNode);
773 }
774 });
775 break;
776 case PORT_REMOVED:
777 eventExecutor.execute(() -> {
778
779 if (!isRelevantHelper()) {
780 return;
781 }
782
783 K8sNode k8sNode = k8sNodeService.node(device.id());
784
785 if (k8sNode == null) {
786 return;
787 }
788
789 Port port = event.port();
790 String portName = port.annotations().value(PORT_NAME);
791 if (k8sNode.state() == COMPLETE && (
Jian Lie2a04ce2020-07-01 19:07:02 +0900792 Objects.equals(portName, k8sNode.vxlanPortName()) ||
793 Objects.equals(portName, k8sNode.grePortName()) ||
794 Objects.equals(portName, k8sNode.genevePortName()))) {
Jian Lif16e8852019-01-22 22:55:31 +0900795 log.warn("Interface {} removed from {}",
796 portName, event.subject().id());
797 setState(k8sNode, INCOMPLETE);
798 }
799 });
800 break;
801 case DEVICE_REMOVED:
802 default:
803 // do nothing
804 break;
805 }
806 }
807 }
808
809 /**
810 * An internal kubernetes node listener.
811 * The notification is triggered by KubernetesNodeStore.
812 */
813 private class InternalK8sNodeListener implements K8sNodeListener {
814
815 private boolean isRelevantHelper() {
816 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
817 }
818
819 @Override
820 public void event(K8sNodeEvent event) {
821 switch (event.type()) {
822 case K8S_NODE_CREATED:
823 case K8S_NODE_UPDATED:
824 eventExecutor.execute(() -> {
825
826 if (!isRelevantHelper()) {
827 return;
828 }
829
830 bootstrapNode(event.subject());
831 });
832 break;
833 case K8S_NODE_REMOVED:
834 eventExecutor.execute(() -> {
835
836 if (!isRelevantHelper()) {
837 return;
838 }
839
840 processK8sNodeRemoved(event.subject());
841 });
842 break;
843 case K8S_NODE_INCOMPLETE:
844 default:
845 break;
846 }
847 }
848 }
849}