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