c370f153fa5761462d15858d7afaebf63ee6c7d2
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * AppcLcmActorServiceProvider
4  * ================================================================================
5  * Copyright (C) 2017-2018 AT&T Intellectual Property. 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
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.onap.policy.controlloop.actor.appclcm;
22
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25
26 import java.util.AbstractMap;
27 import java.util.AbstractMap.SimpleEntry;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.UUID;
33
34 import org.onap.policy.aai.AaiManager;
35 import org.onap.policy.aai.AaiNqInstanceFilters;
36 import org.onap.policy.aai.AaiNqInventoryResponseItem;
37 import org.onap.policy.aai.AaiNqNamedQuery;
38 import org.onap.policy.aai.AaiNqQueryParameters;
39 import org.onap.policy.aai.AaiNqRequest;
40 import org.onap.policy.aai.AaiNqResponse;
41 import org.onap.policy.aai.util.AaiException;
42 import org.onap.policy.appclcm.LcmCommonHeader;
43 import org.onap.policy.appclcm.LcmRequest;
44 import org.onap.policy.appclcm.LcmRequestWrapper;
45 import org.onap.policy.appclcm.LcmResponse;
46 import org.onap.policy.appclcm.LcmResponseCode;
47 import org.onap.policy.appclcm.LcmResponseWrapper;
48 import org.onap.policy.controlloop.ControlLoopOperation;
49 import org.onap.policy.controlloop.VirtualControlLoopEvent;
50 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
51 import org.onap.policy.controlloop.policy.Policy;
52 import org.onap.policy.controlloop.policy.PolicyResult;
53 import org.onap.policy.drools.system.PolicyEngine;
54 import org.onap.policy.rest.RESTManager;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 public class AppcLcmActorServiceProvider implements Actor {
59
60     private static final Logger logger = LoggerFactory.getLogger(AppcLcmActorServiceProvider.class);
61
62     /* To be used in future releases to restart a single vm */
63     private static final String APPC_VM_ID = "vm-id";
64
65     // Strings for targets
66     private static final String TARGET_VM = "VM";
67     private static final String TARGET_VNF = "VNF";
68
69     // Strings for recipes
70     private static final String RECIPE_RESTART = "Restart";
71     private static final String RECIPE_REBUILD = "Rebuild";
72     private static final String RECIPE_MIGRATE = "Migrate";
73     private static final String RECIPE_MODIFY = "ConfigModify";
74
75     /* To be used in future releases when LCM ConfigModify is used */
76     private static final String APPC_REQUEST_PARAMS = "request-parameters";
77     private static final String APPC_CONFIG_PARAMS = "configuration-parameters";
78
79     private static final ImmutableList<String> recipes =
80             ImmutableList.of(RECIPE_RESTART, RECIPE_REBUILD, RECIPE_MIGRATE, RECIPE_MODIFY);
81     private static final ImmutableMap<String, List<String>> targets = new ImmutableMap.Builder<String, List<String>>()
82             .put(RECIPE_RESTART, ImmutableList.of(TARGET_VM)).put(RECIPE_REBUILD, ImmutableList.of(TARGET_VM))
83             .put(RECIPE_MIGRATE, ImmutableList.of(TARGET_VM)).put(RECIPE_MODIFY, ImmutableList.of(TARGET_VNF)).build();
84     private static final ImmutableMap<String, List<String>> payloads =
85             new ImmutableMap.Builder<String, List<String>>().put(RECIPE_RESTART, ImmutableList.of(APPC_VM_ID))
86                     .put(RECIPE_MODIFY, ImmutableList.of(APPC_REQUEST_PARAMS, APPC_CONFIG_PARAMS)).build();
87
88     @Override
89     public String actor() {
90         return "APPC";
91     }
92
93     @Override
94     public List<String> recipes() {
95         return ImmutableList.copyOf(recipes);
96     }
97
98     @Override
99     public List<String> recipeTargets(String recipe) {
100         return ImmutableList.copyOf(targets.getOrDefault(recipe, Collections.emptyList()));
101     }
102
103     @Override
104     public List<String> recipePayloads(String recipe) {
105         return ImmutableList.copyOf(payloads.getOrDefault(recipe, Collections.emptyList()));
106     }
107
108     /**
109      * This method recursively traverses the A&AI named query response to find the generic-vnf
110      * object that contains a model-invariant-id that matches the resourceId of the policy. Once
111      * this match is found the generic-vnf object's vnf-id is returned.
112      * 
113      * @param items the list of items related to the vnf returned by A&AI
114      * @param resourceId the id of the target from the sdc catalog
115      * 
116      * @return the vnf-id of the target vnf to act upon or null if not found
117      */
118     private static String parseAaiResponse(List<AaiNqInventoryResponseItem> items, String resourceId) {
119         String vnfId = null;
120         for (AaiNqInventoryResponseItem item : items) {
121             if ((item.getGenericVnf() != null) && (item.getGenericVnf().getModelInvariantId() != null)
122                     && (resourceId.equals(item.getGenericVnf().getModelInvariantId()))) {
123                 vnfId = item.getGenericVnf().getVnfId();
124                 break;
125             } else {
126                 if ((item.getItems() != null) && (item.getItems().getInventoryResponseItems() != null)) {
127                     vnfId = parseAaiResponse(item.getItems().getInventoryResponseItems(), resourceId);
128                 }
129             }
130         }
131         return vnfId;
132     }
133
134     /**
135      * Constructs an A&AI Named Query using a source vnf-id to determine the vnf-id of the target
136      * entity specified in the policy to act upon.
137      * 
138      * @param resourceId the id of the target from the sdc catalog
139      * 
140      * @param sourceVnfId the vnf id of the source entity reporting the alert
141      * 
142      * @return the target entities vnf id to act upon
143      * @throws AaiException it an error occurs
144      */
145     public static String vnfNamedQuery(String resourceId, String sourceVnfId) throws AaiException {
146
147         // TODO: This request id should not be hard coded in future releases
148         UUID requestId = UUID.fromString("a93ac487-409c-4e8c-9e5f-334ae8f99087");
149
150         AaiNqRequest aaiRequest = new AaiNqRequest();
151         aaiRequest.setQueryParameters(new AaiNqQueryParameters());
152         aaiRequest.getQueryParameters().setNamedQuery(new AaiNqNamedQuery());
153         aaiRequest.getQueryParameters().getNamedQuery().setNamedQueryUuid(requestId);
154
155         Map<String, Map<String, String>> filter = new HashMap<>();
156         Map<String, String> filterItem = new HashMap<>();
157
158         filterItem.put("vnf-id", sourceVnfId);
159         filter.put("generic-vnf", filterItem);
160
161         aaiRequest.setInstanceFilters(new AaiNqInstanceFilters());
162         aaiRequest.getInstanceFilters().getInstanceFilter().add(filter);
163
164         AaiNqResponse aaiResponse = new AaiManager(new RESTManager()).postQuery(getPeManagerEnvProperty("aai.url"),
165                 getPeManagerEnvProperty("aai.username"), getPeManagerEnvProperty("aai.password"), aaiRequest,
166                 requestId);
167
168         if (aaiResponse == null) {
169             throw new AaiException("The named query response was null");
170         }
171
172         String targetVnfId = parseAaiResponse(aaiResponse.getInventoryResponseItems(), resourceId);
173         if (targetVnfId == null) {
174             throw new AaiException("Target vnf-id could not be found");
175         }
176
177         return targetVnfId;
178     }
179
180     /**
181      * Constructs an APPC request conforming to the lcm API. The actual request is constructed and
182      * then placed in a wrapper object used to send through DMAAP.
183      * 
184      * @param onset the event that is reporting the alert for policy to perform an action
185      * @param operation the control loop operation specifying the actor, operation, target, etc.
186      * @param policy the policy the was specified from the yaml generated by CLAMP or through the
187      *        Policy GUI/API
188      * @return an APPC request conforming to the lcm API using the DMAAP wrapper
189      */
190     public static LcmRequestWrapper constructRequest(VirtualControlLoopEvent onset, ControlLoopOperation operation,
191             Policy policy, String targetVnf) {
192
193         /* Construct an APPC request using LCM Model */
194
195         /*
196          * The actual LCM request is placed in a wrapper used to send through dmaap. The current
197          * version is 2.0 as of R1.
198          */
199         LcmRequestWrapper dmaapRequest = new LcmRequestWrapper();
200         dmaapRequest.setVersion("2.0");
201         dmaapRequest.setCorrelationId(onset.getRequestId() + "-" + operation.getSubRequestId());
202         dmaapRequest.setRpcName(policy.getRecipe().toLowerCase());
203         dmaapRequest.setType("request");
204
205         /* This is the actual request that is placed in the dmaap wrapper. */
206         final LcmRequest appcRequest = new LcmRequest();
207
208         /* The common header is a required field for all APPC requests. */
209         LcmCommonHeader requestCommonHeader = new LcmCommonHeader();
210         requestCommonHeader.setOriginatorId(onset.getRequestId().toString());
211         requestCommonHeader.setRequestId(onset.getRequestId());
212         requestCommonHeader.setSubRequestId(operation.getSubRequestId());
213
214         appcRequest.setCommonHeader(requestCommonHeader);
215
216         /*
217          * Action Identifiers are required for APPC LCM requests. For R1, the recipes supported by
218          * Policy only require a vnf-id.
219          */
220         HashMap<String, String> requestActionIdentifiers = new HashMap<>();
221         requestActionIdentifiers.put("vnf-id", targetVnf);
222
223         appcRequest.setActionIdentifiers(requestActionIdentifiers);
224
225         /*
226          * An action is required for all APPC requests, this will be the recipe specified in the
227          * policy.
228          */
229         appcRequest.setAction(
230                 policy.getRecipe().substring(0, 1).toUpperCase() + policy.getRecipe().substring(1).toLowerCase());
231
232         /*
233          * For R1, the payloads will not be required for the Restart, Rebuild, or Migrate recipes.
234          * APPC will populate the payload based on A&AI look up of the vnd-id provided in the action
235          * identifiers.
236          */
237         if (RECIPE_RESTART.equalsIgnoreCase(policy.getRecipe()) || RECIPE_REBUILD.equalsIgnoreCase(policy.getRecipe())
238                 || RECIPE_MIGRATE.equalsIgnoreCase(policy.getRecipe())) {
239             appcRequest.setPayload(null);
240         }
241
242         /*
243          * Once the LCM request is constructed, add it into the body of the dmaap wrapper.
244          */
245         dmaapRequest.setBody(appcRequest);
246
247         /* Return the request to be sent through dmaap. */
248         return dmaapRequest;
249     }
250
251     /**
252      * Parses the operation attempt using the subRequestId of APPC response.
253      * 
254      * @param subRequestId the sub id used to send to APPC, Policy sets this using the operation
255      *        attempt
256      * 
257      * @return the current operation attempt
258      */
259     public static Integer parseOperationAttempt(String subRequestId) {
260         Integer operationAttempt;
261         try {
262             operationAttempt = Integer.parseInt(subRequestId);
263         } catch (NumberFormatException e) {
264             logger.debug("A NumberFormatException was thrown due to error in parsing the operation attempt");
265             return null;
266         }
267         return operationAttempt;
268     }
269
270     /**
271      * Processes the APPC LCM response sent from APPC. Determines if the APPC operation was
272      * successful/unsuccessful and maps this to the corresponding Policy result.
273      * 
274      * @param dmaapResponse the dmaap wrapper message that contains the actual APPC reponse inside
275      *        the body field
276      * 
277      * @return an key-value pair that contains the Policy result and APPC response message
278      */
279     public static SimpleEntry<PolicyResult, String> processResponse(LcmResponseWrapper dmaapResponse) {
280         /* The actual APPC response is inside the wrapper's body field. */
281         LcmResponse appcResponse = dmaapResponse.getBody();
282
283         /* The message returned in the APPC response. */
284         String message;
285
286         /* The Policy result determined from the APPC Response. */
287         PolicyResult result;
288
289         /* If there is no status, Policy cannot determine if the request was successful. */
290         if (appcResponse.getStatus() == null) {
291             message = "Policy was unable to parse APP-C response status field (it was null).";
292             return new AbstractMap.SimpleEntry<>(PolicyResult.FAILURE_EXCEPTION, message);
293         }
294
295         /* If there is no code, Policy cannot determine if the request was successful. */
296         String responseValue = LcmResponseCode.toResponseValue(appcResponse.getStatus().getCode());
297         if (responseValue == null) {
298             message = "Policy was unable to parse APP-C response status code field.";
299             return new AbstractMap.SimpleEntry<>(PolicyResult.FAILURE_EXCEPTION, message);
300         }
301
302         /* Save the APPC response's message for Policy noticiation message. */
303         message = appcResponse.getStatus().getMessage();
304
305         /* Maps the APPC response result to a Policy result. */
306         switch (responseValue) {
307             case LcmResponseCode.ACCEPTED:
308                 /* Nothing to do if code is accept, continue processing */
309                 result = null;
310                 break;
311             case LcmResponseCode.SUCCESS:
312                 result = PolicyResult.SUCCESS;
313                 break;
314             case LcmResponseCode.FAILURE:
315                 result = PolicyResult.FAILURE;
316                 break;
317             case LcmResponseCode.REJECT:
318             case LcmResponseCode.ERROR:
319             default:
320                 result = PolicyResult.FAILURE_EXCEPTION;
321         }
322         return new AbstractMap.SimpleEntry<>(result, message);
323     }
324
325     /**
326      * This method reads and validates environmental properties coming from the policy engine. Null
327      * properties cause an {@link IllegalArgumentException} runtime exception to be thrown
328      * 
329      * @param string the name of the parameter to retrieve
330      * @return the property value
331      */
332     private static String getPeManagerEnvProperty(String enginePropertyName) {
333         String enginePropertyValue = PolicyEngine.manager.getEnvironmentProperty(enginePropertyName);
334         if (enginePropertyValue == null) {
335             throw new IllegalArgumentException("The value of policy engine manager environment property \""
336                     + enginePropertyName + "\" may not be null");
337         }
338         return enginePropertyValue;
339     }
340 }