03fa97d1106142b72e8b022d94f00e8fbc144656
[nonrtric.git] / policy-agent / src / main / java / org / oransc / policyagent / clients / SdncOscA1Client.java
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2020 Nordix Foundation
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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===================================
19  */
20
21 package org.oransc.policyagent.clients;
22
23 import com.google.gson.FieldNamingPolicy;
24 import com.google.gson.GsonBuilder;
25
26 import java.lang.invoke.MethodHandles;
27 import java.nio.charset.StandardCharsets;
28 import java.util.Arrays;
29 import java.util.List;
30 import java.util.Optional;
31
32 import org.immutables.value.Value;
33 import org.oransc.policyagent.configuration.RicConfig;
34 import org.oransc.policyagent.repository.Policy;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import org.springframework.http.HttpStatus;
38 import org.springframework.web.reactive.function.client.WebClientResponseException;
39
40 import reactor.core.publisher.Flux;
41 import reactor.core.publisher.Mono;
42
43 /**
44  * Client for accessing the A1 adapter in the SDNC controller in OSC.
45  */
46 @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
47 public class SdncOscA1Client implements A1Client {
48
49     @Value.Immutable
50     @org.immutables.gson.Gson.TypeAdapters
51     public interface AdapterRequest {
52         public String nearRtRicUrl();
53
54         public Optional<String> body();
55     }
56
57     @Value.Immutable
58     @org.immutables.gson.Gson.TypeAdapters
59     public interface AdapterResponse {
60         public String body();
61
62         public int httpStatus();
63     }
64
65     static com.google.gson.Gson gson = new GsonBuilder() //
66         .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) //
67         .create(); //
68
69     private static final String GET_POLICY_RPC = "getA1Policy";
70     private static final String UNHANDELED_PROTOCOL = "Bug, unhandeled protocoltype: ";
71     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
72     private final String a1ControllerUsername;
73     private final String a1ControllerPassword;
74     private final AsyncRestClient restClient;
75     private final RicConfig ricConfig;
76     private final A1ProtocolType protocolType;
77
78     /**
79      * Constructor
80      * 
81      * @param protocolType the southbound protocol of the controller. Supported
82      *        protocols are SDNC_OSC_STD_V1_1 and SDNC_OSC_OSC_V1
83      * @param ricConfig
84      * @param controllerBaseUrl the base URL of the SDNC controller
85      * @param username username to accesss the SDNC controller
86      * @param password password to accesss the SDNC controller
87      */
88     public SdncOscA1Client(A1ProtocolType protocolType, RicConfig ricConfig, String controllerBaseUrl, String username,
89         String password) {
90         this(protocolType, ricConfig, username, password,
91             new AsyncRestClient(controllerBaseUrl + "/restconf/operations"));
92         logger.debug("SdncOscA1Client for ric: {}, a1ControllerBaseUrl: {}", ricConfig.name(), controllerBaseUrl);
93     }
94
95     public SdncOscA1Client(A1ProtocolType protocolType, RicConfig ricConfig, String username, String password,
96         AsyncRestClient restClient) {
97         this.a1ControllerUsername = username;
98         this.a1ControllerPassword = password;
99         this.restClient = restClient;
100         this.ricConfig = ricConfig;
101         this.protocolType = protocolType;
102
103     }
104
105     @Override
106     public Mono<List<String>> getPolicyTypeIdentities() {
107         if (this.protocolType == A1ProtocolType.SDNC_OSC_STD_V1_1) {
108             return Mono.just(Arrays.asList(""));
109         } else if (this.protocolType == A1ProtocolType.SDNC_OSC_OSC_V1) {
110             OscA1Client.UriBuilder uri = new OscA1Client.UriBuilder(ricConfig);
111             final String ricUrl = uri.createPolicyTypesUri();
112             return post(GET_POLICY_RPC, ricUrl, Optional.empty()) //
113                 .flatMapMany(SdncJsonHelper::parseJsonArrayOfString) //
114                 .collectList();
115         }
116         throw new NullPointerException(UNHANDELED_PROTOCOL + this.protocolType);
117     }
118
119     @Override
120     public Mono<List<String>> getPolicyIdentities() {
121         return getPolicyIds() //
122             .collectList();
123     }
124
125     @Override
126     public Mono<String> getPolicyTypeSchema(String policyTypeId) {
127         if (this.protocolType == A1ProtocolType.SDNC_OSC_STD_V1_1) {
128             return Mono.just("{}");
129         } else if (this.protocolType == A1ProtocolType.SDNC_OSC_OSC_V1) {
130             OscA1Client.UriBuilder uri = new OscA1Client.UriBuilder(ricConfig);
131             final String ricUrl = uri.createGetSchemaUri(policyTypeId);
132             return post(GET_POLICY_RPC, ricUrl, Optional.empty());
133         }
134         throw new NullPointerException(UNHANDELED_PROTOCOL + this.protocolType);
135     }
136
137     @Override
138     public Mono<String> putPolicy(Policy policy) {
139         final String ricUrl = getUriBuilder().createPutPolicyUri(policy.type().name(), policy.id());
140         return post("putA1Policy", ricUrl, Optional.of(policy.json()));
141     }
142
143     @Override
144     public Mono<String> deletePolicy(Policy policy) {
145         return deletePolicyById(policy.type().name(), policy.id());
146     }
147
148     @Override
149     public Flux<String> deleteAllPolicies() {
150         if (this.protocolType == A1ProtocolType.SDNC_OSC_STD_V1_1) {
151             return getPolicyIds() //
152                 .flatMap(policyId -> deletePolicyById("", policyId)); //
153         } else if (this.protocolType == A1ProtocolType.SDNC_OSC_OSC_V1) {
154             OscA1Client.UriBuilder uriBuilder = new OscA1Client.UriBuilder(ricConfig);
155             return getPolicyTypeIdentities() //
156                 .flatMapMany(Flux::fromIterable)
157                 .flatMap(type -> post(GET_POLICY_RPC, uriBuilder.createGetPolicyIdsUri(type), Optional.empty())) //
158                 .flatMap(SdncJsonHelper::parseJsonArrayOfString);
159         }
160         throw new NullPointerException(UNHANDELED_PROTOCOL + this.protocolType);
161     }
162
163     @Override
164     public Mono<A1ProtocolType> getProtocolVersion() {
165         return tryStdProtocolVersion() //
166             .onErrorResume(t -> tryOscProtocolVersion());
167     }
168
169     @Override
170     public Mono<String> getPolicyStatus(Policy policy) {
171         final String ricUrl = getUriBuilder().createGetPolicyStatusUri(policy.type().name(), policy.id());
172         return post("getA1PolicyStatus", ricUrl, Optional.empty());
173     }
174
175     private A1UriBuilder getUriBuilder() {
176         if (protocolType == A1ProtocolType.SDNC_OSC_STD_V1_1) {
177             return new StdA1ClientVersion1.UriBuilder(ricConfig);
178         } else if (this.protocolType == A1ProtocolType.SDNC_OSC_OSC_V1) {
179             return new OscA1Client.UriBuilder(ricConfig);
180         }
181         throw new NullPointerException(UNHANDELED_PROTOCOL + this.protocolType);
182     }
183
184     private Mono<A1ProtocolType> tryOscProtocolVersion() {
185         OscA1Client.UriBuilder oscApiuriBuilder = new OscA1Client.UriBuilder(ricConfig);
186         return post(GET_POLICY_RPC, oscApiuriBuilder.createHealtcheckUri(), Optional.empty()) //
187             .flatMap(x -> Mono.just(A1ProtocolType.SDNC_OSC_OSC_V1));
188     }
189
190     private Mono<A1ProtocolType> tryStdProtocolVersion() {
191         StdA1ClientVersion1.UriBuilder uriBuilder = new StdA1ClientVersion1.UriBuilder(ricConfig);
192         return post(GET_POLICY_RPC, uriBuilder.createGetPolicyIdsUri(), Optional.empty()) //
193             .flatMap(x -> Mono.just(A1ProtocolType.SDNC_OSC_STD_V1_1));
194     }
195
196     private Flux<String> getPolicyIds() {
197         if (this.protocolType == A1ProtocolType.SDNC_OSC_STD_V1_1) {
198             StdA1ClientVersion1.UriBuilder uri = new StdA1ClientVersion1.UriBuilder(ricConfig);
199             final String ricUrl = uri.createGetPolicyIdsUri();
200             return post(GET_POLICY_RPC, ricUrl, Optional.empty()) //
201                 .flatMapMany(SdncJsonHelper::parseJsonArrayOfString);
202         } else if (this.protocolType == A1ProtocolType.SDNC_OSC_OSC_V1) {
203             OscA1Client.UriBuilder uri = new OscA1Client.UriBuilder(ricConfig);
204             return getPolicyTypeIdentities() //
205                 .flatMapMany(Flux::fromIterable)
206                 .flatMap(type -> post(GET_POLICY_RPC, uri.createGetPolicyIdsUri(type), Optional.empty())) //
207                 .flatMap(SdncJsonHelper::parseJsonArrayOfString);
208         }
209         throw new NullPointerException(UNHANDELED_PROTOCOL + this.protocolType);
210     }
211
212     private Mono<String> deletePolicyById(String type, String policyId) {
213         final String ricUrl = getUriBuilder().createDeleteUri(type, policyId);
214         return post("deleteA1Policy", ricUrl, Optional.empty());
215     }
216
217     private Mono<String> post(String rpcName, String ricUrl, Optional<String> body) {
218         AdapterRequest inputParams = ImmutableAdapterRequest.builder() //
219             .nearRtRicUrl(ricUrl) //
220             .body(body) //
221             .build();
222         final String inputJsonString = SdncJsonHelper.createInputJsonString(inputParams);
223
224         return restClient
225             .postWithAuthHeader(controllerUrl(rpcName), inputJsonString, a1ControllerUsername, a1ControllerPassword)
226             .flatMap(this::extractResponseBody);
227     }
228
229     private Mono<String> extractResponseBody(String response) {
230         AdapterResponse output = gson.fromJson(response, ImmutableAdapterResponse.class);
231         String body = output.body();
232         if (HttpStatus.valueOf(output.httpStatus()).is2xxSuccessful()) {
233             return Mono.just(body);
234         }
235         byte[] responseBodyBytes = body.getBytes(StandardCharsets.UTF_8);
236         WebClientResponseException e = new WebClientResponseException(output.httpStatus(), "statusText", null,
237             responseBodyBytes, StandardCharsets.UTF_8, null);
238
239         return Mono.error(e);
240     }
241
242     private String controllerUrl(String rpcName) {
243         return "/A1-ADAPTER-API:" + rpcName;
244     }
245 }