74548e724e6a40c21a2509f35561864e670599e3
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.clamp.controlloop.runtime.main.rest;
22
23 import com.fasterxml.jackson.core.JsonProcessingException;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.fasterxml.jackson.databind.PropertyNamingStrategies;
26 import io.swagger.annotations.ApiOperation;
27 import io.swagger.annotations.ApiParam;
28 import io.swagger.annotations.ApiResponse;
29 import io.swagger.annotations.ApiResponses;
30 import io.swagger.annotations.Authorization;
31 import io.swagger.annotations.Extension;
32 import io.swagger.annotations.ExtensionProperty;
33 import io.swagger.annotations.ResponseHeader;
34 import java.util.List;
35 import java.util.UUID;
36 import javax.ws.rs.core.Response.Status;
37 import org.onap.policy.clamp.controlloop.models.messages.rest.commissioning.CommissioningResponse;
38 import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningProvider;
39 import org.onap.policy.clamp.controlloop.runtime.main.web.AbstractRestController;
40 import org.onap.policy.models.base.PfModelException;
41 import org.onap.policy.models.base.PfModelRuntimeException;
42 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
43 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46 import org.springframework.http.HttpStatus;
47 import org.springframework.http.MediaType;
48 import org.springframework.http.ResponseEntity;
49 import org.springframework.web.bind.annotation.DeleteMapping;
50 import org.springframework.web.bind.annotation.GetMapping;
51 import org.springframework.web.bind.annotation.PostMapping;
52 import org.springframework.web.bind.annotation.RequestBody;
53 import org.springframework.web.bind.annotation.RequestHeader;
54 import org.springframework.web.bind.annotation.RequestParam;
55 import org.springframework.web.bind.annotation.RestController;
56
57 /**
58  * Class to provide REST end points for creating, deleting, querying commissioned control loops.
59  */
60 @RestController
61 public class CommissioningController extends AbstractRestController {
62
63     private static final Logger LOGGER = LoggerFactory.getLogger(CommissioningController.class);
64
65     private final CommissioningProvider provider;
66
67     /**
68      * Create Commissioning Controller.
69      *
70      * @param provider the CommissioningProvider
71      */
72     public CommissioningController(CommissioningProvider provider) {
73         this.provider = provider;
74     }
75
76     /**
77      * Creates a control loop definition.
78      *
79      * @param requestId request ID used in ONAP logging
80      * @param body the body of control loop following TOSCA definition
81      * @return a response
82      */
83     // @formatter:off
84     @PostMapping(value = "/commission",
85             consumes = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML},
86             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
87     @ApiOperation(
88         value = "Commissions control loop definitions",
89         notes = "Commissions control loop definitions, returning the commissioned control loop definition IDs",
90         response = CommissioningResponse.class,
91         tags = {"Control Loop Commissioning API"},
92         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
93         responseHeaders = {
94             @ResponseHeader(
95                 name = VERSION_MINOR_NAME,
96                 description = VERSION_MINOR_DESCRIPTION,
97                 response = String.class),
98             @ResponseHeader(
99                 name = VERSION_PATCH_NAME,
100                 description = VERSION_PATCH_DESCRIPTION,
101                 response = String.class),
102             @ResponseHeader(
103                 name = VERSION_LATEST_NAME,
104                 description = VERSION_LATEST_DESCRIPTION,
105                 response = String.class),
106             @ResponseHeader(
107                 name = REQUEST_ID_NAME,
108                 description = REQUEST_ID_HDR_DESCRIPTION,
109                 response = UUID.class)
110         },
111         extensions = {
112             @Extension
113                 (
114                     name = EXTENSION_NAME,
115                     properties = {
116                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
117                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
118                     }
119                 )
120         }
121     )
122     @ApiResponses(
123             value = {
124                 @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
125                 @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
126                 @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
127             }
128     )
129     // @formatter:on
130     public ResponseEntity<CommissioningResponse> create(
131             @RequestHeader(
132                     name = REQUEST_ID_NAME,
133                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
134             @ApiParam(value = "Entity Body of Control Loop", required = true) @RequestBody ToscaServiceTemplate body) {
135         try {
136             return ResponseEntity.ok().body(provider.createControlLoopDefinitions(body));
137
138         } catch (PfModelRuntimeException | PfModelException e) {
139             LOGGER.warn("Commissioning of the control loops failed", e);
140             var resp = new CommissioningResponse();
141             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
142             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
143         }
144
145     }
146
147     /**
148      * Deletes a control loop definition.
149      *
150      * @param requestId request ID used in ONAP logging
151      * @param name the name of the control loop definition to delete
152      * @param version the version of the control loop definition to delete
153      * @return a response
154      */
155     // @formatter:off
156     @DeleteMapping(value = "/commission",
157             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
158     @ApiOperation(value = "Delete a commissioned control loop",
159         notes = "Deletes a Commissioned Control Loop, returning optional error details",
160         response = CommissioningResponse.class,
161         tags = {"Clamp Control Loop Commissioning API"},
162         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
163         responseHeaders = {
164             @ResponseHeader(
165                 name = VERSION_MINOR_NAME,
166                 description = VERSION_MINOR_DESCRIPTION,
167                 response = String.class),
168             @ResponseHeader(
169                 name = VERSION_PATCH_NAME,
170                 description = VERSION_PATCH_DESCRIPTION,
171                 response = String.class),
172             @ResponseHeader(
173                 name = VERSION_LATEST_NAME,
174                 description = VERSION_LATEST_DESCRIPTION,
175                 response = String.class),
176             @ResponseHeader(
177                 name = REQUEST_ID_NAME,
178                 description = REQUEST_ID_HDR_DESCRIPTION,
179                 response = UUID.class)},
180         extensions = {
181             @Extension
182                 (
183                     name = EXTENSION_NAME,
184                     properties = {
185                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
186                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
187                     }
188                 )
189         }
190     )
191     @ApiResponses(
192         value = {
193             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
194             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
195             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
196         }
197     )
198     // @formatter:on
199     public ResponseEntity<CommissioningResponse> delete(
200             @RequestHeader(
201                     name = REQUEST_ID_NAME,
202                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
203             @ApiParam(value = "Control Loop definition name", required = true) @RequestParam(
204                     value = "name") String name,
205             @ApiParam(
206                     value = "Control Loop definition version",
207                     required = true) @RequestParam("version") String version) {
208
209         try {
210             return ResponseEntity.ok().body(provider.deleteControlLoopDefinition(name, version));
211
212         } catch (PfModelRuntimeException | PfModelException e) {
213             LOGGER.warn("Decommisssioning of control loop failed", e);
214             var resp = new CommissioningResponse();
215             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
216             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
217         }
218
219     }
220
221     /**
222      * Queries details of all or specific control loop definitions.
223      *
224      * @param requestId request ID used in ONAP logging
225      * @param name the name of the control loop definition to get, null for all definitions
226      * @param version the version of the control loop definition to get, null for all definitions
227      * @return the control loop definitions
228      */
229     // @formatter:off
230     @GetMapping(value = "/commission",
231             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
232     @ApiOperation(value = "Query details of the requested commissioned control loop definitions",
233         notes = "Queries details of the requested commissioned control loop definitions, "
234             + "returning all control loop details",
235         response = ToscaNodeTemplate.class,
236         tags = {"Clamp Control Loop Commissioning API"},
237         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
238         responseHeaders = {
239             @ResponseHeader(
240                 name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
241                 response = String.class),
242             @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
243                 response = String.class),
244             @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
245                 response = String.class),
246             @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
247                 response = UUID.class)},
248         extensions = {
249             @Extension
250                 (
251                     name = EXTENSION_NAME,
252                     properties = {
253                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
254                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
255                     }
256                 )
257             }
258     )
259     @ApiResponses(
260         value = {
261             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
262             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
263             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
264         }
265     )
266     // @formatter:on
267     public ResponseEntity<?> query(
268             @RequestHeader(
269                     name = REQUEST_ID_NAME,
270                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
271             @ApiParam(value = "Control Loop definition name", required = false) @RequestParam(
272                     value = "name",
273                     required = false) String name,
274             @ApiParam(value = "Control Loop definition version", required = false) @RequestParam(
275                     value = "version",
276                     required = false) String version) {
277
278         try {
279             return ResponseEntity.ok().body(provider.getControlLoopDefinitions(name, version));
280
281         } catch (PfModelRuntimeException | PfModelException e) {
282             LOGGER.warn("Get of control loop definitions failed", e);
283             var resp = new CommissioningResponse();
284             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
285             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
286         }
287
288     }
289
290     /**
291      * Retrieves the Tosca Service Template.
292      *
293      * @param requestId request ID used in ONAP logging
294      * @param name the name of the tosca service template to retrieve
295      * @param version the version of the tosca service template to get
296      * @return the specified tosca service template
297      */
298     // @formatter:off
299     @GetMapping(value = "/commission/toscaservicetemplate",
300             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
301     @ApiOperation(value = "Query details of the requested tosca service templates",
302         notes = "Queries details of the requested commissioned tosca service template, "
303             + "returning all tosca service template details",
304         response = ToscaServiceTemplate.class,
305         tags = {"Clamp Control Loop Commissioning API"},
306         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
307         responseHeaders = {
308             @ResponseHeader(
309                 name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
310                 response = String.class),
311             @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
312                 response = String.class),
313             @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
314                 response = String.class),
315             @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
316                 response = UUID.class)},
317         extensions = {
318             @Extension
319                 (
320                     name = EXTENSION_NAME,
321                     properties = {
322                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
323                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
324                     }
325                 )
326         }
327     )
328     @ApiResponses(
329         value = {
330             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
331             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
332             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
333         }
334     )
335     // @formatter:on
336     public ResponseEntity<?> queryToscaServiceTemplate(
337             @RequestHeader(
338                     name = REQUEST_ID_NAME,
339                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
340             @ApiParam(value = "Tosca service template name", required = false) @RequestParam(
341                     value = "name",
342                     required = false) String name,
343             @ApiParam(value = "Tosca service template version", required = true) @RequestParam(
344                     value = "version",
345                     required = false) String version) {
346
347         try {
348             ObjectMapper mapper = new ObjectMapper();
349             mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
350             String response = mapper.writerWithDefaultPrettyPrinter()
351                 .writeValueAsString(provider.getToscaServiceTemplate(name, version));
352
353             return ResponseEntity.ok().body(response);
354
355         } catch (PfModelRuntimeException | PfModelException e) {
356             LOGGER.warn("Get of tosca service template failed", e);
357             var resp = new CommissioningResponse();
358             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
359             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
360         } catch (JsonProcessingException e) {
361             LOGGER.warn("Get of tosca service template failed", e);
362             var resp = new CommissioningResponse();
363             resp.setErrorDetails(e.getMessage());
364             return ResponseEntity.status(Status.BAD_REQUEST.getStatusCode()).body(resp);
365         }
366     }
367
368     /**
369      * Retrieves the Json Schema for the specified Tosca Service Template.
370      *
371      * @param requestId request ID used in ONAP logging
372      * @param section section of the tosca service template to get schema for
373      * @return the specified tosca service template or section Json Schema
374      */
375     // @formatter:off
376     @GetMapping(value = "/commission/toscaServiceTemplateSchema",
377         produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
378     @ApiOperation(value = "Query details of the requested tosca service template json schema",
379         notes = "Queries details of the requested commissioned tosca service template json schema, "
380             + "returning all tosca service template json schema details",
381         response = ToscaServiceTemplate.class,
382         tags = {"Clamp Control Loop Commissioning API"},
383         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
384         responseHeaders = {
385             @ResponseHeader(
386                 name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
387                 response = String.class),
388             @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
389                 response = String.class),
390             @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
391                 response = String.class),
392             @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
393                 response = UUID.class)},
394         extensions = {
395             @Extension
396                 (
397                     name = EXTENSION_NAME,
398                     properties = {
399                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
400                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
401                     }
402                 )
403         }
404     )
405     @ApiResponses(
406         value = {
407             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
408             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
409             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
410         }
411     )
412     // @formatter:on
413     public ResponseEntity<?> queryToscaServiceTemplateJsonSchema(
414         @RequestHeader(
415             name = REQUEST_ID_NAME,
416             required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
417         @ApiParam(value = "Section of Template schema is desired for", required = false) @RequestParam(
418             value = "section",
419             required = false, defaultValue = "all") String section) {
420         try {
421             return ResponseEntity.ok().body(provider.getToscaServiceTemplateSchema(section));
422
423         } catch (PfModelRuntimeException | PfModelException e) {
424             LOGGER.warn("Get of tosca service template json schema failed", e);
425             var resp = new CommissioningResponse();
426             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
427             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
428         } catch (JsonProcessingException e) {
429             LOGGER.warn("Get of tosca service template json schema failed", e);
430             var resp = new CommissioningResponse();
431             resp.setErrorDetails(e.getMessage());
432             return ResponseEntity.status(Status.BAD_REQUEST.getStatusCode()).body(resp);
433         }
434     }
435
436     /**
437      * Queries the elements of a specific control loop.
438      *
439      * @param requestId request ID used in ONAP logging
440      * @param name the name of the control loop definition to get
441      * @param version the version of the control loop definition to get
442      * @return the control loop element definitions
443      */
444     // @formatter:off
445     @GetMapping(value = "/commission/elements",
446             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
447     @ApiOperation(value = "Query details of the requested commissioned control loop element definitions",
448         notes = "Queries details of the requested commissioned control loop element definitions, "
449             + "returning all control loop elements' details",
450         response = ToscaNodeTemplate.class,
451         tags = {"Clamp Control Loop Commissioning API"},
452         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
453         responseHeaders = {
454             @ResponseHeader(
455                 name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
456                 response = String.class),
457             @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
458                 response = String.class),
459             @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
460                 response = String.class),
461             @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
462                 response = UUID.class)},
463         extensions = {
464             @Extension
465                 (
466                     name = EXTENSION_NAME,
467                     properties = {
468                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
469                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
470                     }
471                 )
472         }
473     )
474     @ApiResponses(
475         value = {
476             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
477             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
478             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
479         }
480     )
481     // @formatter:on
482     public ResponseEntity<?> queryElements(
483             @RequestHeader(
484                     name = REQUEST_ID_NAME,
485                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
486             @ApiParam(value = "Control Loop definition name", required = false) @RequestParam(
487                     value = "name",
488                     required = false) String name,
489             @ApiParam(value = "Control Loop definition version", required = true) @RequestParam(
490                     value = "version",
491                     required = false) String version) {
492
493         try {
494             List<ToscaNodeTemplate> nodeTemplate = provider.getControlLoopDefinitions(name, version);
495             // Prevent ambiguous queries with multiple returns
496             if (nodeTemplate.size() > 1) {
497                 var resp = new CommissioningResponse();
498                 resp.setErrorDetails("Multiple ControlLoops are not supported");
499                 return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(resp);
500             }
501
502             List<ToscaNodeTemplate> response = provider.getControlLoopElementDefinitions(nodeTemplate.get(0));
503             return ResponseEntity.ok().body(response);
504
505         } catch (PfModelRuntimeException | PfModelException e) {
506             LOGGER.warn("Get of control loop element definitions failed", e);
507             var resp = new CommissioningResponse();
508             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
509             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
510         }
511
512     }
513 }