blob: bde645412a9677baf1d46d329750f6d14323ee6a [file] [log] [blame]
Jian Li034820d2021-01-15 16:58:48 +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.kubevirtnetworking.impl;
17
18import io.fabric8.kubernetes.client.KubernetesClient;
19import io.fabric8.kubernetes.client.Watcher;
20import io.fabric8.kubernetes.client.WatcherException;
21import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
Jian Li4c35a262021-02-01 20:36:45 +090022import org.apache.commons.lang.StringUtils;
23import org.json.JSONArray;
Jian Li034820d2021-01-15 16:58:48 +090024import org.json.JSONException;
25import org.json.JSONObject;
26import org.onlab.packet.IpAddress;
Jian Li4c35a262021-02-01 20:36:45 +090027import org.onlab.packet.IpPrefix;
Jian Li034820d2021-01-15 16:58:48 +090028import org.onosproject.cluster.ClusterService;
29import org.onosproject.cluster.LeadershipService;
30import org.onosproject.cluster.NodeId;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.kubevirtnetworking.api.DefaultKubevirtNetwork;
Jian Li4c35a262021-02-01 20:36:45 +090034import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
Jian Li034820d2021-01-15 16:58:48 +090035import org.onosproject.kubevirtnetworking.api.KubevirtIpPool;
36import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
37import org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type;
38import org.onosproject.kubevirtnetworking.api.KubevirtNetworkAdminService;
39import org.onosproject.kubevirtnode.api.KubevirtApiConfigEvent;
40import org.onosproject.kubevirtnode.api.KubevirtApiConfigListener;
41import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
42import org.onosproject.mastership.MastershipService;
43import org.osgi.service.component.annotations.Activate;
44import org.osgi.service.component.annotations.Component;
45import org.osgi.service.component.annotations.Deactivate;
46import org.osgi.service.component.annotations.Reference;
47import org.osgi.service.component.annotations.ReferenceCardinality;
48import org.slf4j.Logger;
49
50import java.io.IOException;
Jian Li4c35a262021-02-01 20:36:45 +090051import java.util.HashSet;
Jian Li340819a2021-11-05 17:22:50 +090052import java.util.Locale;
Jian Li034820d2021-01-15 16:58:48 +090053import java.util.Objects;
Jian Li4c35a262021-02-01 20:36:45 +090054import java.util.Set;
Jian Li034820d2021-01-15 16:58:48 +090055import java.util.concurrent.ExecutorService;
56
57import static java.util.concurrent.Executors.newSingleThreadExecutor;
58import static org.onlab.util.Tools.groupedThreads;
59import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
60import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.FLAT;
61import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.k8sClient;
Jian Li810f58c2021-02-27 01:10:50 +090062import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.parseResourceName;
Jian Li034820d2021-01-15 16:58:48 +090063import static org.slf4j.LoggerFactory.getLogger;
64
65/**
66 * Kubernetes network-attachment-definition watcher used for feeding kubevirt network information.
67 */
68@Component(immediate = true)
69public class NetworkAttachmentDefinitionWatcher {
70
71 private final Logger log = getLogger(getClass());
72
73 private static final String NETWORK_CONFIG = "network-config";
74 private static final String TYPE = "type";
75 private static final String MTU = "mtu";
76 private static final String SEGMENT_ID = "segmentId";
77 private static final String GATEWAY_IP = "gatewayIp";
Jian Lie2abe812021-08-12 18:03:30 +090078 private static final String DEFAULT_ROUTE = "defaultRoute";
Jian Li034820d2021-01-15 16:58:48 +090079 private static final String CIDR = "cidr";
80 private static final String HOST_ROUTES = "hostRoutes";
Jian Li4c35a262021-02-01 20:36:45 +090081 private static final String DESTINATION = "destination";
82 private static final String NEXTHOP = "nexthop";
Jian Li034820d2021-01-15 16:58:48 +090083 private static final String IP_POOL = "ipPool";
84 private static final String START = "start";
85 private static final String END = "end";
Jian Li4c35a262021-02-01 20:36:45 +090086 private static final String DNSES = "dnses";
Jian Li034820d2021-01-15 16:58:48 +090087
88 @Reference(cardinality = ReferenceCardinality.MANDATORY)
89 protected CoreService coreService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY)
92 protected MastershipService mastershipService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY)
95 protected ClusterService clusterService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
98 protected LeadershipService leadershipService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
101 protected KubevirtNetworkAdminService adminService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
104 protected KubevirtApiConfigService configService;
105
106 private final ExecutorService eventExecutor = newSingleThreadExecutor(
107 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
108
109 private final InternalNetworkAttachmentDefinitionWatcher
110 watcher = new InternalNetworkAttachmentDefinitionWatcher();
111 private final InternalKubevirtApiConfigListener
112 configListener = new InternalKubevirtApiConfigListener();
113
114 CustomResourceDefinitionContext nadCrdCxt = new CustomResourceDefinitionContext
115 .Builder()
116 .withGroup("k8s.cni.cncf.io")
117 .withScope("Namespaced")
118 .withVersion("v1")
119 .withPlural("network-attachment-definitions")
120 .build();
121
122 private ApplicationId appId;
123 private NodeId localNodeId;
124
125 @Activate
126 protected void activate() {
127 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
128 localNodeId = clusterService.getLocalNode().id();
129 leadershipService.runForLeadership(appId.name());
130 configService.addListener(configListener);
131
132 log.info("Started");
133 }
134
135 @Deactivate
136 protected void deactivate() {
137 configService.removeListener(configListener);
138 leadershipService.withdraw(appId.name());
139 eventExecutor.shutdown();
140
141 log.info("Stopped");
142 }
143
Jian Lid2ade0f2021-02-19 14:00:07 +0900144 private void instantiateWatcher() {
145 KubernetesClient client = k8sClient(configService);
146
147 if (client != null) {
148 try {
149 client.customResource(nadCrdCxt).watch(watcher);
150 } catch (IOException e) {
151 e.printStackTrace();
152 }
153 }
154 }
155
Jian Li034820d2021-01-15 16:58:48 +0900156 private class InternalKubevirtApiConfigListener implements KubevirtApiConfigListener {
157
158 private boolean isRelevantHelper() {
159 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
160 }
161
162 @Override
163 public void event(KubevirtApiConfigEvent event) {
164
165 switch (event.type()) {
166 case KUBEVIRT_API_CONFIG_UPDATED:
167 eventExecutor.execute(this::processConfigUpdate);
168 break;
169 case KUBEVIRT_API_CONFIG_CREATED:
170 case KUBEVIRT_API_CONFIG_REMOVED:
171 default:
172 // do nothing
173 break;
174 }
175 }
176
177 private void processConfigUpdate() {
178 if (!isRelevantHelper()) {
179 return;
180 }
181
Jian Lid2ade0f2021-02-19 14:00:07 +0900182 instantiateWatcher();
Jian Li034820d2021-01-15 16:58:48 +0900183 }
184 }
185
186 private class InternalNetworkAttachmentDefinitionWatcher
187 implements Watcher<String> {
188
189 @Override
190 public void eventReceived(Action action, String resource) {
191 switch (action) {
192 case ADDED:
193 eventExecutor.execute(() -> processAddition(resource));
194 break;
195 case MODIFIED:
196 eventExecutor.execute(() -> processModification(resource));
197 break;
198 case DELETED:
199 eventExecutor.execute(() -> processDeletion(resource));
200 break;
201 case ERROR:
202 log.warn("Failures processing network-attachment-definition manipulation.");
203 break;
204 default:
205 break;
206 }
207 }
208
209 @Override
210 public void onClose(WatcherException e) {
Jian Lid2ade0f2021-02-19 14:00:07 +0900211 // due to the bugs in fabric8, the watcher might be closed,
212 // we will re-instantiate the watcher in this case
213 // FIXME: https://github.com/fabric8io/kubernetes-client/issues/2135
214 log.warn("Network-attachment-definition watcher OnClose, re-instantiate the watcher...");
215
216 instantiateWatcher();
Jian Li034820d2021-01-15 16:58:48 +0900217 }
218
219 private void processAddition(String resource) {
220 if (!isMaster()) {
221 return;
222 }
223
Jian Li810f58c2021-02-27 01:10:50 +0900224 String name = parseResourceName(resource);
Jian Li034820d2021-01-15 16:58:48 +0900225
226 log.trace("Process NetworkAttachmentDefinition {} creating event from API server.",
227 name);
228
229 KubevirtNetwork network = parseKubevirtNetwork(resource);
230 if (network != null) {
Jian Liea585882021-01-19 23:42:55 +0900231 if (adminService.network(network.networkId()) == null) {
232 adminService.createNetwork(network);
233 }
Jian Li034820d2021-01-15 16:58:48 +0900234 }
235 }
236
237 private void processModification(String resource) {
238 if (!isMaster()) {
239 return;
240 }
241
Jian Li810f58c2021-02-27 01:10:50 +0900242 String name = parseResourceName(resource);
Jian Li034820d2021-01-15 16:58:48 +0900243
244 log.trace("Process NetworkAttachmentDefinition {} updating event from API server.",
245 name);
246
247 KubevirtNetwork network = parseKubevirtNetwork(resource);
248 if (network != null) {
249 adminService.updateNetwork(network);
250 }
251 }
252
253 private void processDeletion(String resource) {
254 if (!isMaster()) {
255 return;
256 }
257
Jian Li810f58c2021-02-27 01:10:50 +0900258 String name = parseResourceName(resource);
Jian Li034820d2021-01-15 16:58:48 +0900259
260 log.trace("Process NetworkAttachmentDefinition {} removal event from API server.",
261 name);
262
Jian Li362bd8b2021-06-11 14:47:57 +0900263 if (adminService.network(name) != null) {
264 adminService.removeNetwork(name);
265 }
Jian Li034820d2021-01-15 16:58:48 +0900266 }
267
268 private boolean isMaster() {
269 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
270 }
271
Jian Li034820d2021-01-15 16:58:48 +0900272 private KubevirtNetwork parseKubevirtNetwork(String resource) {
273 try {
274 JSONObject json = new JSONObject(resource);
Jian Li810f58c2021-02-27 01:10:50 +0900275 String name = parseResourceName(resource);
Jian Li034820d2021-01-15 16:58:48 +0900276 JSONObject annots = json.getJSONObject("metadata").getJSONObject("annotations");
Jian Li9b718fb2021-07-23 19:54:31 +0900277 if (!annots.has(NETWORK_CONFIG)) {
278 // SR-IOV network does not contain network-config field
279 return null;
280 }
Jian Li034820d2021-01-15 16:58:48 +0900281 String networkConfig = annots.getString(NETWORK_CONFIG);
282 if (networkConfig != null) {
283 KubevirtNetwork.Builder builder = DefaultKubevirtNetwork.builder();
284
285 JSONObject configJson = new JSONObject(networkConfig);
Jian Li340819a2021-11-05 17:22:50 +0900286 String type = configJson.getString(TYPE).toUpperCase(Locale.ROOT);
Jian Li034820d2021-01-15 16:58:48 +0900287 Integer mtu = configJson.getInt(MTU);
288 String gatewayIp = configJson.getString(GATEWAY_IP);
Jian Lie2abe812021-08-12 18:03:30 +0900289 boolean defaultRoute = configJson.getBoolean(DEFAULT_ROUTE);
Jian Li034820d2021-01-15 16:58:48 +0900290
291 if (!type.equalsIgnoreCase(FLAT.name())) {
292 builder.segmentId(configJson.getString(SEGMENT_ID));
293 }
294
295 String cidr = configJson.getString(CIDR);
296
297 JSONObject poolJson = configJson.getJSONObject(IP_POOL);
298 if (poolJson != null) {
299 String start = poolJson.getString(START);
300 String end = poolJson.getString(END);
301 builder.ipPool(new KubevirtIpPool(
302 IpAddress.valueOf(start), IpAddress.valueOf(end)));
303 }
304
Jian Li7a581b12021-02-18 14:24:32 +0900305 if (configJson.has(HOST_ROUTES)) {
306 JSONArray routesJson = configJson.getJSONArray(HOST_ROUTES);
307 Set<KubevirtHostRoute> hostRoutes = new HashSet<>();
308 if (routesJson != null) {
309 for (int i = 0; i < routesJson.length(); i++) {
310 JSONObject route = routesJson.getJSONObject(i);
311 String destinationStr = route.getString(DESTINATION);
312 String nexthopStr = route.getString(NEXTHOP);
Jian Li4c35a262021-02-01 20:36:45 +0900313
Jian Li7a581b12021-02-18 14:24:32 +0900314 if (StringUtils.isNotEmpty(destinationStr) &&
315 StringUtils.isNotEmpty(nexthopStr)) {
316 hostRoutes.add(new KubevirtHostRoute(
317 IpPrefix.valueOf(destinationStr),
318 IpAddress.valueOf(nexthopStr)));
319 }
Jian Li4c35a262021-02-01 20:36:45 +0900320 }
321 }
Jian Li7a581b12021-02-18 14:24:32 +0900322 builder.hostRoutes(hostRoutes);
Jian Li4c35a262021-02-01 20:36:45 +0900323 }
Jian Li4c35a262021-02-01 20:36:45 +0900324
Jian Li7a581b12021-02-18 14:24:32 +0900325 if (configJson.has(DNSES)) {
326 JSONArray dnsesJson = configJson.getJSONArray(DNSES);
327 Set<IpAddress> dnses = new HashSet<>();
328 if (dnsesJson != null) {
329 for (int i = 0; i < dnsesJson.length(); i++) {
330 String dns = dnsesJson.getString(i);
331 if (StringUtils.isNotEmpty(dns)) {
332 dnses.add(IpAddress.valueOf(dns));
333 }
334 }
Jian Li4c35a262021-02-01 20:36:45 +0900335 }
Jian Li7a581b12021-02-18 14:24:32 +0900336 builder.dnses(dnses);
Jian Li4c35a262021-02-01 20:36:45 +0900337 }
Jian Li4c35a262021-02-01 20:36:45 +0900338
Jian Li034820d2021-01-15 16:58:48 +0900339 builder.networkId(name).name(name).type(Type.valueOf(type))
Jian Lie2abe812021-08-12 18:03:30 +0900340 .mtu(mtu).gatewayIp(IpAddress.valueOf(gatewayIp))
341 .defaultRoute(defaultRoute).cidr(cidr);
Jian Li034820d2021-01-15 16:58:48 +0900342
343 return builder.build();
344 }
345 } catch (JSONException e) {
Jian Lif3a3c5a2021-06-30 10:21:31 +0900346 log.error("Failed to parse network attachment definition object", e);
Jian Li034820d2021-01-15 16:58:48 +0900347 }
348
349 return null;
350 }
351 }
352}