support for PAP delta updates
[policy/drools-pdp.git] / feature-lifecycle / src / main / java / org / onap / policy / drools / server / restful / RestLifecycleManager.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
4  * Modifications Copyright (C) 2021 Nordix Foundation.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  * ============LICENSE_END=========================================================
18  */
19
20 package org.onap.policy.drools.server.restful;
21
22 import com.worldturner.medeia.api.ValidationFailedException;
23 import io.swagger.annotations.Api;
24 import io.swagger.annotations.ApiOperation;
25 import io.swagger.annotations.ApiParam;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Properties;
29 import javax.ws.rs.Consumes;
30 import javax.ws.rs.DELETE;
31 import javax.ws.rs.GET;
32 import javax.ws.rs.POST;
33 import javax.ws.rs.PUT;
34 import javax.ws.rs.Path;
35 import javax.ws.rs.PathParam;
36 import javax.ws.rs.Produces;
37 import javax.ws.rs.core.MediaType;
38 import javax.ws.rs.core.Response;
39 import org.onap.policy.common.endpoints.event.comm.TopicSink;
40 import org.onap.policy.common.endpoints.event.comm.TopicSource;
41 import org.onap.policy.common.endpoints.http.server.YamlMessageBodyHandler;
42 import org.onap.policy.common.utils.coder.CoderException;
43 import org.onap.policy.common.utils.coder.StandardCoder;
44 import org.onap.policy.drools.lifecycle.LifecycleFeature;
45 import org.onap.policy.drools.lifecycle.PolicyTypeController;
46 import org.onap.policy.models.pdp.concepts.PdpStateChange;
47 import org.onap.policy.models.pdp.concepts.PdpStatistics;
48 import org.onap.policy.models.pdp.concepts.PdpUpdate;
49 import org.onap.policy.models.pdp.enums.PdpState;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
51 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * REST Lifecycle Manager.
57  */
58
59 @Path("/policy/pdp/engine/lifecycle")
60 @Produces({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
61 @Consumes({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
62 @Api
63 public class RestLifecycleManager {
64
65     private static final Logger logger = LoggerFactory.getLogger(RestLifecycleManager.class);
66
67     private static final StandardCoder coder = new StandardCoder();
68
69     /**
70      * GET group.
71      */
72
73     @GET
74     @Path("group")
75     @ApiOperation(value = "Retrieves the Lifecycle group",
76         notes = "Lifecycle Group", response = String.class)
77     public Response group() {
78         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().getGroup()).build();
79     }
80
81     /**
82      * PUT group.
83      */
84
85     @PUT
86     @Path("group/{group}")
87     @ApiOperation(value = "Updates the Lifecycle group",
88             notes = "Lifecycle Group", response = String.class)
89     public Response updateGroup(
90         @ApiParam(value = "Group", required = true) @PathParam("group") String group) {
91         LifecycleFeature.getFsm().setGroup(group);
92         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().getGroup()).build();
93     }
94
95     /**
96      * GET subgroup.
97      */
98
99     @GET
100     @Path("subgroup")
101     @ApiOperation(value = "Retrieves the Lifecycle subgroup",
102             notes = "Lifecycle Subgroup", response = String.class)
103     public Response subgroup() {
104         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().getSubGroup()).build();
105     }
106
107     /**
108      * PUT subgroup.
109      */
110
111     @PUT
112     @Path("subgroup/{subgroup}")
113     @ApiOperation(value = "Retrieves the Lifecycle subgroup",
114             notes = "Lifecycle Subgroup", response = String.class)
115     public Response subgroup(
116         @ApiParam(value = "Subgroup", required = true) @PathParam("subgroup") String subgroup) {
117         LifecycleFeature.getFsm().setSubGroup(subgroup);
118         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().getSubGroup()).build();
119     }
120
121     /**
122      * GET properties.
123      */
124
125     @GET
126     @Path("properties")
127     @ApiOperation(value = "Retrieves the Lifecycle properties",
128             notes = "Lifecycle Properties", response = Properties.class)
129     public Response properties() {
130         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().getProperties()).build();
131     }
132
133     /**
134      * GET state.
135      */
136
137     @GET
138     @Path("state")
139     @ApiOperation(value = "Retrieves the Lifecycle state", notes = "Lifecycle State", response = PdpState.class)
140     public Response state() {
141         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().state()).build();
142     }
143
144     /**
145      * PUT state.
146      */
147
148     @PUT
149     @Path("state/{state}")
150     @ApiOperation(value = "updates the Lifecycle state", notes = "Lifecycle State", response = Boolean.class)
151     public Response updateState(
152         @ApiParam(value = "state", required = true) @PathParam("state") String state) {
153
154         PdpStateChange change = new PdpStateChange();
155         change.setPdpGroup(LifecycleFeature.getFsm().getGroup());
156         change.setPdpSubgroup(LifecycleFeature.getFsm().getSubGroup());
157         change.setState(PdpState.valueOf(state));
158         change.setName(LifecycleFeature.getFsm().getName());
159
160         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().stateChange(change)).build();
161     }
162
163     /**
164      * GET topic source.
165      */
166
167     @GET
168     @Path("topic/source")
169     @ApiOperation(value = "Retrieves the Lifecycle topic source",
170             notes = "Lifecycle Topic Source", response = TopicSource.class)
171     public Response source() {
172         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().getSource()).build();
173     }
174
175     /**
176      * GET topic sink.
177      */
178
179     @GET
180     @Path("topic/sink")
181     @ApiOperation(value = "Retrieves the Lifecycle topic sink",
182             notes = "Lifecycle Topic Sink", response = TopicSink.class)
183     public Response sink() {
184         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().getClient()).build();
185     }
186
187     /**
188      * GET status interval.
189      */
190
191     @GET
192     @Path("status/interval")
193     @ApiOperation(value = "Retrieves the Lifecycle Status Timer Interval in seconds",
194             notes = "Lifecycle Status Timer Interval in seconds", response = Long.class)
195     public Response updateStatusTimer() {
196         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().getStatusTimerSeconds()).build();
197     }
198
199     /**
200      * PUT timeout.
201      */
202
203     @PUT
204     @Path("status/interval/{timeout}")
205     @ApiOperation(value = "Updates the Lifecycle Status Timer Interval in seconds",
206             notes = "Lifecycle Status Timer Interval in seconds", response = Long.class)
207     public Response statusTimer(
208             @ApiParam(value = "timeout", required = true) @PathParam("timeout") Long timeout) {
209         LifecycleFeature.getFsm().setStatusTimerSeconds(timeout);
210         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().getStatusTimerSeconds()).build();
211     }
212
213     /**
214      * GET policy types.
215      */
216
217     @GET
218     @Path("policyTypes")
219     @ApiOperation(value = "List of supported policy types",
220             notes = "Lifecycle Policy Types", responseContainer = "List")
221     public Response policyTypes() {
222         return Response.status(Response.Status.OK)
223                        .entity(LifecycleFeature.getFsm().getPolicyTypesMap().keySet())
224                        .build();
225     }
226
227     /**
228      * GET controllers.
229      */
230
231     @GET
232     @Path("policyTypes/{policyType}/{policyTypeVersion}")
233     @ApiOperation(value = "Entities associated with a policy type",
234             notes = "Lifecycle policy Types", response = PolicyTypeController.class)
235     public Response policyType(
236         @ApiParam(value = "Policy Type", required = true)
237             @PathParam("policyType") String policyType,
238         @ApiParam(value = "Policy Type Version", required = true)
239             @PathParam("policyTypeVersion") String policyTypeVersion) {
240         PolicyTypeController typeController =
241             LifecycleFeature.getFsm().getPolicyTypesMap()
242                     .get(new ToscaConceptIdentifier(policyType, policyTypeVersion));
243         if (typeController == null) {
244             return Response.status(Response.Status.NOT_FOUND).build();
245         }
246
247         return Response.status(Response.Status.OK)
248                        .entity(typeController)
249                        .build();
250     }
251
252     /**
253      * GET policies.
254      */
255
256     @GET
257     @Path("policies")
258     @ApiOperation(value = "List of policies", responseContainer = "List")
259     public Response policies() {
260         return Response.status(Response.Status.OK)
261                        .entity(LifecycleFeature.getFsm().getPoliciesMap().keySet())
262                        .build();
263
264     }
265
266     /**
267      * POST a Policy.
268      */
269
270     @POST
271     @Path("policies")
272     @ApiOperation(value = "Deploy a policy", response = Boolean.class)
273     public Response deployTrackedPolicy(
274             @ApiParam(value = "Tosca Policy", required = true) String policy) {
275
276         ToscaPolicy toscaPolicy = getToscaPolicy(policy);
277         if (toscaPolicy == null) {
278             return Response.status(Response.Status.NOT_ACCEPTABLE).build();
279         }
280
281         PolicyTypeController typeController = getPolicyTypeController(toscaPolicy);
282         if (typeController == null) {
283             return Response.status(Response.Status.NOT_FOUND).build();
284         }
285
286         boolean updateResult = LifecycleFeature.getFsm().update(getDeployPolicyUpdate(List.of(toscaPolicy)));
287         return Response.status((updateResult ? Response.Status.OK : Response.Status.NOT_ACCEPTABLE))
288                        .entity(updateResult)
289                        .build();
290     }
291
292     /**
293      * GET a policy.
294      */
295
296     @GET
297     @Path("policies/{policyName}/{policyVersion}")
298     @ApiOperation(value = "Retrieves a policy", response = ToscaPolicy.class)
299     public Response policy(
300             @ApiParam(value = "Policy Name", required = true) @PathParam("policyName") String policyName,
301             @ApiParam(value = "Policy Version", required = true) @PathParam("policyVersion") String policyVersion) {
302
303         ToscaPolicy policy;
304         try {
305             policy =
306                 LifecycleFeature.getFsm().getPoliciesMap().get(new ToscaConceptIdentifier(policyName, policyVersion));
307         } catch (RuntimeException r) {
308             logger.debug("policy {}:{} has not been found", policyName, policyVersion, r);
309             return Response.status(Response.Status.NOT_FOUND).build();
310         }
311
312         if (policy == null) {
313             return Response.status(Response.Status.NOT_FOUND).build();
314         }
315
316         return Response.status(Response.Status.OK).entity(policy).build();
317     }
318
319     /**
320      * DELETE a policy.
321      */
322
323     @DELETE
324     @Path("policies/{policyName}/{policyVersion}")
325     @ApiOperation(value = "Deletes a Lifecycle tracked policy", response = Boolean.class)
326     public Response undeployPolicy(
327             @ApiParam(value = "Policy", required = true) @PathParam("policyName") String policyName,
328             @ApiParam(value = "Policy Version", required = true) @PathParam("policyVersion") String policyVersion) {
329
330         ToscaPolicy policy;
331         try {
332             policy =
333                 LifecycleFeature.getFsm().getPoliciesMap().get(new ToscaConceptIdentifier(policyName, policyVersion));
334         } catch (RuntimeException r) {
335             logger.debug("policy {}:{} has not been found", policyName, policyVersion, r);
336             return Response.status(Response.Status.NOT_FOUND).build();
337         }
338
339         if (policy == null) {
340             return Response.status(Response.Status.NOT_FOUND).build();
341         }
342
343         return Response.status(Response.Status.OK)
344                        .entity(LifecycleFeature.getFsm().update(getUndeployPolicyUpdate(List.of(policy))))
345                        .build();
346     }
347
348     /**
349      * List of policies individual Operations supported.
350      */
351
352     @GET
353     @Path("policies/operations")
354     @ApiOperation(value = "Gets Policy Operations", responseContainer = "List")
355     public Response policiesOperations() {
356         return Response.status(Response.Status.OK).entity(List.of("deployment", "undeployment", "validation")).build();
357     }
358
359     /**
360      * POST a deployment operation on a policy.
361      */
362
363     @POST
364     @Path("policies/operations/deployment")
365     @ApiOperation(value = "Deploys a policy", notes = "Deploys a policy", response = Boolean.class)
366     public Response deployOperation(@ApiParam(value = "Tosca Policy", required = true) String policy) {
367         return deployUndeployOperation(policy, true);
368     }
369
370     /**
371      * POST an undeployment operation on a policy.
372      */
373
374     @POST
375     @Path("policies/operations/undeployment")
376     @ApiOperation(value = "Undeploys a policy", response = Boolean.class)
377     public Response undeployOperation(@ApiParam(value = "Tosca Policy", required = true) String policy) {
378         return deployUndeployOperation(policy, false);
379     }
380
381     /**
382      * POST a policy for validation.
383      */
384
385     @POST
386     @Path("policies/operations/validation")
387     @ApiOperation(value = "Validates a policy", responseContainer = "List")
388     public Response validateOperation(@ApiParam(value = "Tosca Policy", required = true) String policy) {
389         ToscaPolicy toscaPolicy = getToscaPolicy(policy);
390         if (toscaPolicy == null) {
391             return Response.status(Response.Status.NOT_ACCEPTABLE).build();
392         }
393
394         try {
395             LifecycleFeature.getFsm().getDomainMaker().conformance(toscaPolicy);
396         } catch (ValidationFailedException v) {
397             logger.trace("policy {} validation errors: {}", toscaPolicy, v.getMessage(), v);
398             return Response.status(Response.Status.NOT_ACCEPTABLE).entity(v.getFailures()).build();
399         }
400
401         return Response.status(Response.Status.OK).entity(Collections.emptyList()).build();
402     }
403
404     /**
405      * Get current counts.
406      */
407
408     @GET
409     @Path("statistics")
410     @ApiOperation(value = "Gets Policy Statistics", response = PdpStatistics.class)
411     public Response stats() {
412         return Response.status(Response.Status.OK).entity(LifecycleFeature.getFsm().statisticsPayload()).build();
413     }
414
415     private Response deployUndeployOperation(String policy, boolean deploy) {
416         ToscaPolicy toscaPolicy = getToscaPolicy(policy);
417         if (toscaPolicy == null) {
418             return Response.status(Response.Status.NOT_ACCEPTABLE).build();
419         }
420
421         PolicyTypeController typeController = getPolicyTypeController(toscaPolicy);
422         if (typeController == null) {
423             return Response.status(Response.Status.NOT_FOUND).build();
424         }
425
426         return Response.status(Response.Status.OK)
427                        .entity((deploy) ? typeController.deploy(toscaPolicy) : typeController.undeploy(toscaPolicy))
428                        .build();
429     }
430
431     private ToscaPolicy getToscaPolicy(String policy) {
432         try {
433             return coder.decode(policy, ToscaPolicy.class);
434         } catch (CoderException | RuntimeException e) {
435             return null;
436         }
437     }
438
439     private PolicyTypeController getPolicyTypeController(ToscaPolicy policy) {
440         return LifecycleFeature.getFsm().getPolicyTypesMap().get(policy.getTypeIdentifier());
441     }
442
443     private PdpUpdate getPolicyUpdate() {
444         PdpUpdate update = new PdpUpdate();
445         update.setName(LifecycleFeature.getFsm().getName());
446         update.setPdpGroup(LifecycleFeature.getFsm().getGroup());
447         update.setPdpSubgroup(LifecycleFeature.getFsm().getSubGroup());
448         return update;
449     }
450
451     private PdpUpdate getDeployPolicyUpdate(List<ToscaPolicy> policies) {
452         PdpUpdate update = getPolicyUpdate();
453         update.setPoliciesToBeDeployed(policies);
454         return update;
455     }
456
457     private PdpUpdate getUndeployPolicyUpdate(List<ToscaPolicy> policies) {
458         PdpUpdate update = getPolicyUpdate();
459         update.setPoliciesToBeUndeployed(LifecycleFeature.fsm.getPolicyIds(policies));
460         return update;
461     }
462
463 }