blob: c3861f7469f45bafaa9a8a265501591f5c7e9af9 [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;
26import org.onosproject.kubevirtnode.api.KubevirtNodeState;
Jian Lie2a53cb2020-12-19 01:26:57 +090027import org.onosproject.rest.AbstractWebResource;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
31import javax.ws.rs.Consumes;
Jian Lib230e07c2020-12-21 11:25:12 +090032import javax.ws.rs.DELETE;
33import javax.ws.rs.GET;
Jian Lie2a53cb2020-12-19 01:26:57 +090034import javax.ws.rs.POST;
Jian Lib230e07c2020-12-21 11:25:12 +090035import javax.ws.rs.PUT;
Jian Lie2a53cb2020-12-19 01:26:57 +090036import javax.ws.rs.Path;
Jian Lib230e07c2020-12-21 11:25:12 +090037import javax.ws.rs.PathParam;
Jian Lie2a53cb2020-12-19 01:26:57 +090038import javax.ws.rs.Produces;
Jian Lib230e07c2020-12-21 11:25:12 +090039import javax.ws.rs.core.Context;
Jian Lie2a53cb2020-12-19 01:26:57 +090040import javax.ws.rs.core.MediaType;
41import javax.ws.rs.core.Response;
Jian Lib230e07c2020-12-21 11:25:12 +090042import javax.ws.rs.core.UriBuilder;
43import javax.ws.rs.core.UriInfo;
Jian Lie2a53cb2020-12-19 01:26:57 +090044import java.io.InputStream;
Jian Lib230e07c2020-12-21 11:25:12 +090045import java.util.Set;
Jian Lie2a53cb2020-12-19 01:26:57 +090046
Jian Lib230e07c2020-12-21 11:25:12 +090047import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
48import static javax.ws.rs.core.Response.created;
49import static org.onlab.util.Tools.nullIsIllegal;
50import static org.onlab.util.Tools.readTreeFromStream;
Jian Libe478b72021-05-14 15:21:08 +090051import static org.onosproject.kubevirtnode.api.KubevirtNodeState.COMPLETE;
52import static org.onosproject.kubevirtnode.api.KubevirtNodeState.INIT;
Jian Li528335e2021-07-08 20:52:50 +090053import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.waitFor;
Jian Lib230e07c2020-12-21 11:25:12 +090054
55/**
56 * Handles REST API call of KubeVirt node config.
57 */
Jian Lif2483072020-12-25 02:24:16 +090058@Path("node")
Jian Lie2a53cb2020-12-19 01:26:57 +090059public class KubevirtNodeWebResource extends AbstractWebResource {
60
61 private final Logger log = LoggerFactory.getLogger(getClass());
62
Jian Lib230e07c2020-12-21 11:25:12 +090063 private static final String MESSAGE_NODE = "Received node %s request";
64 private static final String NODES = "nodes";
65 private static final String CREATE = "CREATE";
66 private static final String UPDATE = "UPDATE";
67 private static final String NODE_ID = "NODE_ID";
68 private static final String REMOVE = "REMOVE";
69 private static final String QUERY = "QUERY";
Jian Lib230e07c2020-12-21 11:25:12 +090070 private static final String NOT_EXIST = "Not exist";
71 private static final String STATE = "State";
Jian Lif54f8a62021-05-22 00:10:15 +090072 private static final String API_CONFIG = "apiConfig";
73 private static final String OK = "ok";
74 private static final String ERROR = "error";
Jian Lib230e07c2020-12-21 11:25:12 +090075
Jian Li528335e2021-07-08 20:52:50 +090076 private static final int SLEEP_S = 1; // we re-check the status on every 1s
77 private static final long TIMEOUT_MS = 15000;
Jian Libe478b72021-05-14 15:21:08 +090078
Jian Lib230e07c2020-12-21 11:25:12 +090079 private static final String HOST_NAME = "hostname";
80 private static final String ERROR_MESSAGE = " cannot be null";
81
Jian Lib230e07c2020-12-21 11:25:12 +090082 @Context
83 private UriInfo uriInfo;
84
Jian Lie2a53cb2020-12-19 01:26:57 +090085 /**
86 * Creates a set of KubeVirt nodes' config from the JSON input stream.
87 *
88 * @param input KubeVirt nodes JSON input stream
89 * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
90 * is malformed
91 * @onos.rsModel KubevirtNode
92 */
93 @POST
Jian Lie2a53cb2020-12-19 01:26:57 +090094 @Consumes(MediaType.APPLICATION_JSON)
95 @Produces(MediaType.APPLICATION_JSON)
Jian Lib230e07c2020-12-21 11:25:12 +090096 public Response createNodes(InputStream input) {
97 log.trace(String.format(MESSAGE_NODE, CREATE));
98
Jian Li35813002021-02-19 10:28:01 +090099 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
100
Jian Lib230e07c2020-12-21 11:25:12 +0900101 readNodeConfiguration(input).forEach(node -> {
Jian Li35813002021-02-19 10:28:01 +0900102 KubevirtNode existing = service.node(node.hostname());
Jian Lib230e07c2020-12-21 11:25:12 +0900103 if (existing == null) {
Jian Li35813002021-02-19 10:28:01 +0900104 service.createNode(node);
Jian Lib230e07c2020-12-21 11:25:12 +0900105 }
106 });
107
108 UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
109 .path(NODES)
110 .path(NODE_ID);
111
112 return created(locationBuilder.build()).build();
113 }
114
115 /**
116 * Updates a set of KubeVirt nodes' config from the JSON input stream.
117 *
118 * @param input KubeVirt nodes JSON input stream
119 * @return 200 OK with the updated KubeVirt node's config, 400 BAD_REQUEST
120 * if the JSON is malformed, and 304 NOT_MODIFIED without the updated config
121 * @onos.rsModel KubevirtNode
122 */
123 @PUT
Jian Lib230e07c2020-12-21 11:25:12 +0900124 @Consumes(MediaType.APPLICATION_JSON)
125 @Produces(MediaType.APPLICATION_JSON)
126 public Response updateNodes(InputStream input) {
127 log.trace(String.format(MESSAGE_NODE, UPDATE));
128
Jian Li35813002021-02-19 10:28:01 +0900129 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
Jian Lib230e07c2020-12-21 11:25:12 +0900130 Set<KubevirtNode> nodes = readNodeConfiguration(input);
131 for (KubevirtNode node: nodes) {
Jian Li35813002021-02-19 10:28:01 +0900132 KubevirtNode existing = service.node(node.hostname());
Jian Lib230e07c2020-12-21 11:25:12 +0900133 if (existing == null) {
134 log.warn("There is no node configuration to update : {}", node.hostname());
135 return Response.notModified().build();
136 } else if (!existing.equals(node)) {
Jian Li35813002021-02-19 10:28:01 +0900137 service.updateNode(node);
Jian Lib230e07c2020-12-21 11:25:12 +0900138 }
139 }
140
Jian Lie2a53cb2020-12-19 01:26:57 +0900141 return Response.ok().build();
142 }
Jian Lib230e07c2020-12-21 11:25:12 +0900143
144 /**
145 * Removes a set of KubeVirt nodes' config from the JSON input stream.
146 *
147 * @param hostname host name contained in KubeVirt nodes configuration
148 * @return 204 NO_CONTENT, 400 BAD_REQUEST if the JSON is malformed, and
149 * 304 NOT_MODIFIED without the updated config
150 */
151 @DELETE
Jian Lif2483072020-12-25 02:24:16 +0900152 @Path("{hostname}")
Jian Lib230e07c2020-12-21 11:25:12 +0900153 @Consumes(MediaType.APPLICATION_JSON)
154 @Produces(MediaType.APPLICATION_JSON)
155 public Response deleteNode(@PathParam("hostname") String hostname) {
156 log.trace(String.format(MESSAGE_NODE, REMOVE));
157
Jian Li35813002021-02-19 10:28:01 +0900158 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
159 KubevirtNode existing = service.node(
Jian Lib230e07c2020-12-21 11:25:12 +0900160 nullIsIllegal(hostname, HOST_NAME + ERROR_MESSAGE));
161
162 if (existing == null) {
163 log.warn("There is no node configuration to delete : {}", hostname);
164 return Response.notModified().build();
165 } else {
Jian Li35813002021-02-19 10:28:01 +0900166 service.removeNode(hostname);
Jian Lib230e07c2020-12-21 11:25:12 +0900167 }
168
169 return Response.noContent().build();
170 }
171
172 /**
173 * Obtains the state of the KubeVirt node.
174 *
175 * @param hostname hostname of the KubeVirt
176 * @return the state of the KubeVirt node in Json
177 */
178 @GET
179 @Produces(MediaType.APPLICATION_JSON)
180 @Path("state/{hostname}")
181 public Response stateOfNode(@PathParam("hostname") String hostname) {
182 log.trace(String.format(MESSAGE_NODE, QUERY));
183
Jian Li35813002021-02-19 10:28:01 +0900184 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
185 KubevirtNode node = service.node(hostname);
Jian Lib230e07c2020-12-21 11:25:12 +0900186 String nodeState = node != null ? node.state().toString() : NOT_EXIST;
187
188 return ok(mapper().createObjectNode().put(STATE, nodeState)).build();
189 }
190
191 /**
192 * Initializes KubeVirt node.
193 *
194 * @param hostname hostname of KubeVirt node
195 * @return 200 OK with init result, 404 not found, 500 server error
196 */
197 @GET
198 @Produces(MediaType.APPLICATION_JSON)
Jian Lif2483072020-12-25 02:24:16 +0900199 @Path("init/{hostname}")
Jian Lib230e07c2020-12-21 11:25:12 +0900200 public Response initNode(@PathParam("hostname") String hostname) {
201 log.trace(String.format(MESSAGE_NODE, QUERY));
202
Jian Li35813002021-02-19 10:28:01 +0900203 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
204 KubevirtNode node = service.node(hostname);
Jian Lib230e07c2020-12-21 11:25:12 +0900205 if (node == null) {
206 log.error("Given node {} does not exist", hostname);
207 return Response.serverError().build();
208 }
Jian Libe478b72021-05-14 15:21:08 +0900209 KubevirtNode updated = node.updateState(INIT);
Jian Li35813002021-02-19 10:28:01 +0900210 service.updateNode(updated);
Jian Lib230e07c2020-12-21 11:25:12 +0900211 return ok(mapper().createObjectNode()).build();
212 }
213
214 /**
215 * Initializes all KubeVirt nodes.
216 *
217 * @return 200 OK with init result, 500 server error
218 */
219 @GET
220 @Produces(MediaType.APPLICATION_JSON)
221 @Path("init/all")
222 public Response initAllNodes() {
223 log.trace(String.format(MESSAGE_NODE, QUERY));
224
Jian Li35813002021-02-19 10:28:01 +0900225 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
226
227 service.nodes()
Jian Lib230e07c2020-12-21 11:25:12 +0900228 .forEach(n -> {
Jian Libe478b72021-05-14 15:21:08 +0900229 KubevirtNode updated = n.updateState(INIT);
Jian Li35813002021-02-19 10:28:01 +0900230 service.updateNode(updated);
Jian Lib230e07c2020-12-21 11:25:12 +0900231 });
232
233 return ok(mapper().createObjectNode()).build();
234 }
235
236 /**
237 * Initializes KubeVirt nodes which are in the stats other than COMPLETE.
238 *
239 * @return 200 OK with init result, 500 server error
240 */
241 @GET
242 @Produces(MediaType.APPLICATION_JSON)
243 @Path("init/incomplete")
244 public Response initIncompleteNodes() {
245 log.trace(String.format(MESSAGE_NODE, QUERY));
246
Jian Li35813002021-02-19 10:28:01 +0900247 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
248 service.nodes().stream()
Jian Lib230e07c2020-12-21 11:25:12 +0900249 .filter(n -> n.state() != KubevirtNodeState.COMPLETE)
250 .forEach(n -> {
Jian Libe478b72021-05-14 15:21:08 +0900251 KubevirtNode updated = n.updateState(INIT);
Jian Li35813002021-02-19 10:28:01 +0900252 service.updateNode(updated);
Jian Lib230e07c2020-12-21 11:25:12 +0900253 });
254
255 return ok(mapper().createObjectNode()).build();
256 }
257
Jian Libe478b72021-05-14 15:21:08 +0900258 /**
259 * Synchronizes the flow rules.
260 *
261 * @return 200 OK with sync result, 404 not found
262 */
263 @GET
264 @Produces(MediaType.APPLICATION_JSON)
265 @Path("sync/rules")
266 public Response syncRules() {
267
268 KubevirtNodeAdminService service = get(KubevirtNodeAdminService.class);
269
Jian Li528335e2021-07-08 20:52:50 +0900270 service.completeNodes().forEach(node -> syncRulesBase(service, node));
Jian Libe478b72021-05-14 15:21:08 +0900271 return ok(mapper().createObjectNode()).build();
272 }
273
Jian Lif54f8a62021-05-22 00:10:15 +0900274 /**
275 * Returns the health check result.
276 *
277 * @return 200 OK with health check result, 404 not found
278 */
279 @GET
280 @Produces(MediaType.APPLICATION_JSON)
281 @Path("healthz")
282 public Response healthz() {
283 KubevirtApiConfigService configService = get(KubevirtApiConfigService.class);
284 KubevirtApiConfig config = configService.apiConfig();
285
286 // TODO: we need to add more health check items
287 ObjectNode jsonResult = mapper().createObjectNode();
288 String result = config != null ? OK : ERROR;
289 jsonResult.put(API_CONFIG, result);
290 return ok(jsonResult).build();
291 }
292
Jian Li528335e2021-07-08 20:52:50 +0900293 private void syncRulesBase(KubevirtNodeAdminService service, KubevirtNode node) {
Jian Libe478b72021-05-14 15:21:08 +0900294 KubevirtNode updated = node.updateState(INIT);
Jian Libe478b72021-05-14 15:21:08 +0900295 service.updateNode(updated);
296
297 boolean result = true;
298 long timeoutExpiredMs = System.currentTimeMillis() + TIMEOUT_MS;
299
300 while (service.node(node.hostname()).state() != COMPLETE) {
Jian Libe478b72021-05-14 15:21:08 +0900301 long waitMs = timeoutExpiredMs - System.currentTimeMillis();
302
Jian Li528335e2021-07-08 20:52:50 +0900303 waitFor(SLEEP_S);
Jian Libe478b72021-05-14 15:21:08 +0900304
305 if (waitMs <= 0) {
306 result = false;
307 break;
308 }
309 }
310
311 if (result) {
312 log.info("Successfully synchronize flow rules for node {}!", node.hostname());
313 } else {
314 log.warn("Failed to synchronize flow rules for node {}.", node.hostname());
315 }
316 }
317
Jian Lib230e07c2020-12-21 11:25:12 +0900318 private Set<KubevirtNode> readNodeConfiguration(InputStream input) {
319 Set<KubevirtNode> nodeSet = Sets.newHashSet();
320 try {
321 JsonNode jsonTree = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
322 ArrayNode nodes = (ArrayNode) jsonTree.path(NODES);
323 nodes.forEach(node -> {
324 try {
325 ObjectNode objectNode = node.deepCopy();
326 KubevirtNode kubevirtNode =
327 codec(KubevirtNode.class).decode(objectNode, this);
328 nodeSet.add(kubevirtNode);
329 } catch (Exception e) {
330 log.error("Exception occurred due to {}", e);
331 throw new IllegalArgumentException();
332 }
333 });
334 } catch (Exception e) {
335 throw new IllegalArgumentException(e);
336 }
337
338 return nodeSet;
339 }
Jian Lie2a53cb2020-12-19 01:26:57 +0900340}