2 * ========================LICENSE_START=================================
4 * ======================================================================
5 * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved.
6 * ======================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ========================LICENSE_END===================================
21 package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v1;
23 import com.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
26 import io.swagger.v3.oas.annotations.Operation;
27 import io.swagger.v3.oas.annotations.Parameter;
28 import io.swagger.v3.oas.annotations.media.ArraySchema;
29 import io.swagger.v3.oas.annotations.media.Content;
30 import io.swagger.v3.oas.annotations.media.Schema;
31 import io.swagger.v3.oas.annotations.responses.ApiResponse;
32 import io.swagger.v3.oas.annotations.responses.ApiResponses;
33 import io.swagger.v3.oas.annotations.tags.Tag;
35 import java.lang.invoke.MethodHandles;
36 import java.time.Instant;
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.List;
43 import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1ClientFactory;
44 import org.onap.ccsdk.oran.a1policymanagementservice.controllers.VoidResponse;
45 import org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2.ErrorResponse;
46 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.EntityNotFoundException;
47 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Lock.LockType;
48 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies;
49 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy;
50 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType;
51 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes;
52 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric;
53 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics;
54 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Service;
55 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58 import org.springframework.beans.factory.annotation.Autowired;
59 import org.springframework.http.HttpStatus;
60 import org.springframework.http.ResponseEntity;
61 import org.springframework.web.bind.annotation.DeleteMapping;
62 import org.springframework.web.bind.annotation.GetMapping;
63 import org.springframework.web.bind.annotation.PutMapping;
64 import org.springframework.web.bind.annotation.RequestBody;
65 import org.springframework.web.bind.annotation.RequestParam;
66 import org.springframework.web.bind.annotation.RestController;
67 import org.springframework.web.reactive.function.client.WebClientResponseException;
68 import reactor.core.publisher.Mono;
71 @Tag(name = Consts.V1_API_NAME)
72 public class PolicyController {
74 public static class RejectionException extends Exception {
75 private static final long serialVersionUID = 1L;
77 private final HttpStatus status;
79 public RejectionException(String message, HttpStatus status) {
88 private PolicyTypes policyTypes;
90 private Policies policies;
92 private A1ClientFactory a1ClientFactory;
94 private Services services;
96 private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
97 private static Gson gson = new GsonBuilder().create();
99 @GetMapping("/policy_schemas") //
100 @Operation(summary = "Returns policy type schema definitions") //
101 @ApiResponses(value = { //
102 @ApiResponse(responseCode = "200", //
103 description = "Policy schemas", //
104 content = @Content(array = @ArraySchema(schema = @Schema(implementation = Object.class)))), //
105 @ApiResponse(responseCode = "404", //
106 description = "Near-RT RIC is not found", //
107 content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) //
109 public ResponseEntity<String> getPolicySchemas( //
110 @Parameter(name = "ric", required = false,
111 description = "The name of the Near-RT RIC to get the definitions for.") //
112 @RequestParam(name = "ric", required = false) String ricName) throws EntityNotFoundException {
113 if (ricName == null) {
114 Collection<PolicyType> types = this.policyTypes.getAll();
115 return new ResponseEntity<>(toPolicyTypeSchemasJson(types), HttpStatus.OK);
117 Collection<PolicyType> types = rics.getRic(ricName).getSupportedPolicyTypes();
118 return new ResponseEntity<>(toPolicyTypeSchemasJson(types), HttpStatus.OK);
122 @GetMapping("/policy_schema")
123 @Operation(summary = "Returns one policy type schema definition")
124 @ApiResponses(value = { //
125 @ApiResponse(responseCode = "200", //
126 description = "Policy schema", //
127 content = @Content(schema = @Schema(implementation = Object.class))),
128 @ApiResponse(responseCode = "404", //
129 description = "The policy type is not found", //
130 content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class)))//
132 public ResponseEntity<String> getPolicySchema( //
133 @Parameter(name = "id", required = true,
134 description = "The identity of the policy type to get the definition for.") //
135 @RequestParam(name = "id", required = true) String id) throws EntityNotFoundException {
136 PolicyType type = policyTypes.getType(id);
137 return new ResponseEntity<>(type.getSchema(), HttpStatus.OK);
140 @GetMapping("/policy_types")
141 @Operation(summary = "Query policy type names")
142 @ApiResponses(value = { //
143 @ApiResponse(responseCode = "200", //
144 description = "Policy type names", //
145 content = @Content(array = @ArraySchema(schema = @Schema(implementation = String.class)))), //
146 @ApiResponse(responseCode = "404", //
147 description = "Near-RT RIC is not found", //
148 content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class)))//
150 public ResponseEntity<String> getPolicyTypes( //
151 @Parameter(name = "ric", required = false, description = "The name of the Near-RT RIC to get types for.") //
152 @RequestParam(name = "ric", required = false) String ricName) throws EntityNotFoundException {
153 if (ricName == null) {
154 Collection<PolicyType> types = this.policyTypes.getAll();
155 return new ResponseEntity<>(toPolicyTypeIdsJson(types), HttpStatus.OK);
157 Collection<PolicyType> types = rics.getRic(ricName).getSupportedPolicyTypes();
158 return new ResponseEntity<>(toPolicyTypeIdsJson(types), HttpStatus.OK);
162 @GetMapping("/policy")
163 @Operation(summary = "Returns a policy configuration") //
164 @ApiResponses(value = { //
165 @ApiResponse(responseCode = "200", //
166 description = "Policy found", //
167 content = @Content(schema = @Schema(implementation = Object.class))), //
168 @ApiResponse(responseCode = "404", //
169 description = "Policy is not found", //
170 content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))//
173 public ResponseEntity<String> getPolicy( //
174 @Parameter(name = "id", required = true, description = "The identity of the policy instance.") //
175 @RequestParam(name = "id", required = true) String id) throws EntityNotFoundException {
176 Policy p = policies.getPolicy(id);
177 return new ResponseEntity<>(p.getJson(), HttpStatus.OK);
180 @DeleteMapping("/policy")
181 @Operation(summary = "Delete a policy")
182 @ApiResponses(value = { //
183 @ApiResponse(responseCode = "200", //
184 description = "Not used", //
185 content = @Content(schema = @Schema(implementation = VoidResponse.class))),
186 @ApiResponse(responseCode = "204", //
187 description = "Policy deleted", //
188 content = @Content(schema = @Schema(implementation = VoidResponse.class))),
189 @ApiResponse(responseCode = "404", //
190 description = "Policy is not found", //
191 content = @Content(schema = @Schema(implementation = String.class))),
192 @ApiResponse(responseCode = "423", //
193 description = "Near-RT RIC is not operational", //
194 content = @Content(schema = @Schema(implementation = String.class)))})
195 public Mono<ResponseEntity<Object>> deletePolicy( //
196 @Parameter(name = "id", required = true, description = "The identity of the policy instance.") //
197 @RequestParam(name = "id", required = true) String id) throws EntityNotFoundException {
198 Policy policy = policies.getPolicy(id);
199 keepServiceAlive(policy.getOwnerServiceId());
200 Ric ric = policy.getRic();
201 return ric.getLock().lock(LockType.SHARED) //
202 .flatMap(notUsed -> assertRicStateIdle(ric)) //
203 .flatMap(notUsed -> a1ClientFactory.createA1Client(policy.getRic())) //
204 .doOnNext(notUsed -> policies.remove(policy)) //
205 .flatMap(client -> client.deletePolicy(policy)) //
206 .doOnNext(notUsed -> ric.getLock().unlockBlocking()) //
207 .doOnError(notUsed -> ric.getLock().unlockBlocking()) //
208 .flatMap(notUsed -> Mono.just(new ResponseEntity<>(HttpStatus.NO_CONTENT)))
209 .onErrorResume(this::handleException);
212 @PutMapping(path = "/policy")
213 @Operation(summary = "Put a policy")
214 @ApiResponses(value = { //
215 @ApiResponse(responseCode = "201", //
216 description = "Policy created", //
217 content = @Content(schema = @Schema(implementation = VoidResponse.class))), //
218 @ApiResponse(responseCode = "200", //
219 description = "Policy updated", //
220 content = @Content(schema = @Schema(implementation = VoidResponse.class))), //
221 @ApiResponse(responseCode = "423", //
222 description = "Near-RT RIC is not operational", //
223 content = @Content(schema = @Schema(implementation = String.class))), //
224 @ApiResponse(responseCode = "404", //
225 description = "Near-RT RIC or policy type is not found", //
226 content = @Content(schema = @Schema(implementation = String.class))) //
228 public Mono<ResponseEntity<Object>> putPolicy( //
229 @Parameter(name = "type", required = false, description = "The name of the policy type.") //
230 @RequestParam(name = "type", required = false, defaultValue = "") String typeName, //
231 @Parameter(name = "id", required = true, description = "The identity of the policy instance.") //
232 @RequestParam(name = "id", required = true) String instanceId, //
233 @Parameter(name = "ric", required = true,
234 description = "The name of the Near-RT RIC where the policy will be " + //
236 @RequestParam(name = "ric", required = true) String ricName, //
237 @Parameter(name = "service", required = true, description = "The name of the service creating the policy.") //
238 @RequestParam(name = "service", required = true) String service, //
239 @Parameter(name = "transient", required = false,
240 description = "If the policy is transient or not (boolean " + //
241 "defaulted to false). A policy is transient if it will be forgotten when the service needs to "
243 "reconnect to the Near-RT RIC.") //
244 @RequestParam(name = "transient", required = false, defaultValue = "false") boolean isTransient, //
245 @RequestBody Object jsonBody) {
247 String jsonString = gson.toJson(jsonBody);
248 Ric ric = rics.get(ricName);
249 PolicyType type = policyTypes.get(typeName);
250 keepServiceAlive(service);
251 if (ric == null || type == null) {
252 return Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND));
254 Policy policy = Policy.builder() //
259 .ownerServiceId(service) //
260 .lastModified(Instant.now()) //
261 .isTransient(isTransient) //
262 .statusNotificationUri("") //
265 final boolean isCreate = this.policies.get(policy.getId()) == null;
267 return ric.getLock().lock(LockType.SHARED) //
268 .flatMap(notUsed -> assertRicStateIdle(ric)) //
269 .flatMap(notUsed -> checkSupportedType(ric, type)) //
270 .flatMap(notUsed -> validateModifiedPolicy(policy)) //
271 .flatMap(notUsed -> a1ClientFactory.createA1Client(ric)) //
272 .flatMap(client -> client.putPolicy(policy)) //
273 .doOnNext(notUsed -> policies.put(policy)) //
274 .doOnNext(notUsed -> ric.getLock().unlockBlocking()) //
275 .doOnError(trowable -> ric.getLock().unlockBlocking()) //
276 .flatMap(notUsed -> Mono.just(new ResponseEntity<>(isCreate ? HttpStatus.CREATED : HttpStatus.OK))) //
277 .onErrorResume(this::handleException);
280 @SuppressWarnings({"unchecked"})
281 private <T> Mono<ResponseEntity<T>> createResponseEntity(String message, HttpStatus status) {
282 ResponseEntity<T> re = new ResponseEntity<>((T) message, status);
283 return Mono.just(re);
286 private <T> Mono<ResponseEntity<T>> handleException(Throwable throwable) {
287 if (throwable instanceof WebClientResponseException) {
288 WebClientResponseException e = (WebClientResponseException) throwable;
289 return createResponseEntity(e.getResponseBodyAsString(), e.getStatusCode());
290 } else if (throwable instanceof RejectionException) {
291 RejectionException e = (RejectionException) throwable;
292 return createResponseEntity(e.getMessage(), e.getStatus());
294 return createResponseEntity(throwable.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
298 private Mono<Object> validateModifiedPolicy(Policy policy) {
299 // Check that ric is not updated
300 Policy current = this.policies.get(policy.getId());
301 if (current != null && !current.getRic().id().equals(policy.getRic().id())) {
302 RejectionException e = new RejectionException("Policy cannot change RIC, policyId: " + current.getId() + //
303 ", RIC name: " + current.getRic().id() + //
304 ", new name: " + policy.getRic().id(), HttpStatus.CONFLICT);
305 logger.debug("Request rejected, {}", e.getMessage());
306 return Mono.error(e);
308 return Mono.just("OK");
311 private Mono<Object> checkSupportedType(Ric ric, PolicyType type) {
312 if (!ric.isSupportingType(type.getId())) {
313 logger.debug("Request rejected, type not supported, RIC: {}", ric);
314 RejectionException e = new RejectionException(
315 "Type: " + type.getId() + " not supported by RIC: " + ric.id(), HttpStatus.NOT_FOUND);
316 return Mono.error(e);
318 return Mono.just("OK");
321 private Mono<Object> assertRicStateIdle(Ric ric) {
322 if (ric.getState() == Ric.RicState.AVAILABLE) {
323 return Mono.just("OK");
325 logger.debug("Request rejected RIC not IDLE, ric: {}", ric);
326 RejectionException e = new RejectionException(
327 "Ric is not operational, RIC name: " + ric.id() + ", state: " + ric.getState(), HttpStatus.LOCKED);
328 return Mono.error(e);
332 @GetMapping("/policies")
333 @Operation(summary = "Query policies")
334 @ApiResponses(value = { //
335 @ApiResponse(responseCode = "200", //
336 description = "Policies", //
337 content = @Content(array = @ArraySchema(schema = @Schema(implementation = PolicyInfo.class)))), //
338 @ApiResponse(responseCode = "404", //
339 description = "Near-RT RIC or type not found", //
340 content = @Content(schema = @Schema(implementation = String.class)))})
341 public ResponseEntity<String> getPolicies( //
342 @Parameter(name = "type", required = false,
343 description = "The name of the policy type to get policies for.") //
344 @RequestParam(name = "type", required = false) String type, //
345 @Parameter(name = "ric", required = false, description = "The name of the Near-RT RIC to get policies for.") //
346 @RequestParam(name = "ric", required = false) String ric, //
347 @Parameter(name = "service", required = false, description = "The name of the service to get policies for.") //
348 @RequestParam(name = "service", required = false) String service) //
350 if ((type != null && this.policyTypes.get(type) == null)) {
351 return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND);
353 if ((ric != null && this.rics.get(ric) == null)) {
354 return new ResponseEntity<>("Near-RT RIC not found", HttpStatus.NOT_FOUND);
357 String filteredPolicies = policiesToJson(filter(type, ric, service));
358 return new ResponseEntity<>(filteredPolicies, HttpStatus.OK);
361 @GetMapping("/policy_ids")
362 @Operation(summary = "Query policies, only policy identities returned")
363 @ApiResponses(value = { //
364 @ApiResponse(responseCode = "200", //
365 description = "Policy identitiess", //
366 content = @Content(array = @ArraySchema(schema = @Schema(implementation = String.class)))),
367 @ApiResponse(responseCode = "404", //
368 description = "Near-RT RIC or type not found", //
369 content = @Content(schema = @Schema(implementation = String.class)))})
370 public ResponseEntity<String> getPolicyIds( //
371 @Parameter(name = "type", required = false,
372 description = "The name of the policy type to get policies for.") //
373 @RequestParam(name = "type", required = false) String type, //
374 @Parameter(name = "ric", required = false, description = "The name of the Near-RT RIC to get policies for.") //
375 @RequestParam(name = "ric", required = false) String ric, //
376 @Parameter(name = "service", required = false, description = "The name of the service to get policies for.") //
377 @RequestParam(name = "service", required = false) String service) //
379 if ((type != null && this.policyTypes.get(type) == null)) {
380 return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND);
382 if ((ric != null && this.rics.get(ric) == null)) {
383 return new ResponseEntity<>("Near-RT RIC not found", HttpStatus.NOT_FOUND);
386 String policyIdsJson = toPolicyIdsJson(filter(type, ric, service));
387 return new ResponseEntity<>(policyIdsJson, HttpStatus.OK);
390 @GetMapping("/policy_status")
391 @Operation(summary = "Returns a policy status") //
392 @ApiResponses(value = { //
393 @ApiResponse(responseCode = "200", //
394 description = "Policy status", //
395 content = @Content(schema = @Schema(implementation = Object.class))), //
396 @ApiResponse(responseCode = "404", //
397 description = "Policy is not found", //
398 content = @Content(schema = @Schema(implementation = String.class))) //
400 public Mono<ResponseEntity<String>> getPolicyStatus( //
401 @Parameter(name = "id", required = true, description = "The identity of the policy.") @RequestParam(
403 required = true) String id)
404 throws EntityNotFoundException {
405 Policy policy = policies.getPolicy(id);
407 return a1ClientFactory.createA1Client(policy.getRic()) //
408 .flatMap(client -> client.getPolicyStatus(policy)) //
409 .flatMap(status -> Mono.just(new ResponseEntity<>(status, HttpStatus.OK)))
410 .onErrorResume(this::handleException);
413 private void keepServiceAlive(String name) {
414 Service s = this.services.get(name);
420 private boolean include(String filter, String value) {
421 return filter == null || value.equals(filter);
424 private Collection<Policy> filter(Collection<Policy> collection, String type, String ric, String service) {
425 if (type == null && ric == null && service == null) {
428 List<Policy> filtered = new ArrayList<>();
429 for (Policy p : collection) {
430 if (include(type, p.getType().getId()) && include(ric, p.getRic().id())
431 && include(service, p.getOwnerServiceId())) {
438 private Collection<Policy> filter(String type, String ric, String service) {
440 return filter(policies.getForType(type), null, ric, service);
441 } else if (service != null) {
442 return filter(policies.getForService(service), type, ric, null);
443 } else if (ric != null) {
444 return filter(policies.getForRic(ric), type, null, service);
446 return policies.getAll();
450 private String policiesToJson(Collection<Policy> policies) {
451 List<PolicyInfo> v = new ArrayList<>(policies.size());
452 for (Policy p : policies) {
453 PolicyInfo policyInfo = new PolicyInfo();
454 policyInfo.id = p.getId();
455 policyInfo.json = fromJson(p.getJson());
456 policyInfo.ric = p.getRic().id();
457 policyInfo.type = p.getType().getId();
458 policyInfo.service = p.getOwnerServiceId();
459 policyInfo.lastModified = p.getLastModified().toString();
460 if (!policyInfo.validate()) {
461 logger.error("BUG, all fields must be set");
465 return gson.toJson(v);
468 private Object fromJson(String jsonStr) {
469 return gson.fromJson(jsonStr, Object.class);
472 private String toPolicyTypeSchemasJson(Collection<PolicyType> types) {
473 StringBuilder result = new StringBuilder();
475 boolean first = true;
476 for (PolicyType t : types) {
481 result.append(t.getSchema());
484 return result.toString();
487 private String toPolicyTypeIdsJson(Collection<PolicyType> types) {
488 List<String> v = new ArrayList<>(types.size());
489 for (PolicyType t : types) {
492 return gson.toJson(v);
495 private String toPolicyIdsJson(Collection<Policy> policies) {
496 List<String> v = new ArrayList<>(policies.size());
497 for (Policy p : policies) {
500 return gson.toJson(v);