65cc0393133420fb631b90dc27c37df0099a6408
[policy/models.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2019 Bell Canada. All rights reserved.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  * ============LICENSE_END=========================================================
17  */
18
19 package org.onap.policy.controlloop.actor.cds;
20
21 import com.google.common.base.Preconditions;
22 import com.google.common.base.Strings;
23 import com.google.protobuf.InvalidProtocolBufferException;
24 import com.google.protobuf.Struct;
25 import com.google.protobuf.Struct.Builder;
26 import com.google.protobuf.util.JsonFormat;
27 import io.grpc.Status;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Optional;
32 import java.util.UUID;
33 import java.util.concurrent.CountDownLatch;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.atomic.AtomicReference;
36 import org.onap.ccsdk.cds.controllerblueprints.common.api.ActionIdentifiers;
37 import org.onap.ccsdk.cds.controllerblueprints.common.api.CommonHeader;
38 import org.onap.ccsdk.cds.controllerblueprints.common.api.EventType;
39 import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput;
40 import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceOutput;
41 import org.onap.policy.cds.CdsResponse;
42 import org.onap.policy.cds.api.CdsProcessorListener;
43 import org.onap.policy.cds.client.CdsProcessorGrpcClient;
44 import org.onap.policy.cds.properties.CdsServerProperties;
45 import org.onap.policy.common.utils.coder.CoderException;
46 import org.onap.policy.controlloop.ControlLoopOperation;
47 import org.onap.policy.controlloop.VirtualControlLoopEvent;
48 import org.onap.policy.controlloop.actor.cds.constants.CdsActorConstants;
49 import org.onap.policy.controlloop.actor.cds.request.CdsActionRequest;
50 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
51 import org.onap.policy.controlloop.policy.Policy;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * CDS Actor service-provider implementation. This is a deploy dark feature for El-Alto release.
57  */
58 public class CdsActorServiceProvider implements Actor {
59
60     private static final Logger LOGGER = LoggerFactory.getLogger(CdsActorServiceProvider.class);
61
62     /**
63      * {@inheritDoc}.
64      */
65     @Override
66     public String actor() {
67         return CdsActorConstants.CDS_ACTOR;
68     }
69
70     /**
71      * {@inheritDoc}. Note: This is a placeholder for now.
72      */
73     @Override
74     public List<String> recipes() {
75         return new ArrayList<>();
76     }
77
78     /**
79      * {@inheritDoc}. Note: This is a placeholder for now.
80      */
81     @Override
82     public List<String> recipeTargets(final String recipe) {
83         return new ArrayList<>();
84     }
85
86     /**
87      * {@inheritDoc}. Note: This is a placeholder for now.
88      */
89     @Override
90     public List<String> recipePayloads(final String recipe) {
91         return new ArrayList<>();
92     }
93
94     /**
95      * Build the CDS ExecutionServiceInput request from the policy object and the AAI enriched parameters. TO-DO: Avoid
96      * leaking Exceptions to the Kie Session thread. TBD item for Frankfurt release.
97      *
98      * @param onset the event that is reporting the alert for policy to perform an action.
99      * @param operation the control loop operation specifying the actor, operation, target, etc.
100      * @param policy the policy specified from the yaml generated by CLAMP or through Policy API.
101      * @param aaiParams Map of enriched AAI attributes in node.attribute notation.
102      * @return an Optional ExecutionServiceInput instance if valid else an Optional empty object is returned.
103      */
104     public Optional<ExecutionServiceInput> constructRequest(VirtualControlLoopEvent onset,
105         ControlLoopOperation operation, Policy policy, Map<String, String> aaiParams) {
106
107         // For the current operational TOSCA policy model (yaml) CBA name and version are embedded in the payload
108         // section, with the new policy type model being proposed in Frankfurt we will be able to move it out.
109         Map<String, String> payload = policy.getPayload();
110         if (!validateCdsMandatoryParams(policy)) {
111             return Optional.empty();
112         }
113         String cbaName = payload.get(CdsActorConstants.KEY_CBA_NAME);
114         String cbaVersion = payload.get(CdsActorConstants.KEY_CBA_VERSION);
115
116         // Retain only the payload by removing CBA name and version once they are extracted
117         // to be put in CDS request header.
118         payload.remove(CdsActorConstants.KEY_CBA_NAME);
119         payload.remove(CdsActorConstants.KEY_CBA_VERSION);
120
121         // Embed payload from policy to ConfigDeployRequest object, serialize and inject into grpc request.
122         String cbaActionName = policy.getRecipe();
123         CdsActionRequest request = new CdsActionRequest();
124         request.setPolicyPayload(payload);
125         request.setActionName(cbaActionName);
126         request.setResolutionKey(UUID.randomUUID().toString());
127
128         // Inject AAI properties into payload map. Offer flexibility to the usecase
129         // implementation to inject whatever AAI parameters are of interest to them.
130         // E.g. For vFW usecase El-Alto inject service-instance-id, generic-vnf-id as needed by CDS.
131         request.setAaiProperties(aaiParams);
132
133         Builder struct = Struct.newBuilder();
134         try {
135             String requestStr = request.generateCdsPayload();
136             Preconditions.checkState(!Strings.isNullOrEmpty(requestStr), "Unable to build "
137                 + "config-deploy-request from payload parameters: {}", payload);
138             JsonFormat.parser().merge(requestStr, struct);
139         } catch (InvalidProtocolBufferException | CoderException e) {
140             LOGGER.error("Failed to embed CDS payload string into the input request. blueprint({}:{}) for action({})",
141                     cbaName, cbaVersion, cbaActionName, e);
142             return Optional.empty();
143         }
144
145         // Build CDS gRPC request common-header
146         CommonHeader commonHeader = CommonHeader.newBuilder()
147             .setOriginatorId(CdsActorConstants.ORIGINATOR_ID)
148             .setRequestId(onset.getRequestId().toString())
149             .setSubRequestId(operation.getSubRequestId())
150             .build();
151
152         // Build CDS gRPC request action-identifier
153         ActionIdentifiers actionIdentifiers = ActionIdentifiers.newBuilder()
154             .setBlueprintName(cbaName)
155             .setBlueprintVersion(cbaVersion)
156             .setActionName(cbaActionName)
157             .setMode(CdsActorConstants.CDS_MODE)
158             .build();
159
160         // Finally build the ExecutionServiceInput gRPC request object.
161         ExecutionServiceInput executionServiceInput = ExecutionServiceInput.newBuilder()
162             .setCommonHeader(commonHeader)
163             .setActionIdentifiers(actionIdentifiers)
164             .setPayload(struct.build())
165             .build();
166         return Optional.of(executionServiceInput);
167     }
168
169     private boolean validateCdsMandatoryParams(Policy policy) {
170         if (policy == null || policy.getPayload() == null) {
171             return false;
172         }
173         Map<String, String> payload = policy.getPayload();
174         String cbaName = payload.get(CdsActorConstants.KEY_CBA_NAME);
175         String cbaVersion = payload.get(CdsActorConstants.KEY_CBA_VERSION);
176         String cbaActionName = policy.getRecipe();
177         return !Strings.isNullOrEmpty(cbaName) && !Strings.isNullOrEmpty(cbaVersion) && !Strings
178             .isNullOrEmpty(cbaActionName);
179     }
180
181     public class CdsActorServiceManager implements CdsProcessorListener {
182
183         private final AtomicReference<String> cdsStatus = new AtomicReference<>();
184
185         /**
186          * {@inheritDoc}.
187          */
188         @Override
189         public void onMessage(final ExecutionServiceOutput message) {
190             LOGGER.info("Received notification from CDS: {}", message);
191             EventType eventType = message.getStatus().getEventType();
192             switch (eventType) {
193                 case EVENT_COMPONENT_FAILURE:
194                     cdsStatus.compareAndSet(null, CdsActorConstants.FAILED);
195                     break;
196                 case EVENT_COMPONENT_PROCESSING:
197                     cdsStatus.compareAndSet(null, CdsActorConstants.PROCESSING);
198                     break;
199                 case EVENT_COMPONENT_EXECUTED:
200                     cdsStatus.compareAndSet(null, CdsActorConstants.SUCCESS);
201                     break;
202                 default:
203                     cdsStatus.compareAndSet(null, CdsActorConstants.FAILED);
204                     break;
205             }
206         }
207
208         /**
209          * {@inheritDoc}.
210          */
211         @Override
212         public void onError(final Throwable throwable) {
213             Status status = Status.fromThrowable(throwable);
214             cdsStatus.compareAndSet(null, CdsActorConstants.ERROR);
215             LOGGER.error("Failed processing blueprint {} {}", status, throwable);
216         }
217
218         /**
219          * Send gRPC request to CDS to execute the blueprint.
220          *
221          * @param cdsClient CDS grpc client object.
222          * @param cdsProps CDS properties.
223          * @param executionServiceInput a valid CDS grpc request object.
224          * @return the cds response.
225          */
226         public CdsResponse sendRequestToCds(CdsProcessorGrpcClient cdsClient, CdsServerProperties cdsProps,
227                                             ExecutionServiceInput executionServiceInput) {
228             try {
229                 LOGGER.trace("Start CdsActorServiceProvider.executeCdsBlueprintProcessor {}.", executionServiceInput);
230                 // TO-DO: Handle requests asynchronously once the callback support is added to actors.
231                 CountDownLatch countDownLatch = cdsClient.sendRequest(executionServiceInput);
232                 boolean status = countDownLatch.await(cdsProps.getTimeout(), TimeUnit.SECONDS);
233                 if (!status) {
234                     cdsStatus.compareAndSet(null, CdsActorConstants.TIMED_OUT);
235                 }
236                 LOGGER.info("CDS status response {}", getCdsStatus());
237             } catch (InterruptedException ex) {
238                 LOGGER.error("Caught exception in executeCdsBlueprintProcessor in CdsActorServiceProvider: ", ex);
239                 cdsStatus.compareAndSet(null, CdsActorConstants.INTERRUPTED);
240                 Thread.currentThread().interrupt();
241             }
242             LOGGER.info("Status of the CDS gRPC request is: {}", getCdsStatus());
243
244             CdsResponse response = new CdsResponse();
245             response.setRequestId(
246                     executionServiceInput != null && executionServiceInput.getCommonHeader() != null
247                         ? executionServiceInput.getCommonHeader().getRequestId() : null);
248             response.setStatus(this.getCdsStatus());
249             return response;
250         }
251
252         String getCdsStatus() {
253             return cdsStatus.get();
254         }
255     }
256 }