Merge "Fix code error simulator"
[nonrtric.git] / policy-agent / src / main / java / org / oransc / policyagent / controllers / PolicyController.java
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2019 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.controllers;
22
23 import com.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
25 import io.swagger.annotations.Api;
26 import io.swagger.annotations.ApiOperation;
27 import io.swagger.annotations.ApiResponse;
28 import io.swagger.annotations.ApiResponses;
29
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.List;
33 import org.oransc.policyagent.clients.A1ClientFactory;
34 import org.oransc.policyagent.configuration.ApplicationConfig;
35 import org.oransc.policyagent.exceptions.ServiceException;
36 import org.oransc.policyagent.repository.ImmutablePolicy;
37 import org.oransc.policyagent.repository.Policies;
38 import org.oransc.policyagent.repository.Policy;
39 import org.oransc.policyagent.repository.PolicyType;
40 import org.oransc.policyagent.repository.PolicyTypes;
41 import org.oransc.policyagent.repository.Ric;
42 import org.oransc.policyagent.repository.Rics;
43 import org.springframework.beans.factory.annotation.Autowired;
44 import org.springframework.http.HttpStatus;
45 import org.springframework.http.ResponseEntity;
46 import org.springframework.web.bind.annotation.DeleteMapping;
47 import org.springframework.web.bind.annotation.GetMapping;
48 import org.springframework.web.bind.annotation.PutMapping;
49 import org.springframework.web.bind.annotation.RequestBody;
50 import org.springframework.web.bind.annotation.RequestParam;
51 import org.springframework.web.bind.annotation.RestController;
52 import reactor.core.publisher.Mono;
53
54 @RestController
55 @Api(value = "Policy Management API")
56 public class PolicyController {
57
58     private final Rics rics;
59     private final PolicyTypes policyTypes;
60     private final Policies policies;
61     private final A1ClientFactory a1ClientFactory;
62
63     private static Gson gson = new GsonBuilder() //
64         .serializeNulls() //
65         .create(); //
66
67     @Autowired
68     PolicyController(ApplicationConfig config, PolicyTypes types, Policies policies, Rics rics,
69         A1ClientFactory a1ClientFactory) {
70         this.policyTypes = types;
71         this.policies = policies;
72         this.rics = rics;
73         this.a1ClientFactory = a1ClientFactory;
74     }
75
76     @GetMapping("/policy_schemas")
77     @ApiOperation(value = "Returns policy type schema definitions")
78     @ApiResponses(
79         value = {
80             @ApiResponse(code = 200, message = "Policy schemas", response = Object.class, responseContainer = "List")})
81     public ResponseEntity<String> getPolicySchemas(@RequestParam(name = "ric", required = false) String ricName) {
82         synchronized (this.policyTypes) {
83             if (ricName == null) {
84                 Collection<PolicyType> types = this.policyTypes.getAll();
85                 return new ResponseEntity<>(toPolicyTypeSchemasJson(types), HttpStatus.OK);
86             } else {
87                 try {
88                     Collection<PolicyType> types = rics.getRic(ricName).getSupportedPolicyTypes();
89                     return new ResponseEntity<>(toPolicyTypeSchemasJson(types), HttpStatus.OK);
90                 } catch (ServiceException e) {
91                     return new ResponseEntity<>(e.toString(), HttpStatus.NOT_FOUND);
92                 }
93             }
94         }
95     }
96
97     @GetMapping("/policy_schema")
98     @ApiOperation(value = "Returns one policy type schema definition")
99     @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy schema", response = Object.class)})
100     public ResponseEntity<String> getPolicySchema(@RequestParam(name = "id", required = true) String id) {
101         try {
102             PolicyType type = policyTypes.getType(id);
103             return new ResponseEntity<>(type.schema(), HttpStatus.OK);
104         } catch (ServiceException e) {
105             return new ResponseEntity<>(e.toString(), HttpStatus.NOT_FOUND);
106         }
107     }
108
109     @GetMapping("/policy_types")
110     @ApiOperation(value = "Query policy type names")
111     @ApiResponses(
112         value = {@ApiResponse(
113             code = 200,
114             message = "Policy type names",
115             response = String.class,
116             responseContainer = "List")})
117     public ResponseEntity<String> getPolicyTypes(@RequestParam(name = "ric", required = false) String ricName) {
118         synchronized (this.policyTypes) {
119             if (ricName == null) {
120                 Collection<PolicyType> types = this.policyTypes.getAll();
121                 return new ResponseEntity<>(toPolicyTypeIdsJson(types), HttpStatus.OK);
122             } else {
123                 try {
124                     Collection<PolicyType> types = rics.getRic(ricName).getSupportedPolicyTypes();
125                     return new ResponseEntity<>(toPolicyTypeIdsJson(types), HttpStatus.OK);
126                 } catch (ServiceException e) {
127                     return new ResponseEntity<>(e.toString(), HttpStatus.NOT_FOUND);
128                 }
129             }
130         }
131     }
132
133     @GetMapping("/policy")
134     @ApiOperation(value = "Returns a policy configuration") //
135     @ApiResponses(
136         value = { //
137             @ApiResponse(code = 200, message = "Policy found", response = Object.class), //
138             @ApiResponse(code = 204, message = "Policy is not found")} //
139     )
140     public ResponseEntity<String> getPolicy( //
141         @RequestParam(name = "instance", required = true) String instance) {
142         try {
143             Policy p = policies.getPolicy(instance);
144             return new ResponseEntity<>(p.json(), HttpStatus.OK);
145         } catch (ServiceException e) {
146             return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
147         }
148     }
149
150     @DeleteMapping("/policy")
151     @ApiOperation(value = "Delete a policy", response = Object.class)
152     @ApiResponses(value = {@ApiResponse(code = 204, message = "Policy deleted", response = Object.class)})
153     public Mono<ResponseEntity<Void>> deletePolicy( //
154         @RequestParam(name = "instance", required = true) String id) {
155         Policy policy = policies.get(id);
156         if (policy != null && policy.ric().getState() == Ric.RicState.IDLE) {
157             policies.remove(policy);
158             return a1ClientFactory.createA1Client(policy.ric()) //
159                 .flatMap(client -> client.deletePolicy(policy)) //
160                 .flatMap(notUsed -> Mono.just(new ResponseEntity<>(HttpStatus.NO_CONTENT)));
161         } else {
162             return Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND));
163         }
164     }
165
166     @PutMapping(path = "/policy")
167     @ApiOperation(value = "Put a policy", response = String.class)
168     @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy created or updated")})
169     public Mono<ResponseEntity<String>> putPolicy( //
170         @RequestParam(name = "type", required = true) String typeName, //
171         @RequestParam(name = "instance", required = true) String instanceId, //
172         @RequestParam(name = "ric", required = true) String ricName, //
173         @RequestParam(name = "service", required = true) String service, //
174         @RequestBody Object jsonBody) {
175
176         String jsonString = gson.toJson(jsonBody);
177
178         Ric ric = rics.get(ricName);
179         PolicyType type = policyTypes.get(typeName);
180         if (ric != null && type != null && ric.getState() == Ric.RicState.IDLE) {
181             Policy policy = ImmutablePolicy.builder() //
182                 .id(instanceId) //
183                 .json(jsonString) //
184                 .type(type) //
185                 .ric(ric) //
186                 .ownerServiceName(service) //
187                 .lastModified(getTimeStampUtc()) //
188                 .build();
189
190             return validateModifiedPolicy(policy) //
191                 .flatMap(x -> a1ClientFactory.createA1Client(ric)) //
192                 .flatMap(client -> client.putPolicy(policy)) //
193                 .doOnNext(notUsed -> policies.put(policy)) //
194                 .flatMap(notUsed -> Mono.just(new ResponseEntity<>(HttpStatus.OK)));
195         }
196         return Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND));
197     }
198
199     private Mono<Object> validateModifiedPolicy(Policy policy) {
200         // Check that ric is not updated
201         Policy current = this.policies.get(policy.id());
202         if (current != null) {
203             if (!current.ric().name().equals(policy.ric().name())) {
204                 return Mono.error(new Exception("Policy cannot change RIC or service"));
205             }
206         }
207         return Mono.just("OK");
208     }
209
210     @GetMapping("/policies")
211     @ApiOperation(value = "Query policies")
212     @ApiResponses(
213         value = {
214             @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List")})
215     public ResponseEntity<String> getPolicies( //
216         @RequestParam(name = "type", required = false) String type, //
217         @RequestParam(name = "ric", required = false) String ric, //
218         @RequestParam(name = "service", required = false) String service) //
219     {
220         synchronized (policies) {
221             Collection<Policy> result = null;
222
223             if (type != null) {
224                 result = policies.getForType(type);
225                 result = filter(result, null, ric, service);
226             } else if (service != null) {
227                 result = policies.getForService(service);
228                 result = filter(result, type, ric, null);
229             } else if (ric != null) {
230                 result = filter(policies.getForRic(ric), type, null, service);
231             } else {
232                 result = policies.getAll();
233             }
234
235             String policiesJson;
236             try {
237                 policiesJson = policiesToJson(result);
238             } catch (ServiceException e) {
239                 return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
240             }
241             return new ResponseEntity<>(policiesJson, HttpStatus.OK);
242         }
243     }
244
245     @GetMapping("/policy_status")
246     @ApiOperation(value = "Returns a policy status") //
247     @ApiResponses(
248         value = { //
249             @ApiResponse(code = 200, message = "Policy status", response = Object.class), //
250             @ApiResponse(code = 204, message = "Policy is not found", response = String.class)} //
251     )
252     public Mono<ResponseEntity<String>> getPolicyStatus( //
253         @RequestParam(name = "instance", required = true) String instance) {
254         try {
255             Policy policy = policies.getPolicy(instance);
256
257             return a1ClientFactory.createA1Client(policy.ric()) //
258                 .flatMap(client -> client.getPolicyStatus(policy)) //
259                 .flatMap(status -> Mono.just(new ResponseEntity<>(status, HttpStatus.OK)));
260         } catch (ServiceException e) {
261             return Mono.just(new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT));
262         }
263     }
264
265     private boolean include(String filter, String value) {
266         return filter == null || value.equals(filter);
267     }
268
269     private Collection<Policy> filter(Collection<Policy> collection, String type, String ric, String service) {
270         if (type == null && ric == null && service == null) {
271             return collection;
272         }
273         List<Policy> filtered = new ArrayList<>();
274         for (Policy p : collection) {
275             if (include(type, p.type().name()) && include(ric, p.ric().name())
276                 && include(service, p.ownerServiceName())) {
277                 filtered.add(p);
278             }
279         }
280         return filtered;
281     }
282
283     private String policiesToJson(Collection<Policy> policies) throws ServiceException {
284         List<PolicyInfo> v = new ArrayList<>(policies.size());
285         for (Policy p : policies) {
286             PolicyInfo policyInfo = new PolicyInfo();
287             policyInfo.id = p.id();
288             policyInfo.json = fromJson(p.json());
289             policyInfo.ric = p.ric().name();
290             policyInfo.type = p.type().name();
291             policyInfo.service = p.ownerServiceName();
292             policyInfo.lastModified = p.lastModified();
293             if (!policyInfo.validate()) {
294                 throw new ServiceException("BUG, all fields must be set");
295             }
296             v.add(policyInfo);
297         }
298         return gson.toJson(v);
299     }
300
301     private Object fromJson(String jsonStr) {
302         return gson.fromJson(jsonStr, Object.class);
303     }
304
305     private String toPolicyTypeSchemasJson(Collection<PolicyType> types) {
306         StringBuilder result = new StringBuilder();
307         result.append("[");
308         boolean first = true;
309         for (PolicyType t : types) {
310             if (!first) {
311                 result.append(",");
312             }
313             first = false;
314             result.append(t.schema());
315         }
316         result.append("]");
317         return result.toString();
318     }
319
320     private String toPolicyTypeIdsJson(Collection<PolicyType> types) {
321         List<String> v = new ArrayList<>(types.size());
322         for (PolicyType t : types) {
323             v.add(t.name());
324         }
325         return gson.toJson(v);
326     }
327
328     private String getTimeStampUtc() {
329         return java.time.Instant.now().toString();
330     }
331
332 }