2 * ========================LICENSE_START=================================
4 * ======================================================================
5 * Copyright (C) 2019-2023 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.v2;
23 import io.swagger.v3.oas.annotations.tags.Tag;
24 import org.onap.ccsdk.oran.a1policymanagementservice.controllers.api.v2.ServiceRegistryAndSupervisionApi;
25 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
26 import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceRegistrationInfo;
27 import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceStatus;
28 import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceStatusList;
29 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies;
30 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy;
31 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Service;
32 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35 import org.springframework.beans.factory.annotation.Autowired;
36 import org.springframework.http.HttpStatus;
37 import org.springframework.http.ResponseEntity;
38 import org.springframework.web.bind.annotation.RestController;
39 import org.springframework.web.server.ServerWebExchange;
40 import reactor.core.publisher.Mono;
42 import java.lang.invoke.MethodHandles;
43 import java.net.MalformedURLException;
45 import java.time.Duration;
46 import java.util.ArrayList;
47 import java.util.Collection;
48 import java.util.List;
50 @RestController("ServiceControllerV2")
52 name = ServiceController.API_NAME, //
53 description = ServiceController.API_DESCRIPTION //
56 public class ServiceController implements ServiceRegistryAndSupervisionApi {
58 public static final String API_NAME = "Service Registry and Supervision";
59 public static final String API_DESCRIPTION = "";
61 private final Services services;
62 private final Policies policies;
64 private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
67 private PolicyController policyController;
69 ServiceController(Services services, Policies policies) {
70 this.services = services;
71 this.policies = policies;
74 private static final String GET_SERVICE_DETAILS =
75 "Either information about a registered service with given identity or all registered services are returned.";
78 public Mono<ResponseEntity<ServiceStatusList>> getServices(final String name, final ServerWebExchange exchange) throws Exception {
79 if (name != null && this.services.get(name) == null) {
80 throw new ServiceException("Service not found", HttpStatus.NOT_FOUND);
83 List<ServiceStatus> servicesStatus = new ArrayList<>();
84 for (Service s : this.services.getAll()) {
85 if (name == null || name.equals(s.getName())) {
86 servicesStatus.add(toServiceStatus(s));
89 return Mono.just(new ResponseEntity<>(new ServiceStatusList().serviceList(servicesStatus), HttpStatus.OK));
92 private ServiceStatus toServiceStatus(Service s) {
93 return new ServiceStatus()
94 .serviceId(s.getName())
95 .keepAliveIntervalSeconds(s.getKeepAliveInterval().toSeconds())
96 .timeSinceLastActivitySeconds(s.timeSinceLastPing().toSeconds())
97 .callbackUrl(s.getCallbackUrl());
100 private void validateRegistrationInfo(ServiceRegistrationInfo registrationInfo)
101 throws ServiceException, MalformedURLException {
102 if (registrationInfo.getServiceId().isEmpty()) {
103 throw new ServiceException("Missing mandatory parameter 'service-id'");
105 if (registrationInfo.getKeepAliveIntervalSeconds() < 0) {
106 throw new ServiceException("Keep alive interval should be greater or equal to 0");
108 if (!registrationInfo.getCallbackUrl().isEmpty()) {
109 new URL(registrationInfo.getCallbackUrl());
113 private static final String REGISTER_SERVICE_DETAILS = "Registering a service is needed to:" //
115 + "<li>Get callbacks about available NearRT RICs.</li>" //
116 + "<li>Activate supervision of the service. If a service is inactive, its policies will automatically be deleted.</li>"//
118 + "Policies can be created even if the service is not registerred. This is a feature which it is optional to use.";
121 public Mono<ResponseEntity<Object>> putService(
122 final Mono<ServiceRegistrationInfo> registrationInfo, final ServerWebExchange exchange) {
123 return registrationInfo.flatMap(info -> {
125 validateRegistrationInfo(info);
126 } catch(Exception e) {
127 return ErrorResponse.createMono(e, HttpStatus.BAD_REQUEST);
129 final boolean isCreate = this.services.get(info.getServiceId()) == null;
130 this.services.put(toService(info));
131 return Mono.just(new ResponseEntity<>(isCreate ? HttpStatus.CREATED : HttpStatus.OK));
132 }).onErrorResume(Exception.class, e -> ErrorResponse.createMono(e, HttpStatus.BAD_REQUEST));
136 public Mono<ResponseEntity<Object>> deleteService(final String serviceId, final ServerWebExchange exchange) {
138 Service service = removeService(serviceId);
139 removePolicies(service, exchange);
140 return Mono.just(new ResponseEntity<>(HttpStatus.NO_CONTENT));
141 } catch (ServiceException | NullPointerException e) {
142 logger.warn("Exception caught during service deletion while deleting service {}: {}", serviceId, e.getMessage());
143 return ErrorResponse.createMono(e, HttpStatus.NOT_FOUND);
148 public Mono<ResponseEntity<Object>> keepAliveService(final String serviceId, final ServerWebExchange exchange) throws ServiceException {
149 services.getService(serviceId).keepAlive();
150 return Mono.just(new ResponseEntity<>(HttpStatus.OK));
153 private Service removeService(String name) throws ServiceException {
154 Service service = this.services.getService(name); // Just to verify that it exists
155 logger.trace("Service name to be deleted: {}", service.getName());
156 this.services.remove(service.getName());
160 private void removePolicies(Service service, ServerWebExchange exchange) {
161 Collection<Policy> policyList = this.policies.getForService(service.getName());
162 logger.trace("Policies to be deleted: {}", policyList);
163 for (Policy policy : policyList) {
165 policyController.deletePolicy(policy.getId(), exchange).doOnNext(resp -> {
166 if (resp.getStatusCode().is2xxSuccessful()) {
167 logger.trace("Deleting Policy '{}' when deleting Service '{}'", policy.getId(),
170 logger.warn("Possible problem deleting Policy '{}' when deleting Service '{}'. Continuing, "
171 + "but might trigger a re-sync with affected ric '{}'. Repsonse: \"{}\"",
172 policy.getId(), service.getName(), policy.getRic().getConfig().getRicId(),
176 } catch (Exception e) {
177 logger.warn("Problem deleting Policy '{}' when deleting Service '{}'."
178 + " Continuing, but might trigger a re-sync with affected ric '{}'. Problem: \"{}\"",
179 policy.getId(), service.getName(), policy.getRic().getConfig().getRicId(), e.getMessage());
184 private Service toService(ServiceRegistrationInfo s) {
185 return new Service(s.getServiceId(), Duration.ofSeconds(s.getKeepAliveIntervalSeconds()), s.getCallbackUrl());