blob: b659ec6263e4d3fc00543e47884cf28319fa150a [file] [log] [blame]
Jian Lie2a53cb2020-12-19 01:26:57 +09001/*
2 * Copyright 2020-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.web;
17
Jian Lib230e07c2020-12-21 11:25:12 +090018import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
21import com.google.common.collect.Sets;
Jian Lif54f8a62021-05-22 00:10:15 +090022import org.onosproject.kubevirtnode.api.KubevirtApiConfig;
23import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
Jian Lib230e07c2020-12-21 11:25:12 +090024import org.onosproject.kubevirtnode.api.KubevirtNode;
25import org.onosproject.kubevirtnode.api.KubevirtNodeAdminService;
Jian Lie0eaf5c2021-09-06 10:02:13 +090026import org.onosproject.kubevirtnode.api.KubevirtNodeService;
Jian Lib230e07c2020-12-21 11:25:12 +090027import org.onosproject.kubevirtnode.api.KubevirtNodeState;
Jian Lie2a53cb2020-12-19 01:26:57 +090028import org.onosproject.rest.AbstractWebResource;
29import org.slf4j.Logger;
30import org.slf4j.LoggerFactory;
31
32import javax.ws.rs.Consumes;
Jian Lib230e07c2020-12-21 11:25:12 +090033import javax.ws.rs.DELETE;
34import javax.ws.rs.GET;
Jian Lie2a53cb2020-12-19 01:26:57 +090035import javax.ws.rs.POST;
Jian Lib230e07c2020-12-21 11:25:12 +090036import javax.ws.rs.PUT;
Jian Lie2a53cb2020-12-19 01:26:57 +090037import javax.ws.rs.Path;
Jian Lib230e07c2020-12-21 11:25:12 +090038import javax.ws.rs.PathParam;
Jian Lie2a53cb2020-12-19 01:26:57 +090039import javax.ws.rs.Produces;
Jian Lib230e07c2020-12-21 11:25:12 +090040import javax.ws.rs.core.Context;
Jian Lie2a53cb2020-12-19 01:26:57 +090041import javax.ws.rs.core.MediaType;
42import javax.ws.rs.core.Response;
Jian Lib230e07c2020-12-21 11:25:12 +090043import javax.ws.rs.core.UriBuilder;
44import javax.ws.rs.core.UriInfo;
Jian Lie2a53cb2020-12-19 01:26:57 +090045import java.io.InputStream;
Jian Lib230e07c2020-12-21 11:25:12 +090046import java.util.Set;
Jian Lie2a53cb2020-12-19 01:26:57 +090047
Jian Lib230e07c2020-12-21 11:25:12 +090048import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
49import static javax.ws.rs.core.Response.created;
50import static org.onlab.util.Tools.nullIsIllegal;
51import static org.onlab.util.Tools.readTreeFromStream;
Jian Libe478b72021-05-14 15:21:08 +090052import static org.onosproject.kubevirtnode.api.KubevirtNodeState.COMPLETE;
53import static org.onosproject.kubevirtnode.api.KubevirtNodeState.INIT;
Jian Li528335e2021-07-08 20:52:50 +090054import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.waitFor;
Jian Lib230e07c2020-12-21 11:25:12 +090055
56/**
57 * Handles REST API call of KubeVirt node config.
58 */
Jian Lif2483072020-12-25 02:24:16 +090059@Path("node")
Jian Lie2a53cb2020-12-19 01:26:57 +090060public class KubevirtNodeWebResource extends AbstractWebResource {
61
62 private final Logger log = LoggerFactory.getLogger(getClass());
63
Jian Lib230e07c2020-12-21 11:25:12 +090064 private static final String MESSAGE_NODE = "Received node %s request";
65 private static final String NODES = "nodes";
66 private static final String CREATE = "CREATE";
67 private static final String UPDATE = "UPDATE";
68 private static final String NODE_ID = "NODE_ID";
69 private static final String REMOVE = "REMOVE";
70 private static final String QUERY = "QUERY";
Jian Lib230e07c2020-12-21 11:25:12 +090071 private static final String NOT_EXIST = "Not exist";
72 private static final String STATE = "State";
Jian Lif54f8a62021-05-22 00:10:15 +090073 private static final String API_CONFIG = "apiConfig";
74 private static final String OK = "ok";
75 private static final String ERROR = "error";
Jian Lib230e07c2020-12-21 11:25:12 +090076
Jian Li528335e2021-07-08 20:52:50 +090077 private static final int SLEEP_S = 1; // we re-check the status on every 1s
78 private static final long TIMEOUT_MS = 15000;
Jian Libe478b72021-05-14 15:21:08 +090079
Jian Lib230e07c2020-12-21 11:25:12 +090080 private static final String HOST_NAME = "hostname";
81 private static final String ERROR_MESSAGE = " cannot be null";
82
Jian Lib230e07c2020-12-21 11:25:12 +090083 @Context
84 private UriInfo uriInfo;
85
Jian Lie2a53cb2020-12-19 01:26:57 +090086 /**
87 * Creates a set of KubeVirt nodes' config from the JSON input stream.
88 *
89 * @param input KubeVirt nodes JSON input stream
90 * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
91 * is malformed
92 * @onos.rsModel KubevirtNode
93 */
94 @POST
Jian Lie2a53cb2020-12-19 01:26:57 +090095 @Consumes(MediaType.APPLICATION_JSON)
96 @Produces(MediaType.APPLICATION_JSON)
Jian Lib230e07c2020-12-21 11:25:12 +090097 public Response createNodes(InputStream input) {
98 log.trace(String.format(MESSAGE_NODE, CREATE));
99
Jian Li35813002021-02-19 10:28:01 +0900100 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
101
Jian Lib230e07c2020-12-21 11:25:12 +0900102 readNodeConfiguration(input).forEach(node -> {
Jian Li35813002021-02-19 10:28:01 +0900103 KubevirtNode existing = service.node(node.hostname());
Jian Lib230e07c2020-12-21 11:25:12 +0900104 if (existing == null) {
Jian Li35813002021-02-19 10:28:01 +0900105 service.createNode(node);
Jian Lib230e07c2020-12-21 11:25:12 +0900106 }
107 });
108
109 UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
110 .path(NODES)
111 .path(NODE_ID);
112
113 return created(locationBuilder.build()).build();
114 }
115
116 /**
117 * Updates a set of KubeVirt nodes' config from the JSON input stream.
118 *
119 * @param input KubeVirt nodes JSON input stream
120 * @return 200 OK with the updated KubeVirt node's config, 400 BAD_REQUEST
121 * if the JSON is malformed, and 304 NOT_MODIFIED without the updated config
122 * @onos.rsModel KubevirtNode
123 */
124 @PUT
Jian Lib230e07c2020-12-21 11:25:12 +0900125 @Consumes(MediaType.APPLICATION_JSON)
126 @Produces(MediaType.APPLICATION_JSON)
127 public Response updateNodes(InputStream input) {
128 log.trace(String.format(MESSAGE_NODE, UPDATE));
129
Jian Li35813002021-02-19 10:28:01 +0900130 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
Jian Lib230e07c2020-12-21 11:25:12 +0900131 Set<KubevirtNode> nodes = readNodeConfiguration(input);
132 for (KubevirtNode node: nodes) {
Jian Li35813002021-02-19 10:28:01 +0900133 KubevirtNode existing = service.node(node.hostname());
Jian Lib230e07c2020-12-21 11:25:12 +0900134 if (existing == null) {
135 log.warn("There is no node configuration to update : {}", node.hostname());
136 return Response.notModified().build();
137 } else if (!existing.equals(node)) {
Jian Li35813002021-02-19 10:28:01 +0900138 service.updateNode(node);
Jian Lib230e07c2020-12-21 11:25:12 +0900139 }
140 }
141
Jian Lie2a53cb2020-12-19 01:26:57 +0900142 return Response.ok().build();
143 }
Jian Lib230e07c2020-12-21 11:25:12 +0900144
145 /**
146 * Removes a set of KubeVirt nodes' config from the JSON input stream.
147 *
148 * @param hostname host name contained in KubeVirt nodes configuration
149 * @return 204 NO_CONTENT, 400 BAD_REQUEST if the JSON is malformed, and
150 * 304 NOT_MODIFIED without the updated config
151 */
152 @DELETE
Jian Lif2483072020-12-25 02:24:16 +0900153 @Path("{hostname}")
Jian Lib230e07c2020-12-21 11:25:12 +0900154 @Consumes(MediaType.APPLICATION_JSON)
155 @Produces(MediaType.APPLICATION_JSON)
156 public Response deleteNode(@PathParam("hostname") String hostname) {
157 log.trace(String.format(MESSAGE_NODE, REMOVE));
158
Jian Li35813002021-02-19 10:28:01 +0900159 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
160 KubevirtNode existing = service.node(
Jian Lib230e07c2020-12-21 11:25:12 +0900161 nullIsIllegal(hostname, HOST_NAME + ERROR_MESSAGE));
162
163 if (existing == null) {
164 log.warn("There is no node configuration to delete : {}", hostname);
165 return Response.notModified().build();
166 } else {
Jian Li35813002021-02-19 10:28:01 +0900167 service.removeNode(hostname);
Jian Lib230e07c2020-12-21 11:25:12 +0900168 }
169
170 return Response.noContent().build();
171 }
172
173 /**
174 * Obtains the state of the KubeVirt node.
175 *
176 * @param hostname hostname of the KubeVirt
177 * @return the state of the KubeVirt node in Json
178 */
179 @GET
180 @Produces(MediaType.APPLICATION_JSON)
181 @Path("state/{hostname}")
182 public Response stateOfNode(@PathParam("hostname") String hostname) {
183 log.trace(String.format(MESSAGE_NODE, QUERY));
184
Jian Li35813002021-02-19 10:28:01 +0900185 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
186 KubevirtNode node = service.node(hostname);
Jian Lib230e07c2020-12-21 11:25:12 +0900187 String nodeState = node != null ? node.state().toString() : NOT_EXIST;
188
189 return ok(mapper().createObjectNode().put(STATE, nodeState)).build();
190 }
191
192 /**
193 * Initializes KubeVirt node.
194 *
195 * @param hostname hostname of KubeVirt node
196 * @return 200 OK with init result, 404 not found, 500 server error
197 */
198 @GET
199 @Produces(MediaType.APPLICATION_JSON)
Jian Lif2483072020-12-25 02:24:16 +0900200 @Path("init/{hostname}")
Jian Lib230e07c2020-12-21 11:25:12 +0900201 public Response initNode(@PathParam("hostname") String hostname) {
202 log.trace(String.format(MESSAGE_NODE, QUERY));
203
Jian Li35813002021-02-19 10:28:01 +0900204 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
205 KubevirtNode node = service.node(hostname);
Jian Lib230e07c2020-12-21 11:25:12 +0900206 if (node == null) {
207 log.error("Given node {} does not exist", hostname);
208 return Response.serverError().build();
209 }
Jian Libe478b72021-05-14 15:21:08 +0900210 KubevirtNode updated = node.updateState(INIT);
Jian Li35813002021-02-19 10:28:01 +0900211 service.updateNode(updated);
Jian Lib230e07c2020-12-21 11:25:12 +0900212 return ok(mapper().createObjectNode()).build();
213 }
214
215 /**
216 * Initializes all KubeVirt nodes.
217 *
218 * @return 200 OK with init result, 500 server error
219 */
220 @GET
221 @Produces(MediaType.APPLICATION_JSON)
222 @Path("init/all")
223 public Response initAllNodes() {
224 log.trace(String.format(MESSAGE_NODE, QUERY));
225
Jian Li35813002021-02-19 10:28:01 +0900226 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
227
228 service.nodes()
Jian Lib230e07c2020-12-21 11:25:12 +0900229 .forEach(n -> {
Jian Libe478b72021-05-14 15:21:08 +0900230 KubevirtNode updated = n.updateState(INIT);
Jian Li35813002021-02-19 10:28:01 +0900231 service.updateNode(updated);
Jian Lib230e07c2020-12-21 11:25:12 +0900232 });
233
234 return ok(mapper().createObjectNode()).build();
235 }
236
237 /**
238 * Initializes KubeVirt nodes which are in the stats other than COMPLETE.
239 *
240 * @return 200 OK with init result, 500 server error
241 */
242 @GET
243 @Produces(MediaType.APPLICATION_JSON)
244 @Path("init/incomplete")
245 public Response initIncompleteNodes() {
246 log.trace(String.format(MESSAGE_NODE, QUERY));
247
Jian Li35813002021-02-19 10:28:01 +0900248 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
249 service.nodes().stream()
Jian Lib230e07c2020-12-21 11:25:12 +0900250 .filter(n -> n.state() != KubevirtNodeState.COMPLETE)
251 .forEach(n -> {
Jian Libe478b72021-05-14 15:21:08 +0900252 KubevirtNode updated = n.updateState(INIT);
Jian Li35813002021-02-19 10:28:01 +0900253 service.updateNode(updated);
Jian Lib230e07c2020-12-21 11:25:12 +0900254 });
255
256 return ok(mapper().createObjectNode()).build();
257 }
258
Jian Libe478b72021-05-14 15:21:08 +0900259 /**
260 * Synchronizes the flow rules.
261 *
262 * @return 200 OK with sync result, 404 not found
263 */
264 @GET
265 @Produces(MediaType.APPLICATION_JSON)
266 @Path("sync/rules")
267 public Response syncRules() {
268
269 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
270
Jian Li528335e2021-07-08 20:52:50 +0900271 service.completeNodes().forEach(node -> syncRulesBase(service, node));
Jian Libe478b72021-05-14 15:21:08 +0900272 return ok(mapper().createObjectNode()).build();
273 }
274
Jian Lif54f8a62021-05-22 00:10:15 +0900275 /**
276 * Returns the health check result.
277 *
278 * @return 200 OK with health check result, 404 not found
279 */
280 @GET
281 @Produces(MediaType.APPLICATION_JSON)
282 @Path("healthz")
283 public Response healthz() {
284 KubevirtApiConfigService configService = get(KubevirtApiConfigService.class);
Jian Lie0eaf5c2021-09-06 10:02:13 +0900285 KubevirtNodeService nodeService = get(KubevirtNodeService.class);
Jian Lif54f8a62021-05-22 00:10:15 +0900286
287 // TODO: we need to add more health check items
Jian Lie0eaf5c2021-09-06 10:02:13 +0900288 boolean allInit = true;
289 KubevirtApiConfig config = configService.apiConfig();
290
291 if (nodeService.nodes().size() == 0) {
292 allInit = false;
293 } else {
294 for (KubevirtNode node : nodeService.nodes()) {
295 if (node.state() != INIT) {
296 allInit = false;
297 }
298 }
299 }
300
301 String result = ERROR;
302 if (config != null && !allInit) {
303 result = OK;
304 }
305
Jian Lif54f8a62021-05-22 00:10:15 +0900306 ObjectNode jsonResult = mapper().createObjectNode();
Jian Lif54f8a62021-05-22 00:10:15 +0900307 jsonResult.put(API_CONFIG, result);
308 return ok(jsonResult).build();
309 }
310
Jian Li528335e2021-07-08 20:52:50 +0900311 private void syncRulesBase(KubevirtNodeAdminService service, KubevirtNode node) {
Jian Libe478b72021-05-14 15:21:08 +0900312 KubevirtNode updated = node.updateState(INIT);
Jian Libe478b72021-05-14 15:21:08 +0900313 service.updateNode(updated);
314
315 boolean result = true;
316 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
317
318 while (service.node(node.hostname()).state() != COMPLETE) {
Jian Libe478b72021-05-14 15:21:08 +0900319 long waitMs = timeoutExpiredMs - System.currentTimeMillis();
320
Jian Li528335e2021-07-08 20:52:50 +0900321 waitFor(SLEEP_S);
Jian Libe478b72021-05-14 15:21:08 +0900322
323 if (waitMs <= 0) {
324 result = false;
325 break;
326 }
327 }
328
329 if (result) {
330 log.info("Successfully synchronize flow rules for node {}!", node.hostname());
331 } else {
332 log.warn("Failed to synchronize flow rules for node {}.", node.hostname());
333 }
334 }
335
Jian Lib230e07c2020-12-21 11:25:12 +0900336 private Set<KubevirtNode> readNodeConfiguration(InputStream input) {
337 Set<KubevirtNode> nodeSet = Sets.newHashSet();
338 try {
339 JsonNode jsonTree = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
340 ArrayNode nodes = (ArrayNode) jsonTree.path(NODES);
341 nodes.forEach(node -> {
342 try {
343 ObjectNode objectNode = node.deepCopy();
344 KubevirtNode kubevirtNode =
345 codec(KubevirtNode.class).decode(objectNode, this);
346 nodeSet.add(kubevirtNode);
347 } catch (Exception e) {
348 log.error("Exception occurred due to {}", e);
349 throw new IllegalArgumentException();
350 }
351 });
352 } catch (Exception e) {
353 throw new IllegalArgumentException(e);
354 }
355
356 return nodeSet;
357 }
Jian Lie2a53cb2020-12-19 01:26:57 +0900358}