blob: 01a5043cf15eb64213163713987238c0df328246 [file] [log] [blame]
Jian Li1cee9882019-02-13 11:25:25 +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 io.fabric8.kubernetes.api.model.Node;
19import io.fabric8.kubernetes.api.model.NodeAddress;
20import io.fabric8.kubernetes.client.KubernetesClient;
21import org.onlab.packet.IpAddress;
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;
27import org.onosproject.k8snode.api.DefaultK8sNode;
28import org.onosproject.k8snode.api.K8sApiConfig;
29import org.onosproject.k8snode.api.K8sApiConfigAdminService;
30import org.onosproject.k8snode.api.K8sApiConfigEvent;
31import org.onosproject.k8snode.api.K8sApiConfigListener;
32import org.onosproject.k8snode.api.K8sNode;
33import org.onosproject.k8snode.api.K8sNodeAdminService;
34import org.osgi.service.component.annotations.Activate;
35import org.osgi.service.component.annotations.Component;
36import org.osgi.service.component.annotations.Deactivate;
37import org.osgi.service.component.annotations.Reference;
38import org.osgi.service.component.annotations.ReferenceCardinality;
39import org.slf4j.Logger;
40
Jian Li7709eb42019-05-08 15:58:04 +090041import java.util.Map;
Jian Li1cee9882019-02-13 11:25:25 +090042import java.util.Objects;
43import java.util.concurrent.ExecutorService;
44
45import static java.util.concurrent.Executors.newSingleThreadExecutor;
46import static org.onlab.util.Tools.groupedThreads;
47import static org.onosproject.k8snode.api.K8sNode.Type.MASTER;
48import static org.onosproject.k8snode.api.K8sNode.Type.MINION;
49import static org.onosproject.k8snode.api.K8sNodeService.APP_ID;
Jian Li77af8f32019-12-18 11:35:05 +090050import static org.onosproject.k8snode.api.K8sNodeState.PRE_ON_BOARD;
Jian Li1cee9882019-02-13 11:25:25 +090051import static org.onosproject.k8snode.util.K8sNodeUtil.k8sClient;
52import static org.slf4j.LoggerFactory.getLogger;
53
54/**
55 * Handles the state of kubernetes API server configuration.
56 */
57@Component(immediate = true)
58public class DefaultK8sApiConfigHandler {
59
60 private final Logger log = getLogger(getClass());
61
62 private static final String INTERNAL_IP = "InternalIP";
63 private static final String K8S_ROLE = "node-role.kubernetes.io";
Jian Li7709eb42019-05-08 15:58:04 +090064 private static final String EXT_BRIDGE_IP = "external.bridge.ip";
65 private static final String EXT_GATEWAY_IP = "external.gateway.ip";
66 private static final String EXT_INTF_NAME = "external.interface.name";
Jian Li1cee9882019-02-13 11:25:25 +090067
68 @Reference(cardinality = ReferenceCardinality.MANDATORY)
69 protected CoreService coreService;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY)
72 protected LeadershipService leadershipService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY)
75 protected ClusterService clusterService;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY)
78 protected K8sApiConfigAdminService k8sApiConfigAdminService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY)
81 protected K8sNodeAdminService k8sNodeAdminService;
82
83 private final ExecutorService eventExecutor = newSingleThreadExecutor(
84 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
85
86 private final K8sApiConfigListener k8sApiConfigListener = new InternalK8sApiConfigListener();
87
88 private ApplicationId appId;
89 private NodeId localNode;
90
91 @Activate
92 protected void activate() {
93 appId = coreService.getAppId(APP_ID);
94 localNode = clusterService.getLocalNode().id();
95 leadershipService.runForLeadership(appId.name());
96 k8sApiConfigAdminService.addListener(k8sApiConfigListener);
97
98 log.info("Started");
99 }
100
101 @Deactivate
102 protected void deactivate() {
103 k8sApiConfigAdminService.removeListener(k8sApiConfigListener);
104 leadershipService.withdraw(appId.name());
105 eventExecutor.shutdown();
106
107 log.info("Stopped");
108 }
109
110 /**
111 * Checks the validity of the given kubernetes API server configuration.
112 *
113 * @param config kubernetes API server configuration
114 * @return validity result
115 */
116 private boolean checkApiServerConfig(K8sApiConfig config) {
117 KubernetesClient k8sClient = k8sClient(config);
118 return k8sClient != null && k8sClient.getApiVersion() != null;
119 }
120
121 private void bootstrapK8sNodes(K8sApiConfig config) {
122 KubernetesClient k8sClient = k8sClient(config);
123
124 if (k8sClient == null) {
125 log.warn("Failed to connect to kubernetes API server");
126 return;
127 }
128
129 k8sClient.nodes().list().getItems().forEach(n ->
130 k8sNodeAdminService.createNode(buildK8sNode(n))
131 );
132 }
133
134 private K8sNode buildK8sNode(Node node) {
135 String hostname = node.getMetadata().getName();
136 IpAddress managementIp = null;
137 IpAddress dataIp = null;
138
139 for (NodeAddress nodeAddress:node.getStatus().getAddresses()) {
140 // we need to consider assigning managementIp and dataIp differently
141 // FIXME: ExternalIp is not considered currently
142 if (nodeAddress.getType().equals(INTERNAL_IP)) {
143 managementIp = IpAddress.valueOf(nodeAddress.getAddress());
144 dataIp = IpAddress.valueOf(nodeAddress.getAddress());
145 }
146 }
147
148 String roleStr = node.getMetadata().getLabels().keySet().stream()
149 .filter(l -> l.contains(K8S_ROLE))
150 .findFirst().orElse(null);
151
Jian Li8a988042019-05-03 23:41:19 +0900152 K8sNode.Type nodeType = MINION;
Jian Li1cee9882019-02-13 11:25:25 +0900153
154 if (roleStr != null) {
155 String role = roleStr.split("/")[1];
156 if (MASTER.name().equalsIgnoreCase(role)) {
157 nodeType = MASTER;
158 } else {
159 nodeType = MINION;
160 }
161 }
162
Jian Li7709eb42019-05-08 15:58:04 +0900163 Map<String, String> annots = node.getMetadata().getAnnotations();
164
165 String extIntf = annots.get(EXT_INTF_NAME);
166 String extGatewayIpStr = annots.get(EXT_GATEWAY_IP);
167 String extBridgeIpStr = annots.get(EXT_BRIDGE_IP);
168
Jian Li1cee9882019-02-13 11:25:25 +0900169 return DefaultK8sNode.builder()
170 .hostname(hostname)
171 .managementIp(managementIp)
172 .dataIp(dataIp)
Jian Li7709eb42019-05-08 15:58:04 +0900173 .extIntf(extIntf)
174 .type(nodeType)
Jian Li77af8f32019-12-18 11:35:05 +0900175 .state(PRE_ON_BOARD)
Jian Li7709eb42019-05-08 15:58:04 +0900176 .extBridgeIp(IpAddress.valueOf(extBridgeIpStr))
177 .extGatewayIp(IpAddress.valueOf(extGatewayIpStr))
178 .podCidr(node.getSpec().getPodCIDR())
Jian Li1cee9882019-02-13 11:25:25 +0900179 .build();
180 }
181
182 /**
183 * An internal kubernetes API server config listener.
184 * The notification is triggered by K8sApiConfigStore.
185 */
186 private class InternalK8sApiConfigListener implements K8sApiConfigListener {
187
188 private boolean isRelevantHelper() {
189 return Objects.equals(localNode, leadershipService.getLeader(appId.name()));
190 }
191
192 @Override
193 public void event(K8sApiConfigEvent event) {
194
195 switch (event.type()) {
196 case K8S_API_CONFIG_CREATED:
197 eventExecutor.execute(() -> processConfigCreation(event.subject()));
198 break;
199 default:
200 break;
201 }
202 }
203
204 private void processConfigCreation(K8sApiConfig config) {
205 if (!isRelevantHelper()) {
206 return;
207 }
208
209 if (checkApiServerConfig(config)) {
210 K8sApiConfig newConfig = config.updateState(K8sApiConfig.State.CONNECTED);
211 k8sApiConfigAdminService.updateApiConfig(newConfig);
212 bootstrapK8sNodes(config);
213 }
214 }
215 }
216}