Merge "Enhanced List Level flow with backward support"
[so.git] / adapters / mso-openstack-adapters / src / main / java / org / onap / so / adapters / vnf / MsoVnfAdapterImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
7  * ================================================================================
8  * Modifications Copyright (c) 2019 Samsung
9  * Modifications Copyright (c) 2019 IBM
10  * ================================================================================
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.onap.so.adapters.vnf;
26
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Optional;
36 import java.util.concurrent.TimeUnit;
37 import javax.jws.WebService;
38 import javax.xml.ws.Holder;
39 import org.onap.so.logger.LoggingAnchor;
40 import org.apache.commons.collections.CollectionUtils;
41 import org.onap.so.adapters.valet.GenericValetResponse;
42 import org.onap.so.adapters.valet.ValetClient;
43 import org.onap.so.adapters.valet.beans.HeatRequest;
44 import org.onap.so.adapters.valet.beans.ValetConfirmResponse;
45 import org.onap.so.adapters.valet.beans.ValetCreateResponse;
46 import org.onap.so.adapters.valet.beans.ValetDeleteResponse;
47 import org.onap.so.adapters.valet.beans.ValetRollbackResponse;
48 import org.onap.so.adapters.valet.beans.ValetStatus;
49 import org.onap.so.adapters.valet.beans.ValetUpdateResponse;
50 import org.onap.so.adapters.vnf.exceptions.VnfAlreadyExists;
51 import org.onap.so.adapters.vnf.exceptions.VnfException;
52 import org.onap.so.adapters.vnf.exceptions.VnfNotFound;
53 import org.onap.so.client.aai.AAIResourcesClient;
54 import org.onap.so.cloud.CloudConfig;
55 import org.onap.so.db.catalog.beans.CloudIdentity;
56 import org.onap.so.db.catalog.beans.CloudSite;
57 import org.onap.so.db.catalog.beans.HeatEnvironment;
58 import org.onap.so.db.catalog.beans.HeatFiles;
59 import org.onap.so.db.catalog.beans.HeatTemplate;
60 import org.onap.so.db.catalog.beans.HeatTemplateParam;
61 import org.onap.so.db.catalog.beans.VfModule;
62 import org.onap.so.db.catalog.beans.VfModuleCustomization;
63 import org.onap.so.db.catalog.beans.VnfResource;
64 import org.onap.so.db.catalog.data.repository.VFModuleCustomizationRepository;
65 import org.onap.so.db.catalog.data.repository.VnfResourceRepository;
66 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
67 import org.onap.so.entity.MsoRequest;
68 import org.onap.so.heatbridge.HeatBridgeApi;
69 import org.onap.so.heatbridge.HeatBridgeImpl;
70 import org.onap.so.logger.ErrorCode;
71 import org.onap.so.logger.MessageEnum;
72 import org.onap.so.openstack.beans.HeatStatus;
73 import org.onap.so.openstack.beans.StackInfo;
74 import org.onap.so.openstack.beans.VnfRollback;
75 import org.onap.so.openstack.beans.VnfStatus;
76 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
77 import org.onap.so.openstack.exceptions.MsoException;
78 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
79 import org.onap.so.openstack.exceptions.MsoHeatNotFoundException;
80 import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry;
81 import org.onap.so.openstack.utils.MsoHeatUtils;
82 import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
85 import org.springframework.beans.factory.annotation.Autowired;
86 import org.springframework.core.env.Environment;
87 import org.springframework.stereotype.Component;
88 import org.springframework.transaction.annotation.Transactional;
89 import com.fasterxml.jackson.core.JsonParseException;
90 import com.fasterxml.jackson.databind.JsonNode;
91 import com.fasterxml.jackson.databind.ObjectMapper;
92 import org.openstack4j.model.compute.Flavor;
93 import org.openstack4j.model.compute.Image;
94 import org.openstack4j.model.compute.Server;
95 import org.openstack4j.model.heat.Resource;
96
97 @WebService(serviceName = "VnfAdapter", endpointInterface = "org.onap.so.adapters.vnf.MsoVnfAdapter",
98         targetNamespace = "http://org.onap.so/vnf")
99 @Component
100 @Transactional
101 public class MsoVnfAdapterImpl implements MsoVnfAdapter {
102
103     @Autowired
104     private CloudConfig cloudConfig;
105
106     @Autowired
107     private Environment environment;
108
109     private static final Logger logger = LoggerFactory.getLogger(MsoVnfAdapterImpl.class);
110
111
112     private static final String VNF_ADAPTER_SERVICE_NAME = "MSO-BPMN:MSO-VnfAdapter.";
113     private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters";
114     private static final String ADD_GET_FILES_ON_VOLUME_REQ = "org.onap.so.adapters.vnf.addGetFilesOnVolumeReq";
115     private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
116     private static final String VALET_ENABLED = "org.onap.so.adapters.vnf.valet_enabled";
117     private static final String FAIL_REQUESTS_ON_VALET_FAILURE =
118             "org.onap.so.adapters.vnf.fail_requests_on_valet_failure";
119     private static final String OPENSTACK = "OpenStack";
120     private static final String DELETE_VNF = "DeleteVNF";
121     private static final String QUERY_STACK = "QueryStack";
122     private static final String CREATE_VFM_MODULE = "CreateVFModule";
123     private static final String CREATE_VF_STACK = "Create VF: Stack";
124     private static final String STACK = "Stack";
125     private static final String USER_ERROR = "USER ERROR";
126     private static final String VERSION_MIN = "VersionMin";
127     private static final String VERSION_MAX = "VersionMax";
128
129     @Autowired
130     private VFModuleCustomizationRepository vfModuleCustomRepo;
131     @Autowired
132     private VnfResourceRepository vnfResourceRepo;
133     @Autowired
134     private MsoHeatUtilsWithUpdate heatU;
135     @Autowired
136     private MsoHeatUtils heat;
137     @Autowired
138     private ValetClient vci;
139
140     /**
141      * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
142      *
143      * @see MsoVnfAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
144      */
145     public MsoVnfAdapterImpl() {
146         // Do nothing
147         // DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
148     }
149
150     /**
151      * Health Check web method. Does nothing but return to show the adapter is deployed.
152      */
153     @Override
154     public void healthCheck() {
155         logger.debug("Health check call in VNF Adapter");
156     }
157
158     /**
159      * This is the "Create VNF" web service implementation. It will create a new VNF of the requested type in the
160      * specified cloud and tenant. The tenant must exist before this service is called.
161      *
162      * If a VNF with the same name already exists, this can be considered a success or failure, depending on the value
163      * of the 'failIfExists' parameter.
164      *
165      * All VNF types will be defined in the MSO catalog. The caller must request one of these pre-defined types or an
166      * error will be returned. Within the catalog, each VNF type references (among other things) a Heat template which
167      * is used to deploy the required VNF artifacts (VMs, networks, etc.) to the cloud.
168      *
169      * Depending on the Heat template, a variable set of input parameters will be defined, some of which are required.
170      * The caller is responsible to pass the necessary input data for the VNF or an error will be thrown.
171      *
172      * The method returns the vnfId (the canonical name), a Map of VNF output attributes, and a VnfRollback object. This
173      * last object can be passed as-is to the rollbackVnf operation to undo everything that was created for the VNF.
174      * This is useful if a VNF is successfully created but the orchestrator fails on a subsequent operation.
175      *
176      * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
177      * @param cloudOwner cloud owner of the cloud region in which to create the VNF
178      * @param tenantId Openstack tenant identifier
179      * @param vnfType VNF type key, should match a VNF definition in catalog DB
180      * @param vnfVersion VNF version key, should match a VNF definition in catalog DB
181      * @param vnfName Name to be assigned to the new VNF
182      * @param inputs Map of key=value inputs for VNF stack creation
183      * @param failIfExists Flag whether already existing VNF should be considered a success or failure
184      * @param msoRequest Request tracking information for logs
185      * @param vnfId Holder for output VNF Openstack ID
186      * @param outputs Holder for Map of VNF outputs from heat (assigned IPs, etc)
187      * @param rollback Holder for returning VnfRollback object
188      */
189     @Override
190     public void createVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfType, String vnfVersion,
191             String vnfName, String requestType, String volumeGroupHeatStackId, Map<String, Object> inputs,
192             Boolean failIfExists, Boolean backout, Boolean enableBridge, MsoRequest msoRequest, Holder<String> vnfId,
193             Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback) throws VnfException {
194         // parameters used for multicloud adapter
195         String genericVnfId = "";
196         String vfModuleId = "";
197         // Create a hook here to catch shortcut createVf requests:
198         if (requestType != null && requestType.startsWith("VFMOD")) {
199             logger.debug("Calling createVfModule from createVnf -- requestType={}", requestType);
200             String newRequestType = requestType.substring(5);
201             String vfVolGroupHeatStackId = "";
202             String vfBaseHeatStackId = "";
203             try {
204                 if (volumeGroupHeatStackId != null) {
205                     vfVolGroupHeatStackId =
206                             volumeGroupHeatStackId.substring(0, volumeGroupHeatStackId.lastIndexOf('|'));
207                     vfBaseHeatStackId = volumeGroupHeatStackId.substring(volumeGroupHeatStackId.lastIndexOf('|') + 1);
208                 }
209             } catch (Exception e) {
210                 // might be ok - both are just blank
211                 logger.debug("ERROR trying to parse the volumeGroupHeatStackId {}", volumeGroupHeatStackId, e);
212             }
213             this.createVfModule(cloudSiteId, cloudOwner, tenantId, vnfType, vnfVersion, genericVnfId, vnfName,
214                     vfModuleId, newRequestType, vfVolGroupHeatStackId, vfBaseHeatStackId, null, inputs, failIfExists,
215                     backout, enableBridge, msoRequest, vnfId, outputs, rollback);
216             return;
217         }
218         // createVf will know if the requestType starts with "X" that it's the "old" way
219         StringBuilder newRequestTypeSb = new StringBuilder("X");
220         String vfVolGroupHeatStackId = "";
221         String vfBaseHeatStackId = "";
222         if (requestType != null) {
223             newRequestTypeSb.append(requestType);
224         }
225         this.createVfModule(cloudSiteId, cloudOwner, tenantId, vnfType, vnfVersion, genericVnfId, vnfName, vfModuleId,
226                 newRequestTypeSb.toString(), vfVolGroupHeatStackId, vfBaseHeatStackId, null, inputs, failIfExists,
227                 backout, enableBridge, msoRequest, vnfId, outputs, rollback);
228         return;
229         // End createVf shortcut
230     }
231
232     @Override
233     public void updateVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfType, String vnfVersion,
234             String vnfName, String requestType, String volumeGroupHeatStackId, Map<String, Object> inputs,
235             MsoRequest msoRequest, Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback)
236             throws VnfException {
237         // As of 1707 - this method should no longer be called
238         logger.debug("UpdateVnf called?? This should not be called any longer - update vfModule");
239     }
240
241     /**
242      * This is the "Query VNF" web service implementation. It will look up a VNF by name or ID in the specified cloud
243      * and tenant.
244      *
245      * The method returns an indicator that the VNF exists, its Openstack internal ID, its status, and the set of
246      * outputs (from when the stack was created).
247      *
248      * @param cloudSiteId CLLI code of the cloud site in which to query
249      * @param tenantId Openstack tenant identifier
250      * @param vnfName VNF Name or Openstack ID
251      * @param msoRequest Request tracking information for logs
252      * @param vnfExists Flag reporting the result of the query
253      * @param vnfId Holder for output VNF Openstack ID
254      * @param outputs Holder for Map of VNF outputs from heat (assigned IPs, etc)
255      */
256     @Override
257     public void queryVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfName, MsoRequest msoRequest,
258             Holder<Boolean> vnfExists, Holder<String> vnfId, Holder<VnfStatus> status,
259             Holder<Map<String, String>> outputs) throws VnfException {
260
261         logger.debug("Querying VNF {} in {}/{}", vnfName, cloudSiteId, tenantId);
262
263         // Will capture execution time for metrics
264
265         StackInfo heatStack;
266         try {
267             heatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, vnfName);
268         } catch (MsoException me) {
269             me.addContext("QueryVNF");
270             // Failed to query the Stack due to an openstack exception.
271             // Convert to a generic VnfException
272             String error =
273                     "Query VNF: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
274             logger.error(LoggingAnchor.EIGHT, MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudSiteId, tenantId,
275                     OPENSTACK, "QueryVNF", ErrorCode.DataError.getValue(), "Exception - " + QUERY_STACK, me);
276             logger.debug(error);
277             throw new VnfException(me);
278         }
279
280         // Populate the outputs based on the returned Stack information
281         //
282         if (heatStack == null || heatStack.getStatus() == HeatStatus.NOTFOUND) {
283             // Not Found
284             vnfExists.value = Boolean.FALSE;
285             status.value = VnfStatus.NOTFOUND;
286             vnfId.value = null;
287             outputs.value = new HashMap<>(); // Return as an empty map
288
289             logger.debug("VNF {} not found", vnfName);
290         } else {
291             vnfExists.value = Boolean.TRUE;
292             status.value = stackStatusToVnfStatus(heatStack.getStatus());
293             vnfId.value = heatStack.getCanonicalName();
294             outputs.value = copyStringOutputs(heatStack.getOutputs());
295
296             logger.debug("VNF {} found, ID = {}", vnfName, vnfId.value);
297         }
298         return;
299     }
300
301     /**
302      * This is the "Delete VNF" web service implementation. It will delete a VNF by name or ID in the specified cloud
303      * and tenant.
304      *
305      * The method has no outputs.
306      *
307      * @param cloudSiteId CLLI code of the cloud site in which to delete
308      * @param cloudOwner cloud owner of the cloud region in which to delete
309      * @param tenantId Openstack tenant identifier
310      * @param vnfName VNF Name or Openstack ID
311      * @param msoRequest Request tracking information for logs
312      */
313     @Override
314     public void deleteVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfName, MsoRequest msoRequest)
315             throws VnfException {
316
317         logger.debug("Deleting VNF {} in {}", vnfName, cloudSiteId + "/" + tenantId);
318
319         try {
320             heat.deleteStack(tenantId, cloudOwner, cloudSiteId, vnfName, true);
321         } catch (MsoException me) {
322             me.addContext(DELETE_VNF);
323             // Failed to query the Stack due to an openstack exception.
324             // Convert to a generic VnfException
325             String error =
326                     "Delete VNF: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
327             logger.error(LoggingAnchor.NINE, MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId,
328                     tenantId, OPENSTACK, DELETE_VNF, ErrorCode.DataError.getValue(), "Exception - " + DELETE_VNF, me);
329             logger.debug(error);
330             throw new VnfException(me);
331         }
332
333         // On success, nothing is returned.
334         return;
335     }
336
337     /**
338      * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
339      * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
340      * to undo the creation.
341      */
342     @Override
343     public void rollbackVnf(VnfRollback rollback) throws VnfException {
344         // rollback may be null (e.g. if stack already existed when Create was called)
345         if (rollback == null) {
346             logger.info(MessageEnum.RA_ROLLBACK_NULL.toString(), OPENSTACK, "rollbackVnf");
347             return;
348         }
349
350         // Get the elements of the VnfRollback object for easier access
351         String cloudSiteId = rollback.getCloudSiteId();
352         String cloudOwner = rollback.getCloudOwner();
353         String tenantId = rollback.getTenantId();
354         String vnfId = rollback.getVnfId();
355
356         logger.debug("Rolling Back VNF {} in {}", vnfId, cloudOwner + "/" + cloudSiteId + "/" + tenantId);
357
358         // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
359         // The possible outcomes of deleteStack are a StackInfo object with status
360         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
361         // could be thrown.
362         try {
363             heat.deleteStack(tenantId, cloudOwner, cloudSiteId, vnfId, true);
364         } catch (MsoException me) {
365             // Failed to rollback the Stack due to an openstack exception.
366             // Convert to a generic VnfException
367             me.addContext("RollbackVNF");
368             String error =
369                     "Rollback VNF: " + vnfId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
370             logger.error(LoggingAnchor.NINE, MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfId, cloudOwner, cloudSiteId,
371                     tenantId, OPENSTACK, "DeleteStack", ErrorCode.DataError.getValue(), "Exception - DeleteStack", me);
372             logger.debug(error);
373             throw new VnfException(me);
374         }
375         return;
376     }
377
378     private VnfStatus stackStatusToVnfStatus(HeatStatus stackStatus) {
379         switch (stackStatus) {
380             case CREATED:
381                 return VnfStatus.ACTIVE;
382             case UPDATED:
383                 return VnfStatus.ACTIVE;
384             case FAILED:
385                 return VnfStatus.FAILED;
386             default:
387                 return VnfStatus.UNKNOWN;
388         }
389     }
390
391     private Map<String, String> copyStringOutputs(Map<String, Object> stackOutputs) {
392         Map<String, String> stringOutputs = new HashMap<>();
393         for (Map.Entry<String, Object> entry : stackOutputs.entrySet()) {
394             String key = entry.getKey();
395             Object value = entry.getValue();
396             try {
397                 stringOutputs.put(key, value.toString());
398             } catch (Exception e) {
399                 StringBuilder msg = new StringBuilder("Unable to add " + key + " to outputs");
400                 if (value instanceof Integer) { // nothing to add to the message
401                 } else if (value instanceof JsonNode) {
402                     msg.append(" - exception converting JsonNode");
403                 } else if (value instanceof java.util.LinkedHashMap) {
404                     msg.append(" exception converting LinkedHashMap");
405                 } else {
406                     msg.append(" - unable to call .toString() " + e.getMessage());
407                 }
408                 logger.debug(msg.toString(), e);
409             }
410         }
411         return stringOutputs;
412     }
413
414     private Map<String, Object> copyStringInputs(Map<String, Object> stringInputs) {
415         return new HashMap<>(stringInputs);
416     }
417
418     protected boolean callHeatbridge(String heatStackId) {
419         String executionDir = "/usr/local/lib/python2.7/dist-packages/heatbridge";
420         String openstackIdentityUrl = "", username = "", password = "", tenant = "", region = "", owner = "";
421         long waitTimeMs = 10000L;
422         try {
423             String[] cmdarray = {"/usr/bin/python", "HeatBridgeMain.py", openstackIdentityUrl, username, password,
424                     tenant, region, owner, heatStackId};
425             String[] envp = null;
426             File dir = new File(executionDir);
427             logger.debug("Calling HeatBridgeMain.py in {} with arguments {}", dir, Arrays.toString(cmdarray));
428             Runtime r = Runtime.getRuntime();
429             Process p = r.exec(cmdarray, envp, dir);
430             boolean wait = p.waitFor(waitTimeMs, TimeUnit.MILLISECONDS);
431
432             logger.debug(" HeatBridgeMain.py returned {} with code {}", wait, p.exitValue());
433             return wait && p.exitValue() == 0;
434         } catch (IOException e) {
435             logger.debug(" HeatBridgeMain.py failed with IO Exception! {}", e);
436             return false;
437         } catch (RuntimeException e) {
438             logger.debug(" HeatBridgeMain.py failed during runtime! {}", e);
439             return false;
440         } catch (Exception e) {
441             logger.debug(" HeatBridgeMain.py failed for unknown reasons! {}", e);
442             return false;
443         }
444     }
445
446     private void heatbridge(StackInfo heatStack, String cloudOwner, String cloudSiteId, String tenantId,
447             String genericVnfName, String vfModuleId) {
448         try {
449             CloudSite cloudSite =
450                     cloudConfig.getCloudSite(cloudSiteId).orElseThrow(() -> new MsoCloudSiteNotFound(cloudSiteId));
451             CloudIdentity cloudIdentity = cloudSite.getIdentityService();
452             String heatStackId = heatStack.getCanonicalName().split("/")[1];
453
454             List<String> oobMgtNetNames = new ArrayList<>();
455
456             HeatBridgeApi heatBridgeClient =
457                     new HeatBridgeImpl(new AAIResourcesClient(), cloudIdentity, cloudOwner, cloudSiteId, tenantId);
458
459             List<Resource> stackResources = heatBridgeClient.queryNestedHeatStackResources(heatStackId);
460
461             List<Server> osServers = heatBridgeClient.getAllOpenstackServers(stackResources);
462
463             List<Image> osImages = heatBridgeClient.extractOpenstackImagesFromServers(osServers);
464
465             List<Flavor> osFlavors = heatBridgeClient.extractOpenstackFlavorsFromServers(osServers);
466
467             logger.debug("Successfully queried heat stack{} for resources.", heatStackId);
468             // os images
469             if (osImages != null && !osImages.isEmpty()) {
470                 heatBridgeClient.buildAddImagesToAaiAction(osImages);
471                 logger.debug("Successfully built AAI actions to add images.");
472             } else {
473                 logger.debug("No images to update to AAI.");
474             }
475             // flavors
476             if (osFlavors != null && !osFlavors.isEmpty()) {
477                 heatBridgeClient.buildAddFlavorsToAaiAction(osFlavors);
478                 logger.debug("Successfully built AAI actions to add flavors.");
479             } else {
480                 logger.debug("No flavors to update to AAI.");
481             }
482
483             // compute resources
484             heatBridgeClient.buildAddVserversToAaiAction(genericVnfName, vfModuleId, osServers);
485             logger.debug("Successfully queried compute resources and built AAI vserver actions.");
486
487             // neutron resources
488             List<String> oobMgtNetIds = new ArrayList<>();
489
490             // if no network-id list is provided, however network-name list is
491             if (!CollectionUtils.isEmpty(oobMgtNetNames)) {
492                 oobMgtNetIds = heatBridgeClient.extractNetworkIds(oobMgtNetNames);
493             }
494             heatBridgeClient.buildAddVserverLInterfacesToAaiAction(stackResources, oobMgtNetIds);
495             logger.debug(
496                     "Successfully queried neutron resources and built AAI actions to add l-interfaces to vservers.");
497
498             // Update AAI
499             heatBridgeClient.submitToAai();
500         } catch (Exception ex) {
501             logger.debug("Heatbrige failed for stackId: " + heatStack.getCanonicalName(), ex);
502         }
503     }
504
505     private String convertNode(final JsonNode node) {
506         try {
507             final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
508             return JSON_MAPPER.writeValueAsString(obj);
509         } catch (JsonParseException jpe) {
510             logger.debug("Error converting json to string: {}", jpe.getMessage(), jpe);
511         } catch (Exception e) {
512             logger.debug("Error converting json to string: {}", e.getMessage(), e);
513         }
514         return "[Error converting json to string]";
515     }
516
517     private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
518         if (objectMap == null) {
519             return null;
520         }
521         Map<String, String> stringMap = new HashMap<>();
522         for (String key : objectMap.keySet()) {
523             if (!stringMap.containsKey(key)) {
524                 Object obj = objectMap.get(key);
525                 if (obj instanceof String) {
526                     stringMap.put(key, (String) objectMap.get(key));
527                 } else if (obj instanceof JsonNode) {
528                     // This is a bit of mess - but I think it's the least impacting
529                     // let's convert it BACK to a string - then it will get converted back later
530                     try {
531                         String str = this.convertNode((JsonNode) obj);
532                         stringMap.put(key, str);
533                     } catch (Exception e) {
534                         logger.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode " + key, e);
535                         // okay in this instance - only string values (fqdn) are expected to be needed
536                     }
537                 } else if (obj instanceof java.util.LinkedHashMap) {
538                     logger.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
539                     try {
540                         String str = JSON_MAPPER.writeValueAsString(obj);
541                         stringMap.put(key, str);
542                     } catch (Exception e) {
543                         logger.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap " + key, e);
544                     }
545                 } else if (obj instanceof Integer) {
546                     try {
547                         String str = "" + obj;
548                         stringMap.put(key, str);
549                     } catch (Exception e) {
550                         logger.debug("DANGER WILL ROBINSON: unable to convert value for Integer " + key, e);
551                     }
552                 } else {
553                     try {
554                         String str = obj.toString();
555                         stringMap.put(key, str);
556                     } catch (Exception e) {
557                         logger.debug(
558                                 "DANGER WILL ROBINSON: unable to convert value " + key + " (" + e.getMessage() + ")",
559                                 e);
560                     }
561                 }
562             }
563         }
564
565         return stringMap;
566     }
567
568     @Override
569     public void createVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vnfType,
570             String vnfVersion, String genericVnfName, String vnfName, String vfModuleId, String requestType,
571             String volumeGroupHeatStackId, String baseVfHeatStackId, String modelCustomizationUuid,
572             Map<String, Object> inputs, Boolean failIfExists, Boolean backout, Boolean enableBridge,
573             MsoRequest msoRequest, Holder<String> vnfId, Holder<Map<String, String>> outputs,
574             Holder<VnfRollback> rollback) throws VnfException {
575         String vfModuleName = vnfName;
576         String vfModuleType = vnfType;
577         String vfVersion = vnfVersion;
578         String mcu = modelCustomizationUuid;
579         boolean useMCUuid = false;
580         if (mcu != null && !mcu.isEmpty()) {
581             if ("null".equalsIgnoreCase(mcu)) {
582                 logger.debug("modelCustomizationUuid: passed in as the string 'null' - will ignore: "
583                         + modelCustomizationUuid);
584                 useMCUuid = false;
585                 mcu = "";
586             } else {
587                 logger.debug("Found modelCustomizationUuid! Will use that: {}", mcu);
588                 useMCUuid = true;
589             }
590         }
591
592         String requestTypeString = "";
593         if (requestType != null && !"".equals(requestType)) {
594             requestTypeString = requestType;
595         }
596         String nestedStackId = null;
597         if (volumeGroupHeatStackId != null && !"".equals(volumeGroupHeatStackId)
598                 && !"null".equalsIgnoreCase(volumeGroupHeatStackId)) {
599             nestedStackId = volumeGroupHeatStackId;
600         }
601         String nestedBaseStackId = null;
602         if (baseVfHeatStackId != null && !"".equals(baseVfHeatStackId) && !"null".equalsIgnoreCase(baseVfHeatStackId)) {
603             nestedBaseStackId = baseVfHeatStackId;
604         }
605
606         // This method will also handle doing things the "old" way - i.e., just orchestrate a VNF
607         boolean oldWay = false;
608         if (requestTypeString.startsWith("X")) {
609             oldWay = true;
610             logger.debug("orchestrating a VNF - *NOT* a module!");
611             requestTypeString = requestTypeString.substring(1);
612         }
613
614         // let's parse out the request type we're being sent
615         boolean isBaseRequest = false;
616         boolean isVolumeRequest = false;
617         if (requestTypeString.startsWith("VOLUME")) {
618             isVolumeRequest = true;
619         }
620
621         logger.debug("requestTypeString = " + requestTypeString + ", nestedStackId = " + nestedStackId
622                 + ", nestedBaseStackId = " + nestedBaseStackId);
623
624         // Build a default rollback object (no actions performed)
625         VnfRollback vfRollback = new VnfRollback();
626         vfRollback.setCloudSiteId(cloudSiteId);
627         vfRollback.setCloudOwner(cloudOwner);
628         vfRollback.setTenantId(tenantId);
629         vfRollback.setMsoRequest(msoRequest);
630         vfRollback.setRequestType(requestTypeString);
631         vfRollback.setVolumeGroupHeatStackId(volumeGroupHeatStackId);
632         vfRollback.setBaseGroupHeatStackId(baseVfHeatStackId);
633         vfRollback.setIsBase(isBaseRequest);
634         vfRollback.setModelCustomizationUuid(mcu);
635
636         // Put data into A&AI through Heatstack
637         if (enableBridge != null && enableBridge) {
638             callHeatbridge(baseVfHeatStackId);
639         }
640
641         StackInfo heatStack = null;
642         try {
643             heatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, vfModuleName);
644         } catch (MsoException me) {
645             String error = "Create VF Module: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/"
646                     + tenantId + ": " + me;
647             logger.error(LoggingAnchor.NINE, MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner,
648                     cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(),
649                     "Exception - " + QUERY_STACK, me);
650             logger.debug(error);
651             // Failed to query the Stack due to an openstack exception.
652             // Convert to a generic VnfException
653             me.addContext(CREATE_VFM_MODULE);
654             throw new VnfException(me);
655         }
656         // more precise handling/messaging if the stack already exists
657         if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
658             // INIT, CREATED, NOTFOUND, FAILED, BUILDING, DELETING, UNKNOWN, UPDATING, UPDATED
659             HeatStatus status = heatStack.getStatus();
660             if (status == HeatStatus.INIT || status == HeatStatus.BUILDING || status == HeatStatus.DELETING
661                     || status == HeatStatus.UPDATING) {
662                 // fail - it's in progress - return meaningful error
663                 String error = CREATE_VF_STACK + " " + vfModuleName + " already exists and has status "
664                         + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
665                         + "; please wait for it to complete, or fix manually.";
666                 logger.error(LoggingAnchor.NINE, MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, cloudOwner,
667                         cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(),
668                         STACK + " " + vfModuleName + " already exists");
669                 logger.debug(error);
670                 throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId,
671                         heatStack.getCanonicalName());
672             }
673             if (status == HeatStatus.FAILED) {
674                 // fail - it exists and is in a FAILED state
675                 String error = CREATE_VF_STACK + " " + vfModuleName + " already exists and is in FAILED state in "
676                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
677                 logger.error(LoggingAnchor.NINE, MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, cloudOwner,
678                         cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(),
679                         STACK + " " + vfModuleName + " already exists and is " + "in FAILED state");
680                 logger.debug(error);
681                 throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId,
682                         heatStack.getCanonicalName());
683             }
684             if (status == HeatStatus.UNKNOWN || status == HeatStatus.UPDATED) {
685                 // fail - it exists and is in a FAILED state
686                 String error = CREATE_VF_STACK + " " + vfModuleName + " already exists and has status "
687                         + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
688                         + "; requires manual intervention.";
689                 logger.error(LoggingAnchor.NINE, MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, cloudOwner,
690                         cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(),
691                         STACK + " " + vfModuleName + " already exists and is " + "in UPDATED or UNKNOWN state");
692                 logger.debug(error);
693                 throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId,
694                         heatStack.getCanonicalName());
695             }
696             if (status == HeatStatus.CREATED) {
697                 // fail - it exists
698                 if (failIfExists != null && failIfExists) {
699                     String error = CREATE_VF_STACK + " " + vfModuleName + " already exists in " + cloudOwner + "/"
700                             + cloudSiteId + "/" + tenantId;
701                     logger.error(LoggingAnchor.NINE, MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
702                             cloudOwner, cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(),
703                             STACK + " " + vfModuleName + " already exists");
704                     logger.debug(error);
705                     throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId,
706                             heatStack.getCanonicalName());
707                 } else {
708                     logger.debug("Found Existing stack, status={}", heatStack.getStatus());
709                     // Populate the outputs from the existing stack.
710                     vnfId.value = heatStack.getCanonicalName();
711                     outputs.value = copyStringOutputs(heatStack.getOutputs());
712                     rollback.value = vfRollback; // Default rollback - no updates performed
713                 }
714             }
715             return;
716
717         }
718
719         // handle a nestedStackId if sent- this one would be for the volume - so applies to both Vf and Vnf
720         StackInfo nestedHeatStack = null;
721
722         Map<String, Object> nestedVolumeOutputs = null;
723         if (nestedStackId != null) {
724             try {
725                 logger.debug("Querying for nestedStackId = {}", nestedStackId);
726                 nestedHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedStackId);
727             } catch (MsoException me) {
728                 // Failed to query the Stack due to an openstack exception.
729                 // Convert to a generic VnfException
730                 me.addContext(CREATE_VFM_MODULE);
731                 String error = "Create VFModule: Attached heatStack ID Query " + nestedStackId + " in " + cloudOwner
732                         + "/" + cloudSiteId + "/" + tenantId + ": " + me;
733                 logger.error(LoggingAnchor.NINE, MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner,
734                         cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.BusinessProcesssError.getValue(),
735                         "MsoException trying to query nested stack", me);
736                 logger.debug("ERROR trying to query nested stack= {}", error);
737                 throw new VnfException(me);
738             }
739             if (nestedHeatStack == null || nestedHeatStack.getStatus() == HeatStatus.NOTFOUND) {
740                 String error = "Create VFModule: Attached heatStack ID DOES NOT EXIST " + nestedStackId + " in "
741                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " " + USER_ERROR;
742                 logger.error(LoggingAnchor.TEN, MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner,
743                         cloudSiteId, tenantId, error, OPENSTACK, QUERY_STACK,
744                         ErrorCode.BusinessProcesssError.getValue(),
745                         "Create VFModule: Attached heatStack ID " + "DOES NOT EXIST");
746                 logger.debug(error);
747                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
748             } else {
749                 logger.debug("Found nested volume heat stack - copying values to inputs *later*");
750                 nestedVolumeOutputs = nestedHeatStack.getOutputs();
751             }
752         }
753
754         // handle a nestedBaseStackId if sent- this is the stack ID of the base. Should be null for VNF requests
755         StackInfo nestedBaseHeatStack = null;
756         Map<String, Object> baseStackOutputs = null;
757         if (nestedBaseStackId != null) {
758             try {
759                 logger.debug("Querying for nestedBaseStackId = {}", nestedBaseStackId);
760                 nestedBaseHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedBaseStackId);
761             } catch (MsoException me) {
762                 // Failed to query the Stack due to an openstack exception.
763                 // Convert to a generic VnfException
764                 me.addContext(CREATE_VFM_MODULE);
765                 String error = "Create VFModule: Attached baseHeatStack ID Query " + nestedBaseStackId + " in "
766                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
767                 logger.error(LoggingAnchor.NINE, MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner,
768                         cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.BusinessProcesssError.getValue(),
769                         "MsoException trying to query nested base stack", me);
770                 logger.debug("ERROR trying to query nested base stack= {}", error);
771                 throw new VnfException(me);
772             }
773             if (nestedBaseHeatStack == null || nestedBaseHeatStack.getStatus() == HeatStatus.NOTFOUND) {
774                 String error = "Create VFModule: Attached base heatStack ID DOES NOT EXIST " + nestedBaseStackId
775                         + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " " + USER_ERROR;
776                 logger.error(LoggingAnchor.TEN, MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner,
777                         cloudSiteId, tenantId, error, OPENSTACK, QUERY_STACK,
778                         ErrorCode.BusinessProcesssError.getValue(),
779                         "Create VFModule: Attached base heatStack ID DOES NOT EXIST");
780                 logger.debug("Exception occurred", error);
781                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
782             } else {
783                 logger.debug("Found nested base heat stack - these values will be copied to inputs *later*");
784                 baseStackOutputs = nestedBaseHeatStack.getOutputs();
785             }
786         }
787
788         try {
789             // Retrieve the VF
790             VfModule vf = null;
791             VnfResource vnfResource = null;
792             VfModuleCustomization vfmc = null;
793             if (useMCUuid) {
794                 vfmc = vfModuleCustomRepo.findFirstByModelCustomizationUUIDOrderByCreatedDesc(mcu);
795                 if (vfmc != null)
796                     vf = vfmc.getVfModule();
797                 else
798                     vf = null;
799
800                 // this will be the new way going forward. We find the vf by mcu - otherwise, code is the same.
801                 if (vf == null) {
802                     logger.debug("Unable to find vfModuleCust with modelCustomizationUuid={}", mcu);
803                     String error =
804                             "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid=" + mcu;
805                     logger.error(LoggingAnchor.SIX, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
806                             "VF Module ModelCustomizationUuid", modelCustomizationUuid, OPENSTACK,
807                             ErrorCode.DataError.getValue(),
808                             "Create VF Module: Unable to find vfModule with " + "modelCustomizationUuid=" + mcu);
809                     logger.debug(error);
810                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
811                 } else {
812                     logger.trace("Found vfModuleCust entry {}", vfmc.toString());
813                 }
814                 if (vf.getIsBase()) {
815                     isBaseRequest = true;
816                     logger.debug("This is a BASE VF request!");
817                 } else {
818                     logger.debug("This is *not* a BASE VF request!");
819                     if (!isVolumeRequest && nestedBaseStackId == null) {
820                         logger.debug(
821                                 "DANGER WILL ROBINSON! This is unexpected - no nestedBaseStackId with this non-base request");
822                     }
823                 }
824             }
825
826             else { // This is to support gamma only - get info from vnf_resource table
827                 if (vfVersion != null && !vfVersion.isEmpty()) {
828                     vnfResource = vnfResourceRepo.findByModelNameAndModelVersion(vnfType, vnfVersion);
829                 } else {
830                     vnfResource = vnfResourceRepo.findByModelName(vnfType);
831                 }
832                 if (vnfResource == null) {
833                     String error = "Create VNF: Unknown VNF Type: " + vnfType;
834                     logger.error(LoggingAnchor.SIX, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "VNF Type", vnfType,
835                             OPENSTACK, ErrorCode.DataError.getValue(), "Create VNF: Unknown VNF Type");
836                     logger.debug(error);
837                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
838                 }
839                 logger.debug("Got VNF module definition from Catalog: {}", vnfResource.toString());
840             }
841             // By here - we have either a vf or vnfResource
842
843             // Add version check
844             // First - see if it's in the VnfResource record
845             // if we have a vf Module - then we have to query to get the VnfResource record.
846             if (!oldWay) {
847                 if (vf != null) {
848                     vnfResource = vf.getVnfResources();
849                 }
850                 if (vnfResource == null) {
851                     logger.debug("Unable to find vnfResource will not error for now...");
852                 }
853             }
854             String minVersionVnf = null;
855             String maxVersionVnf = null;
856             if (vnfResource != null) {
857                 try {
858                     minVersionVnf = vnfResource.getAicVersionMin();
859                     maxVersionVnf = vnfResource.getAicVersionMax();
860                 } catch (Exception e) {
861                     logger.debug("Unable to pull min/max version for this VNF Resource entry", e);
862                     minVersionVnf = null;
863                     maxVersionVnf = null;
864                 }
865                 if (minVersionVnf != null && "".equals(minVersionVnf)) {
866                     minVersionVnf = null;
867                 }
868                 if (maxVersionVnf != null && "".equals(maxVersionVnf)) {
869                     maxVersionVnf = null;
870                 }
871             }
872             if (minVersionVnf != null && maxVersionVnf != null) {
873                 MavenLikeVersioning aicV = new MavenLikeVersioning();
874
875                 // double check
876                 if (this.cloudConfig != null) {
877                     Optional<CloudSite> cloudSiteOpt = this.cloudConfig.getCloudSite(cloudSiteId);
878                     if (cloudSiteOpt.isPresent()) {
879                         aicV.setVersion(cloudSiteOpt.get().getCloudVersion());
880                         // Add code to handle unexpected values in here
881                         boolean moreThanMin = true;
882                         boolean equalToMin = true;
883                         boolean moreThanMax = true;
884                         boolean equalToMax = true;
885                         boolean doNotTest = false;
886                         try {
887                             moreThanMin = aicV.isMoreRecentThan(minVersionVnf);
888                             equalToMin = aicV.isTheSameVersion(minVersionVnf);
889                             moreThanMax = aicV.isMoreRecentThan(maxVersionVnf);
890                             equalToMax = aicV.isTheSameVersion(maxVersionVnf);
891                         } catch (Exception e) {
892                             logger.debug(
893                                     "An exception occurred while trying to test Cloud Version {} - will default to not check",
894                                     e.getMessage(), e);
895                             doNotTest = true;
896                         }
897                         if (!doNotTest) {
898                             if ((moreThanMin || equalToMin) // aic >= min
899                                     && (equalToMax || !(moreThanMax))) { // aic <= max
900                                 logger.debug("VNF Resource " + vnfResource.getModelName() + ", ModelUuid="
901                                         + vnfResource.getModelUUID() + " " + VERSION_MIN + " =" + minVersionVnf + " "
902                                         + VERSION_MAX + " :" + maxVersionVnf + " supported on Cloud: " + cloudSiteId
903                                         + " with AIC_Version:" + cloudSiteOpt.get().getCloudVersion());
904                             } else {
905                                 // ERROR
906                                 String error = "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid="
907                                         + vnfResource.getModelUUID() + " " + VERSION_MIN + " =" + minVersionVnf + " "
908                                         + VERSION_MAX + " :" + maxVersionVnf + " NOT supported on Cloud: " + cloudSiteId
909                                         + " with AIC_Version:" + cloudSiteOpt.get().getCloudVersion();
910                                 logger.error(LoggingAnchor.FIVE, MessageEnum.RA_CONFIG_EXC.toString(), error, OPENSTACK,
911                                         ErrorCode.BusinessProcesssError.getValue(), "Exception - setVersion");
912                                 logger.debug(error);
913                                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
914                             }
915                         } else {
916                             logger.debug("bypassing testing Cloud version...");
917                         }
918                     } // let this error out downstream to avoid introducing uncertainty at this stage
919                 } else {
920                     logger.debug("cloudConfig is NULL - cannot check cloud site version");
921                 }
922             }
923
924             // By the time we get here - heatTemplateId and heatEnvtId should be populated (or null)
925             HeatTemplate heatTemplate = null;
926             HeatEnvironment heatEnvironment = null;
927             if (oldWay) {
928                 // This will handle old Gamma BrocadeVCE VNF
929                 heatTemplate = vnfResource.getHeatTemplates();
930             } else {
931                 if (vf != null) {
932                     if (isVolumeRequest) {
933                         heatTemplate = vf.getVolumeHeatTemplate();
934                         heatEnvironment = vfmc.getVolumeHeatEnv();
935                     } else {
936                         heatTemplate = vf.getModuleHeatTemplate();
937                         heatEnvironment = vfmc.getHeatEnvironment();
938                     }
939                 }
940             }
941
942             if (heatTemplate == null) {
943                 String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType
944                         + ", modelCustomizationUuid=" + mcu + ", vfModuleUuid="
945                         + (vf != null ? vf.getModelUUID() : "null") + ", vnfResourceModelUuid="
946                         + vnfResource.getModelUUID() + ", reqType=" + requestTypeString;
947                 logger.error(LoggingAnchor.SIX, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template " + "ID",
948                         vfModuleType, OPENSTACK, ErrorCode.DataError.getValue(), error);
949                 logger.debug(error);
950                 throw new VnfException(error, MsoExceptionCategory.INTERNAL);
951             } else {
952                 logger.debug("Got HEAT Template from DB: {}", heatTemplate.getHeatTemplate());
953             }
954
955             if (oldWay) {
956                 // This will handle old Gamma BrocadeVCE VNF
957                 logger.debug("No environment parameter found for this Type " + vfModuleType);
958             } else {
959                 if (heatEnvironment == null) {
960                     String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType
961                             + ", modelCustomizationUuid=" + mcu + ", vfModuleUuid="
962                             + (vf != null ? vf.getModelUUID() : "null") + ", vnfResourceModelUuid="
963                             + vnfResource.getModelUUID() + ", reqType=" + requestTypeString;
964                     logger.error(LoggingAnchor.FIVE, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
965                             "Heat " + "Environment ID", OPENSTACK, ErrorCode.DataError.getValue(), error);
966                     logger.debug(error);
967                     throw new VnfException(error, MsoExceptionCategory.INTERNAL);
968                 } else {
969                     logger.debug("Got Heat Environment from DB: {}", heatEnvironment.getEnvironment());
970                 }
971             }
972
973             logger.debug("In MsoVnfAdapterImpl, about to call db.getNestedTemplates avec templateId="
974                     + heatTemplate.getArtifactUuid());
975
976
977             List<HeatTemplate> nestedTemplates = heatTemplate.getChildTemplates();
978             Map<String, Object> nestedTemplatesChecked = new HashMap<>();
979             if (nestedTemplates != null && !nestedTemplates.isEmpty()) {
980                 // for debugging print them out
981                 logger.debug("Contents of nestedTemplates - to be added to files: on stack:");
982                 for (HeatTemplate entry : nestedTemplates) {
983                     nestedTemplatesChecked.put(entry.getTemplateName(), entry.getTemplateBody());
984                     logger.debug(entry.getTemplateName() + " -> " + entry.getTemplateBody());
985                 }
986             } else {
987                 logger.debug("No nested templates found - nothing to do here");
988                 nestedTemplatesChecked = null;
989             }
990
991             // Also add the files: for any get_files associated with this vnf_resource_id
992             // *if* there are any
993             List<HeatFiles> heatFiles = null;
994
995             Map<String, Object> heatFilesObjects = new HashMap<>();
996
997             // Add ability to turn on adding get_files with volume requests (by property).
998             boolean addGetFilesOnVolumeReq = false;
999             try {
1000                 String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ);
1001                 if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
1002                     addGetFilesOnVolumeReq = true;
1003                     logger.debug("AddGetFilesOnVolumeReq - setting to true! {}", propertyString);
1004                 }
1005             } catch (Exception e) {
1006                 logger.debug("An error occured trying to get property " + MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ
1007                         + " - default to false", e);
1008             }
1009
1010             if (!isVolumeRequest || addGetFilesOnVolumeReq) {
1011                 if (oldWay) {
1012                     logger.debug("In MsoVnfAdapterImpl createVfModule, this should not happen, no heat files!");
1013                 } else {
1014                     // now use VF_MODULE_TO_HEAT_FILES table
1015                     logger.debug(
1016                             "In MsoVnfAdapterImpl createVfModule, about to call db.getHeatFilesForVfModule avec vfModuleId="
1017                                     + vf.getModelUUID());
1018                     heatFiles = vf.getHeatFiles();
1019                 }
1020                 if (heatFiles != null && !heatFiles.isEmpty()) {
1021                     // add these to stack - to be done in createStack
1022                     // here, we will map them to Map<String, Object> from
1023                     // Map<String, HeatFiles>
1024                     // this will match the nested templates format
1025                     logger.debug("Contents of heatFiles - to be added to files: on stack");
1026
1027                     for (HeatFiles heatfile : heatFiles) {
1028                         logger.debug(heatfile.getFileName() + " -> " + heatfile.getFileBody());
1029                         heatFilesObjects.put(heatfile.getFileName(), heatfile.getFileBody());
1030                     }
1031                 } else {
1032                     logger.debug("No heat files found -nothing to do here");
1033                     heatFilesObjects = null;
1034                 }
1035             }
1036
1037             // Check that required parameters have been supplied
1038             String missingParams = null;
1039             List<String> paramList = new ArrayList<>();
1040
1041             // consult the PARAM_ALIAS field to see if we've been
1042             // supplied an alias. Only check if we don't find it initially.
1043             // don't flag missing parameters if there's an environment - because they might be there.
1044             // And also new - add parameter to turn off checking all together if we find we're blocking orders we
1045             // shouldn't
1046             boolean checkRequiredParameters = true;
1047             try {
1048                 String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1049                 if ("false".equalsIgnoreCase(propertyString) || "n".equalsIgnoreCase(propertyString)) {
1050                     checkRequiredParameters = false;
1051                     logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking..."
1052                             + MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1053                 }
1054             } catch (Exception e) {
1055                 // No problem - default is true
1056                 logger.debug("An exception occured trying to get property {}", MsoVnfAdapterImpl.CHECK_REQD_PARAMS, e);
1057             }
1058             // Part 1: parse envt entries to see if reqd parameter is there (before used a simple grep
1059             // Part 2: only submit to openstack the parameters in the envt that are in the heat template
1060             // Note this also removes any comments
1061             MsoHeatEnvironmentEntry mhee = null;
1062             if (heatEnvironment != null && heatEnvironment.getEnvironment() != null
1063                     && heatEnvironment.getEnvironment().contains("parameters:")) {
1064
1065                 StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
1066
1067                 mhee = new MsoHeatEnvironmentEntry(sb);
1068                 StringBuilder sb2 = new StringBuilder("\nHeat Template Parameters:\n");
1069                 for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1070                     sb2.append("\t" + parm.getParamName() + ", required=" + parm.isRequired());
1071                 }
1072                 if (!mhee.isValid()) {
1073                     sb2.append("Environment says it's not valid! " + mhee.getErrorString());
1074                 } else {
1075                     sb2.append("\nEnvironment:");
1076                     sb2.append(mhee.toFullString());
1077                 }
1078                 logger.debug(sb2.toString());
1079             } else {
1080                 logger.debug("NO ENVIRONMENT for this entry");
1081             }
1082             // all variables converted to their native object types
1083             Map<String, Object> goldenInputs = null;
1084
1085             ArrayList<String> parameterNames = new ArrayList<>();
1086             HashMap<String, String> aliasToParam = new HashMap<>();
1087             StringBuilder sb = new StringBuilder("\nTemplate Parameters:\n");
1088             int cntr = 0;
1089             try {
1090                 for (HeatTemplateParam htp : heatTemplate.getParameters()) {
1091                     sb.append("param[" + cntr++ + "]=" + htp.getParamName());
1092                     parameterNames.add(htp.getParamName());
1093                     if (htp.getParamAlias() != null && !"".equals(htp.getParamAlias())) {
1094                         aliasToParam.put(htp.getParamAlias(), htp.getParamName());
1095                         sb.append(" ** (alias=" + htp.getParamAlias() + ")");
1096                     }
1097                     sb.append("\n");
1098                 }
1099                 logger.debug(sb.toString());
1100             } catch (Exception e) {
1101                 logger.debug("??An exception occurred trying to go through Parameter Names {}", e.getMessage(), e);
1102             }
1103             // Step 1 - convert what we got as inputs (Map<String, String>) to a
1104             // Map<String, Object> - where the object matches the param type identified in the template
1105             // This will also not copy over params that aren't identified in the template
1106             goldenInputs = heat.convertInputMap(inputs, heatTemplate);
1107             // Step 2 - now simply add the outputs as we received them - no need to convert to string
1108             logger.debug("Now add in the base stack outputs if applicable");
1109             heat.copyBaseOutputsToInputs(goldenInputs, baseStackOutputs, parameterNames, aliasToParam);
1110             // Step 3 - add the volume inputs if any
1111             logger.debug("Now add in the volume stack outputs if applicable");
1112             heat.copyBaseOutputsToInputs(goldenInputs, nestedVolumeOutputs, parameterNames, aliasToParam);
1113
1114             for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1115                 logger.debug("Parameter:'" + parm.getParamName() + "', isRequired=" + parm.isRequired() + ", alias="
1116                         + parm.getParamAlias());
1117
1118                 if (parm.isRequired() && (goldenInputs == null || !goldenInputs.containsKey(parm.getParamName()))) {
1119                     // The check for an alias was moved to the method in MsoHeatUtils - when we converted the
1120                     // Map<String, String> to Map<String, Object>
1121                     logger.debug("**Parameter " + parm.getParamName() + " is required and not in the inputs...check "
1122                             + "environment");
1123                     if (mhee != null && mhee.containsParameter(parm.getParamName())) {
1124                         logger.debug("Required parameter {} appears to be in environment - do not count as missing",
1125                                 parm.getParamName());
1126                     } else {
1127                         logger.debug("adding to missing parameters list: {}", parm.getParamName());
1128                         if (missingParams == null) {
1129                             missingParams = parm.getParamName();
1130                         } else {
1131                             missingParams += "," + parm.getParamName();
1132                         }
1133                     }
1134                 }
1135                 paramList.add(parm.getParamName());
1136             }
1137             if (missingParams != null) {
1138                 if (checkRequiredParameters) {
1139                     // Problem - missing one or more required parameters
1140                     String error = "Create VFModule: Missing Required inputs: " + missingParams;
1141                     logger.error(LoggingAnchor.FIVE, MessageEnum.RA_MISSING_PARAM.toString(), missingParams, OPENSTACK,
1142                             ErrorCode.DataError.getValue(), "Create VFModule: Missing Required inputs");
1143                     logger.debug(error);
1144                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
1145                 } else {
1146                     logger.debug("found missing parameters - but checkRequiredParameters is false - will not block");
1147                 }
1148             } else {
1149                 logger.debug("No missing parameters found - ok to proceed");
1150             }
1151             // We can now remove the recreating of the ENV with only legit params - that check is done for us,
1152             // and it causes problems with json that has arrays
1153             String newEnvironmentString = null;
1154             if (mhee != null) {
1155                 newEnvironmentString = mhee.getRawEntry().toString();
1156             }
1157
1158             // "Fix" the template if it has CR/LF (getting this from Oracle)
1159             String template = heatTemplate.getHeatTemplate();
1160             template = template.replaceAll("\r\n", "\n");
1161
1162             // Valet - 1806
1163             boolean isValetEnabled = this.checkBooleanProperty(MsoVnfAdapterImpl.VALET_ENABLED, false);
1164             boolean failRequestOnValetFailure =
1165                     this.checkBooleanProperty(MsoVnfAdapterImpl.FAIL_REQUESTS_ON_VALET_FAILURE, false);
1166             logger.debug("isValetEnabled={}, failRequestsOnValetFailure={}", isValetEnabled, failRequestOnValetFailure);
1167             if (oldWay || isVolumeRequest) {
1168                 isValetEnabled = false;
1169                 logger.debug("do not send to valet for volume requests or brocade");
1170             }
1171             boolean sendResponseToValet = false;
1172             if (isValetEnabled) {
1173                 Holder<Map<String, Object>> valetModifiedParamsHolder = new Holder<>();
1174                 sendResponseToValet = this.valetCreateRequest(cloudSiteId, cloudOwner, tenantId, heatFilesObjects,
1175                         nestedTemplatesChecked, vfModuleName, backout, heatTemplate, newEnvironmentString, goldenInputs,
1176                         msoRequest, inputs, failRequestOnValetFailure, valetModifiedParamsHolder);
1177                 if (sendResponseToValet) {
1178                     goldenInputs = valetModifiedParamsHolder.value;
1179                 }
1180             }
1181
1182             // Have the tenant. Now deploy the stack itself
1183             // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
1184             // because we already checked for those.
1185             try {
1186                 if (backout == null) {
1187                     backout = true;
1188                 }
1189                 if (heat != null) {
1190
1191                     heatStack = heat.createStack(cloudSiteId, cloudOwner, tenantId, vfModuleName, null, template,
1192                             goldenInputs, true, heatTemplate.getTimeoutMinutes(), newEnvironmentString,
1193                             nestedTemplatesChecked, heatFilesObjects, backout.booleanValue());
1194                 } else {
1195                     throw new MsoHeatNotFoundException();
1196                 }
1197             } catch (MsoException me) {
1198                 me.addContext(CREATE_VFM_MODULE);
1199                 logger.error("Error creating Stack", me);
1200                 if (isValetEnabled && sendResponseToValet) {
1201                     logger.debug("valet is enabled, the orchestration failed - now sending rollback to valet");
1202                     try {
1203                         GenericValetResponse<ValetRollbackResponse> gvr = this.vci
1204                                 .callValetRollbackRequest(msoRequest.getRequestId(), null, backout, me.getMessage());
1205                         // Nothing to really do here whether it succeeded or not other than log it.
1206                         logger.debug("Return code from Rollback response is {}", gvr.getStatusCode());
1207                     } catch (Exception e) {
1208                         logger.error("Exception encountered while sending Rollback to Valet ", e);
1209                     }
1210                 }
1211                 throw new VnfException(me);
1212             } catch (NullPointerException npe) {
1213                 logger.error("Error creating Stack", npe);
1214                 throw new VnfException("NullPointerException during heat.createStack");
1215             } catch (Exception e) {
1216                 logger.error("Error creating Stack", e);
1217                 throw new VnfException("Exception during heat.createStack! " + e.getMessage());
1218             }
1219             // Reach this point if createStack is successful.
1220             // Populate remaining rollback info and response parameters.
1221             vfRollback.setVnfId(heatStack.getCanonicalName());
1222             vfRollback.setVnfCreated(true);
1223
1224             vnfId.value = heatStack.getCanonicalName();
1225             outputs.value = copyStringOutputs(heatStack.getOutputs());
1226             rollback.value = vfRollback;
1227             if (isValetEnabled && sendResponseToValet) {
1228                 logger.debug("valet is enabled, the orchestration succeeded - now send confirm to valet with stack id");
1229                 try {
1230                     GenericValetResponse<ValetConfirmResponse> gvr =
1231                             this.vci.callValetConfirmRequest(msoRequest.getRequestId(), heatStack.getCanonicalName());
1232                     // Nothing to really do here whether it succeeded or not other than log it.
1233                     logger.debug("Return code from Confirm response is {}", gvr.getStatusCode());
1234                 } catch (Exception e) {
1235                     logger.error("Exception encountered while sending Confirm to Valet ", e);
1236                 }
1237             }
1238             logger.debug("VF Module {} successfully created", vfModuleName);
1239             // call heatbridge
1240             heatbridge(heatStack, cloudOwner, cloudSiteId, tenantId, genericVnfName, vfModuleId);
1241         } catch (Exception e) {
1242             logger.debug("unhandled exception in create VF", e);
1243             throw new VnfException("Exception during create VF " + e.getMessage());
1244         }
1245     }
1246
1247     @Override
1248     public void deleteVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vnfName,
1249             MsoRequest msoRequest, Holder<Map<String, String>> outputs) throws VnfException {
1250
1251         logger.debug("Deleting VF {} in ", vnfName, cloudOwner + "/" + cloudSiteId + "/" + tenantId);
1252         // Will capture execution time for metrics
1253
1254         // 1702 capture the output parameters on a delete
1255         // so we'll need to query first
1256         Map<String, Object> stackOutputs;
1257         try {
1258             stackOutputs = heat.queryStackForOutputs(cloudSiteId, cloudOwner, tenantId, vnfName);
1259         } catch (MsoException me) {
1260             // Failed to query the Stack due to an openstack exception.
1261             // Convert to a generic VnfException
1262             me.addContext("DeleteVFModule");
1263             String error = "Delete VFModule: Query to get outputs: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId
1264                     + "/" + tenantId + ": " + me;
1265             logger.error(LoggingAnchor.NINE, MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId,
1266                     tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(), "Exception - " + QUERY_STACK, me);
1267             logger.debug(error);
1268             throw new VnfException(me);
1269         }
1270         // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected
1271         // Object types
1272         outputs.value = this.convertMapStringObjectToStringString(stackOutputs);
1273
1274         boolean isValetEnabled = this.checkBooleanProperty(MsoVnfAdapterImpl.VALET_ENABLED, false);
1275         boolean failRequestOnValetFailure =
1276                 this.checkBooleanProperty(MsoVnfAdapterImpl.FAIL_REQUESTS_ON_VALET_FAILURE, false);
1277         logger.debug("isValetEnabled={}, failRequestsOnValetFailure={}", isValetEnabled, failRequestOnValetFailure);
1278         boolean valetDeleteRequestSucceeded = false;
1279         if (isValetEnabled) {
1280             valetDeleteRequestSucceeded = this.valetDeleteRequest(cloudSiteId, cloudOwner, tenantId, vnfName,
1281                     msoRequest, failRequestOnValetFailure);
1282         }
1283
1284         try {
1285             heat.deleteStack(tenantId, cloudOwner, cloudSiteId, vnfName, true);
1286         } catch (MsoException me) {
1287             me.addContext(DELETE_VNF);
1288             // Failed to query the Stack due to an openstack exception.
1289             // Convert to a generic VnfException
1290             String error =
1291                     "Delete VF: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1292             logger.error(LoggingAnchor.NINE, MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId,
1293                     tenantId, OPENSTACK, "DeleteStack", ErrorCode.DataError.getValue(), "Exception - deleteStack", me);
1294             logger.debug(error);
1295             if (isValetEnabled && valetDeleteRequestSucceeded) {
1296                 logger.debug("valet is enabled, the orchestration failed - now sending rollback to valet");
1297                 try {
1298                     GenericValetResponse<ValetRollbackResponse> gvr = this.vci
1299                             .callValetRollbackRequest(msoRequest.getRequestId(), vnfName, false, me.getMessage());
1300                     // Nothing to really do here whether it succeeded or not other than log it.
1301                     logger.debug("Return code from Rollback response is {}", gvr.getStatusCode());
1302                 } catch (Exception e) {
1303                     logger.error("Exception encountered while sending Rollback to Valet ", e);
1304                 }
1305             }
1306             throw new VnfException(me);
1307         }
1308         if (isValetEnabled && valetDeleteRequestSucceeded) {
1309             // only if the original request succeeded do we send a confirm
1310             logger.debug("valet is enabled, the delete succeeded - now send confirm to valet");
1311             try {
1312                 GenericValetResponse<ValetConfirmResponse> gvr =
1313                         this.vci.callValetConfirmRequest(msoRequest.getRequestId(), vnfName);
1314                 // Nothing to really do here whether it succeeded or not other than log it.
1315                 logger.debug("Return code from Confirm response is {}", gvr.getStatusCode());
1316             } catch (Exception e) {
1317                 logger.error("Exception encountered while sending Confirm to Valet ", e);
1318             }
1319         }
1320
1321         // On success, nothing is returned.
1322         return;
1323     }
1324
1325     @Override
1326     public void updateVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vnfType,
1327             String vnfVersion, String vnfName, String requestType, String volumeGroupHeatStackId,
1328             String baseVfHeatStackId, String vfModuleStackId, String modelCustomizationUuid, Map<String, Object> inputs,
1329             MsoRequest msoRequest, Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback)
1330             throws VnfException {
1331         String vfModuleName = vnfName;
1332         String vfModuleType = vnfType;
1333         String methodName = "updateVfModule";
1334         String serviceName = VNF_ADAPTER_SERVICE_NAME + methodName;
1335
1336         StringBuilder sbInit = new StringBuilder();
1337         sbInit.append("updateVfModule: \n");
1338         sbInit.append("cloudOwner=" + cloudOwner + "\n");
1339         sbInit.append("cloudSiteId=" + cloudSiteId + "\n");
1340         sbInit.append("tenantId=" + tenantId + "\n");
1341         sbInit.append("vnfType=" + vnfType + "\n");
1342         sbInit.append("vnfVersion=" + vnfVersion + "\n");
1343         sbInit.append("vnfName=" + vnfName + "\n");
1344         sbInit.append("requestType=" + requestType + "\n");
1345         sbInit.append("volumeGroupHeatStackId=" + volumeGroupHeatStackId + "\n");
1346         sbInit.append("baseVfHeatStackId=" + baseVfHeatStackId + "\n");
1347         sbInit.append("vfModuleStackId=" + vfModuleStackId + "\n");
1348         sbInit.append("modelCustomizationUuid=" + modelCustomizationUuid + "\n");
1349         logger.debug(sbInit.toString());
1350
1351         String mcu = modelCustomizationUuid;
1352         boolean useMCUuid = false;
1353         if (mcu != null && !mcu.isEmpty()) {
1354             if ("null".equalsIgnoreCase(mcu)) {
1355                 logger.debug("modelCustomizationUuid: passed in as the string 'null' - will ignore: {}",
1356                         modelCustomizationUuid);
1357                 useMCUuid = false;
1358                 mcu = "";
1359             } else {
1360                 logger.debug("Found modelCustomizationUuid! Will use that: {}", mcu);
1361                 useMCUuid = true;
1362             }
1363         }
1364
1365         String requestTypeString = "";
1366         if (requestType != null && !"".equals(requestType)) {
1367             requestTypeString = requestType;
1368         }
1369
1370         String nestedStackId = null;
1371         if (volumeGroupHeatStackId != null && !"".equals(volumeGroupHeatStackId)
1372                 && !"null".equalsIgnoreCase(volumeGroupHeatStackId)) {
1373             nestedStackId = volumeGroupHeatStackId;
1374         }
1375         String nestedBaseStackId = null;
1376         if (baseVfHeatStackId != null && !"".equals(baseVfHeatStackId) && !"null".equalsIgnoreCase(baseVfHeatStackId)) {
1377             nestedBaseStackId = baseVfHeatStackId;
1378         }
1379
1380         if (inputs == null) {
1381             // Create an empty set of inputs
1382             inputs = new HashMap<>();
1383             logger.debug("inputs == null - setting to empty");
1384         }
1385
1386         boolean isBaseRequest = false;
1387         boolean isVolumeRequest = false;
1388         if (requestTypeString.startsWith("VOLUME")) {
1389             isVolumeRequest = true;
1390         }
1391         if ((vfModuleName == null || "".equals(vfModuleName.trim())) && vfModuleStackId != null) {
1392             vfModuleName = this.getVfModuleNameFromModuleStackId(vfModuleStackId);
1393         }
1394
1395         logger.debug("Updating VFModule: " + vfModuleName + " of type " + vfModuleType + "in " + cloudOwner + "/"
1396                 + cloudSiteId + "/" + tenantId);
1397         logger.debug("requestTypeString = " + requestTypeString + ", nestedVolumeStackId = " + nestedStackId
1398                 + ", nestedBaseStackId = " + nestedBaseStackId);
1399
1400         // Build a default rollback object (no actions performed)
1401         VnfRollback vfRollback = new VnfRollback();
1402         vfRollback.setCloudSiteId(cloudSiteId);
1403         vfRollback.setCloudOwner(cloudOwner);
1404         vfRollback.setTenantId(tenantId);
1405         vfRollback.setMsoRequest(msoRequest);
1406         vfRollback.setRequestType(requestTypeString);
1407         vfRollback.setVolumeGroupHeatStackId(volumeGroupHeatStackId);
1408         vfRollback.setBaseGroupHeatStackId(baseVfHeatStackId);
1409         vfRollback.setIsBase(isBaseRequest);
1410         vfRollback.setVfModuleStackId(vfModuleStackId);
1411         vfRollback.setModelCustomizationUuid(mcu);
1412
1413         StackInfo heatStack;
1414         logger.debug("UpdateVfModule - querying for {}", vfModuleName);
1415         try {
1416             heatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, vfModuleName);
1417         } catch (MsoException me) {
1418             // Failed to query the Stack due to an openstack exception.
1419             // Convert to a generic VnfException
1420             me.addContext("UpdateVFModule");
1421             String error = "Update VFModule: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/"
1422                     + tenantId + ": " + me;
1423             logger.error(LoggingAnchor.NINE, MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner,
1424                     cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(),
1425                     "Exception - " + QUERY_STACK, me);
1426             logger.debug(error);
1427             throw new VnfException(me);
1428         }
1429
1430         // TODO - do we need to check for the other status possibilities?
1431         if (heatStack == null || heatStack.getStatus() == HeatStatus.NOTFOUND) {
1432             // Not Found
1433             String error = "Update VF: Stack " + vfModuleName + " does not exist in " + cloudOwner + "/" + cloudSiteId
1434                     + "/" + tenantId;
1435             logger.error(LoggingAnchor.NINE, MessageEnum.RA_VNF_NOT_EXIST.toString(), vfModuleName, cloudOwner,
1436                     cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(), error);
1437             throw new VnfNotFound(cloudSiteId, cloudOwner, tenantId, vfModuleName);
1438         } else {
1439             logger.debug("Found Existing stack, status={}", heatStack.getStatus());
1440             // Populate the outputs from the existing stack.
1441             outputs.value = copyStringOutputs(heatStack.getOutputs());
1442             rollback.value = vfRollback; // Default rollback - no updates performed
1443         }
1444
1445         // 1604 Cinder Volume support - handle a nestedStackId if sent (volumeGroupHeatStackId):
1446         StackInfo nestedHeatStack = null;
1447         Map<String, Object> nestedVolumeOutputs = null;
1448         if (nestedStackId != null) {
1449             try {
1450                 logger.debug("Querying for nestedStackId = {}", nestedStackId);
1451                 nestedHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedStackId);
1452             } catch (MsoException me) {
1453                 // Failed to query the Stack due to an openstack exception.
1454                 // Convert to a generic VnfException
1455                 me.addContext("UpdateVFModule");
1456                 String error = "Update VF: Attached heatStack ID Query " + nestedStackId + " in " + cloudOwner + "/"
1457                         + cloudSiteId + "/" + tenantId + ": " + me;
1458                 logger.error(LoggingAnchor.NINE, MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner,
1459                         cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(),
1460                         "Exception - " + error, me);
1461                 logger.debug("ERROR trying to query nested stack= {}", error);
1462                 throw new VnfException(me);
1463             }
1464             if (nestedHeatStack == null || nestedHeatStack.getStatus() == HeatStatus.NOTFOUND) {
1465                 String error = "Update VFModule: Attached volume heatStack ID DOES NOT EXIST " + nestedStackId + " in "
1466                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " " + USER_ERROR;
1467                 logger.error(LoggingAnchor.TEN, MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner,
1468                         cloudSiteId, tenantId, error, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(), error);
1469                 logger.debug(error);
1470                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
1471             } else {
1472                 logger.debug("Found nested heat stack - copying values to inputs *later*");
1473                 nestedVolumeOutputs = nestedHeatStack.getOutputs();
1474                 heat.copyStringOutputsToInputs(inputs, nestedHeatStack.getOutputs(), false);
1475             }
1476         }
1477         // handle a nestedBaseStackId if sent - this is the stack ID of the base.
1478         StackInfo nestedBaseHeatStack = null;
1479         Map<String, Object> baseStackOutputs = null;
1480         if (nestedBaseStackId != null) {
1481             try {
1482                 logger.debug("Querying for nestedBaseStackId = {}", nestedBaseStackId);
1483                 nestedBaseHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedBaseStackId);
1484             } catch (MsoException me) {
1485                 // Failed to query the Stack due to an openstack exception.
1486                 // Convert to a generic VnfException
1487                 me.addContext("UpdateVfModule");
1488                 String error = "Update VFModule: Attached baseHeatStack ID Query " + nestedBaseStackId + " in "
1489                         + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1490                 logger.error(LoggingAnchor.NINE, MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner,
1491                         cloudSiteId, tenantId, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(),
1492                         "Exception - " + error, me);
1493                 logger.debug("ERROR trying to query nested base stack= {}", error);
1494                 throw new VnfException(me);
1495             }
1496             if (nestedBaseHeatStack == null || nestedBaseHeatStack.getStatus() == HeatStatus.NOTFOUND) {
1497                 String error = "Update VFModule: Attached base heatStack ID DOES NOT EXIST " + nestedBaseStackId
1498                         + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " " + USER_ERROR;
1499                 logger.error(LoggingAnchor.TEN, MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner,
1500                         cloudSiteId, tenantId, error, OPENSTACK, QUERY_STACK, ErrorCode.DataError.getValue(), error);
1501                 logger.debug(error);
1502                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
1503             } else {
1504                 logger.debug("Found nested base heat stack - copying values to inputs *later*");
1505                 baseStackOutputs = nestedBaseHeatStack.getOutputs();
1506                 heat.copyStringOutputsToInputs(inputs, nestedBaseHeatStack.getOutputs(), false);
1507             }
1508         }
1509
1510         // Ready to deploy the new VNF
1511
1512
1513
1514         // Retrieve the VF definition
1515         VnfResource vnfResource = null;
1516         VfModule vf = null;
1517         VfModuleCustomization vfmc = null;
1518         if (useMCUuid) {
1519             vfmc = vfModuleCustomRepo.findFirstByModelCustomizationUUIDOrderByCreatedDesc(modelCustomizationUuid);
1520             vf = vfmc != null ? vfmc.getVfModule() : null;
1521             if (vf == null) {
1522                 logger.debug("Unable to find a vfModule matching modelCustomizationUuid={}", mcu);
1523             }
1524         } else {
1525             logger.debug("1707 and later - MUST PROVIDE Model Customization UUID!");
1526         }
1527         if (vf == null) {
1528             String error = "Update VfModule: unable to find vfModule with modelCustomizationUuid=" + mcu;
1529             logger.error(LoggingAnchor.SIX, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "VF Module Type", vfModuleType,
1530                     OPENSTACK, ErrorCode.DataError.getValue(), error);
1531             throw new VnfException(error, MsoExceptionCategory.USERDATA);
1532         }
1533         logger.debug("Got VF module definition from Catalog: {}", vf.toString());
1534         if (vf.getIsBase()) {
1535             isBaseRequest = true;
1536             logger.debug("This a BASE update request");
1537         } else {
1538             logger.debug("This is *not* a BASE VF update request");
1539             if (!isVolumeRequest && nestedBaseStackId == null) {
1540                 logger.debug(
1541                         "DANGER WILL ROBINSON! This is unexpected - no nestedBaseStackId with this non-base request");
1542             }
1543         }
1544
1545         // 1607 - Add version check
1546         // First - see if it's in the VnfResource record
1547         // if we have a vf Module - then we have to query to get the VnfResource record.
1548         if (vf.getModelUUID() != null) {
1549             String vnfResourceModelUuid = vf.getModelUUID();
1550
1551             vnfResource = vf.getVnfResources();
1552             if (vnfResource == null) {
1553                 logger.debug("Unable to find vnfResource at ? will not error for now...", vnfResourceModelUuid);
1554             }
1555         }
1556
1557         String minVersionVnf = null;
1558         String maxVersionVnf = null;
1559         if (vnfResource != null) {
1560             try {
1561                 minVersionVnf = vnfResource.getAicVersionMin();
1562                 maxVersionVnf = vnfResource.getAicVersionMax();
1563             } catch (Exception e) {
1564                 logger.debug("Unable to pull min/max version for this VNF Resource entry", e);
1565                 minVersionVnf = null;
1566                 maxVersionVnf = null;
1567             }
1568             if (minVersionVnf != null && "".equals(minVersionVnf)) {
1569                 minVersionVnf = null;
1570             }
1571             if (maxVersionVnf != null && "".equals(maxVersionVnf)) {
1572                 maxVersionVnf = null;
1573             }
1574         }
1575         if (minVersionVnf != null && maxVersionVnf != null) {
1576             MavenLikeVersioning aicV = new MavenLikeVersioning();
1577
1578             // double check
1579             if (this.cloudConfig != null) {
1580                 Optional<CloudSite> cloudSiteOpt = this.cloudConfig.getCloudSite(cloudSiteId);
1581                 if (cloudSiteOpt.isPresent()) {
1582                     aicV.setVersion(cloudSiteOpt.get().getCloudVersion());
1583                     boolean moreThanMin = true;
1584                     boolean equalToMin = true;
1585                     boolean moreThanMax = true;
1586                     boolean equalToMax = true;
1587                     boolean doNotTest = false;
1588                     try {
1589                         moreThanMin = aicV.isMoreRecentThan(minVersionVnf);
1590                         equalToMin = aicV.isTheSameVersion(minVersionVnf);
1591                         moreThanMax = aicV.isMoreRecentThan(maxVersionVnf);
1592                         equalToMax = aicV.isTheSameVersion(maxVersionVnf);
1593                     } catch (Exception e) {
1594                         logger.debug(
1595                                 "An exception occured while trying to test AIC Version {} - will default to not check",
1596                                 e.getMessage(), e);
1597                         doNotTest = true;
1598                     }
1599                     if (!doNotTest) {
1600                         if ((moreThanMin || equalToMin) // aic >= min
1601                                 && ((equalToMax) || !(moreThanMax))) { // aic <= max
1602                             logger.debug("VNF Resource " + vnfResource.getModelName() + " " + VERSION_MIN + " ="
1603                                     + minVersionVnf + " " + VERSION_MAX + " :" + maxVersionVnf + " supported on Cloud: "
1604                                     + cloudSiteId + " with AIC_Version:" + aicV);
1605                         } else {
1606                             // ERROR
1607                             String error = "VNF Resource type: " + vnfResource.getModelName() + " " + VERSION_MIN + " ="
1608                                     + minVersionVnf + " " + VERSION_MAX + " :" + maxVersionVnf
1609                                     + " NOT supported on Cloud: " + cloudSiteId + " with AIC_Version:" + aicV;
1610                             logger.error(LoggingAnchor.FIVE, MessageEnum.RA_CONFIG_EXC.toString(), error, OPENSTACK,
1611                                     ErrorCode.BusinessProcesssError.getValue(), "Exception - setVersion");
1612                             logger.debug(error);
1613                             throw new VnfException(error, MsoExceptionCategory.USERDATA);
1614                         }
1615                     } else {
1616                         logger.debug("bypassing testing AIC version...");
1617                     }
1618                 } // let this error out downstream to avoid introducing uncertainty at this stage
1619             } else {
1620                 logger.debug("cloudConfig is NULL - cannot check cloud site version");
1621             }
1622
1623         } else {
1624             logger.debug("AIC Version not set in VNF_Resource - do not error for now - not checked.");
1625         }
1626         // End Version check 1607
1627
1628         HeatTemplate heatTemplate = null;
1629         HeatEnvironment heatEnvironment = null;
1630         if (isVolumeRequest) {
1631             heatTemplate = vf.getVolumeHeatTemplate();
1632             heatEnvironment = vfmc.getVolumeHeatEnv();
1633         } else {
1634             heatTemplate = vf.getModuleHeatTemplate();
1635             heatEnvironment = vfmc.getHeatEnvironment();
1636         }
1637
1638         if (heatTemplate == null) {
1639             String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType
1640                     + ", modelCustomizationUuid=" + mcu + ", vfModuleUuid=" + vf.getModelUUID() + ", reqType="
1641                     + requestTypeString;
1642             logger.error(LoggingAnchor.SIX, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID",
1643                     vfModuleType, OPENSTACK, ErrorCode.DataError.getValue(), error);
1644             throw new VnfException(error, MsoExceptionCategory.INTERNAL);
1645         } else {
1646             logger.debug("Got HEAT Template from DB: {}", heatTemplate.getHeatTemplate());
1647         }
1648
1649         if (heatEnvironment == null) {
1650             String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType + ", modelCustomizationUuid="
1651                     + mcu + ", vfModuleUuid=" + vf.getModelUUID() + ", reqType=" + requestTypeString;
1652             logger.error(LoggingAnchor.FIVE, MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID",
1653                     OPENSTACK, ErrorCode.DataError.getValue(), error);
1654             throw new VnfException(error, MsoExceptionCategory.INTERNAL);
1655         } else {
1656             logger.debug("Got Heat Environment from DB: {}", heatEnvironment.getEnvironment());
1657         }
1658
1659         logger.debug("In MsoVnfAdapterImpl, about to call db.getNestedTemplates avec templateId={}",
1660                 heatTemplate.getArtifactUuid());
1661
1662
1663         List<HeatTemplate> nestedTemplates = heatTemplate.getChildTemplates();
1664         Map<String, Object> nestedTemplatesChecked = new HashMap<>();
1665         if (nestedTemplates != null && !nestedTemplates.isEmpty()) {
1666             // for debugging print them out
1667             logger.debug("Contents of nestedTemplates - to be added to files: on stack:");
1668             for (HeatTemplate entry : nestedTemplates) {
1669
1670                 nestedTemplatesChecked.put(entry.getTemplateName(), entry.getTemplateBody());
1671                 logger.debug(entry.getTemplateName() + " -> " + entry.getTemplateBody());
1672             }
1673         } else {
1674             logger.debug("No nested templates found - nothing to do here");
1675             nestedTemplatesChecked = null;
1676         }
1677
1678         // Also add the files: for any get_files associated with this VfModule
1679         // *if* there are any
1680         logger.debug("In MsoVnfAdapterImpl.updateVfModule, about to call db.getHeatFiles avec vfModuleId={}",
1681                 vf.getModelUUID());
1682
1683         List<HeatFiles> heatFiles = null;
1684         Map<String, Object> heatFilesObjects = new HashMap<>();
1685
1686         // Add ability to turn on adding get_files with volume requests (by property).
1687         boolean addGetFilesOnVolumeReq = false;
1688         try {
1689             String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ);
1690             if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
1691                 addGetFilesOnVolumeReq = true;
1692                 logger.debug("AddGetFilesOnVolumeReq - setting to true! {}", propertyString);
1693             }
1694         } catch (Exception e) {
1695             logger.debug("An error occured trying to get property {} - default to false",
1696                     MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ, e);
1697         }
1698         if (!isVolumeRequest || addGetFilesOnVolumeReq) {
1699             logger.debug("In MsoVnfAdapterImpl updateVfModule, about to call db.getHeatFilesForVfModule avec "
1700                     + "vfModuleId={}", vf.getModelUUID());
1701
1702             heatFiles = vf.getHeatFiles();
1703             if (heatFiles != null && !heatFiles.isEmpty()) {
1704                 // add these to stack - to be done in createStack
1705                 // here, we will map them to Map<String, Object> from Map<String, HeatFiles>
1706                 // this will match the nested templates format
1707                 logger.debug("Contents of heatFiles - to be added to files: on stack:");
1708                 for (HeatFiles heatfile : heatFiles) {
1709                     logger.debug(heatfile.getFileName() + " -> " + heatfile.getFileBody());
1710                     heatFilesObjects.put(heatfile.getFileName(), heatfile.getFileBody());
1711                 }
1712             } else {
1713                 logger.debug("No heat files found -nothing to do here");
1714                 heatFilesObjects = null;
1715             }
1716         }
1717
1718         // Check that required parameters have been supplied
1719         String missingParams = null;
1720         List<String> paramList = new ArrayList<>();
1721
1722         // New for 1510 - consult the PARAM_ALIAS field to see if we've been
1723         // supplied an alias. Only check if we don't find it initially.
1724         // Also new in 1510 - don't flag missing parameters if there's an environment - because they might be there.
1725         // And also new - add parameter to turn off checking all together if we find we're blocking orders we
1726         // shouldn't
1727         boolean checkRequiredParameters = true;
1728         try {
1729             String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1730             if ("false".equalsIgnoreCase(propertyString) || "n".equalsIgnoreCase(propertyString)) {
1731                 checkRequiredParameters = false;
1732                 logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking...",
1733                         MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1734             }
1735         } catch (Exception e) {
1736             // No problem - default is true
1737             logger.debug("An exception occured trying to get property {}", MsoVnfAdapterImpl.CHECK_REQD_PARAMS, e);
1738         }
1739         // 1604 - Add enhanced environment & parameter checking
1740         // Part 1: parse envt entries to see if reqd parameter is there (before used a simple grep
1741         // Part 2: only submit to openstack the parameters in the envt that are in the heat template
1742         // Note this also removes any comments
1743         MsoHeatEnvironmentEntry mhee = null;
1744         if (heatEnvironment != null && heatEnvironment.getEnvironment().toLowerCase().contains("parameters:")) {
1745             logger.debug("Enhanced environment checking enabled - 1604");
1746             StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
1747             mhee = new MsoHeatEnvironmentEntry(sb);
1748             StringBuilder sb2 = new StringBuilder("\nHeat Template Parameters:\n");
1749             for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1750                 sb2.append("\t" + parm.getParamName() + ", required=" + parm.isRequired());
1751             }
1752             if (!mhee.isValid()) {
1753                 sb2.append("Environment says it's not valid! " + mhee.getErrorString());
1754             } else {
1755                 sb2.append("\nEnvironment:");
1756                 sb2.append(mhee.toFullString());
1757             }
1758             logger.debug(sb2.toString());
1759         } else {
1760             logger.debug("NO ENVIRONMENT for this entry");
1761         }
1762         // New for 1607 - support params of json type
1763         HashMap<String, JsonNode> jsonParams = new HashMap<>();
1764         boolean hasJson = false;
1765
1766         for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1767             logger.debug("Parameter:'" + parm.getParamName() + "', isRequired=" + parm.isRequired() + ", alias="
1768                     + parm.getParamAlias());
1769             // handle json
1770             String parameterType = parm.getParamType();
1771             if (parameterType == null || "".equals(parameterType.trim())) {
1772                 parameterType = "String";
1773             }
1774             JsonNode jsonNode = null;
1775             if ("json".equalsIgnoreCase(parameterType) && inputs != null) {
1776                 if (inputs.containsKey(parm.getParamName())) {
1777                     hasJson = true;
1778                     String jsonString = null;
1779                     try {
1780                         jsonString = JSON_MAPPER.writeValueAsString(inputs.get(parm.getParamName()));
1781                         jsonNode = JSON_MAPPER.readTree(jsonString);
1782                     } catch (JsonParseException jpe) {
1783                         // TODO - what to do here?
1784                         // for now - send the error to debug
1785                         logger.debug("Json Error Converting {} - {}", parm.getParamName(), jpe.getMessage(), jpe);
1786                         hasJson = false;
1787                         jsonNode = null;
1788                     } catch (Exception e) {
1789                         // or here?
1790                         logger.debug("Json Error Converting {} {}", parm.getParamName(), e.getMessage(), e);
1791                         hasJson = false;
1792                         jsonNode = null;
1793                     }
1794                     if (jsonNode != null) {
1795                         jsonParams.put(parm.getParamName(), jsonNode);
1796                     }
1797                 } else if (inputs.containsKey(parm.getParamAlias())) {
1798                     hasJson = true;
1799                     String jsonString = null;
1800                     try {
1801                         jsonString = (String) inputs.get(parm.getParamAlias());
1802                         jsonNode = JSON_MAPPER.readTree(jsonString);
1803                     } catch (JsonParseException jpe) {
1804                         // TODO - what to do here?
1805                         // for now - send the error to debug, but just leave it as a String
1806                         String errorMessage = jpe.getMessage();
1807                         logger.debug("Json Error Converting " + parm.getParamName() + " - " + errorMessage, jpe);
1808                         hasJson = false;
1809                         jsonNode = null;
1810                     } catch (Exception e) {
1811                         // or here?
1812                         logger.debug("Json Error Converting " + parm.getParamName() + " " + e.getMessage(), e);
1813                         hasJson = false;
1814                         jsonNode = null;
1815                     }
1816                     if (jsonNode != null) {
1817                         // Notice here - we add it to the jsonParams hashMap with the actual name -
1818                         // then manipulate the inputs so when we check for aliases below - it will not
1819                         // get flagged.
1820                         jsonParams.put(parm.getParamName(), jsonNode);
1821                         inputs.remove(parm.getParamAlias());
1822                         inputs.put(parm.getParamName(), jsonString);
1823                     }
1824                 } // TODO add a check for the parameter in the env file
1825             }
1826
1827             if (parm.isRequired() && (inputs == null || !inputs.containsKey(parm.getParamName()))) {
1828                 if (inputs.containsKey(parm.getParamAlias())) {
1829                     // They've submitted using an alias name. Remove that from inputs, and add back using real name.
1830                     String realParamName = parm.getParamName();
1831                     String alias = parm.getParamAlias();
1832                     Object value = inputs.get(alias);
1833                     logger.debug("*Found an Alias: paramName=" + realParamName + ",alias=" + alias + ",value=" + value);
1834                     inputs.remove(alias);
1835                     inputs.put(realParamName, value);
1836                     logger.debug("{} entry removed from inputs, added back using {}", alias, realParamName);
1837                 }
1838                 // enhanced - check if it's in the Environment (note: that method
1839                 else if (mhee != null && mhee.containsParameter(parm.getParamName())) {
1840
1841                     logger.debug("Required parameter {} appears to be in environment - do not count as missing",
1842                             parm.getParamName());
1843                 } else {
1844                     logger.debug("adding to missing parameters list: {}", parm.getParamName());
1845                     if (missingParams == null) {
1846                         missingParams = parm.getParamName();
1847                     } else {
1848                         missingParams += "," + parm.getParamName();
1849                     }
1850                 }
1851             }
1852             paramList.add(parm.getParamName());
1853         }
1854
1855
1856         if (missingParams != null) {
1857             // Problem - missing one or more required parameters
1858             if (checkRequiredParameters) {
1859                 String error = "Update VNF: Missing Required inputs: " + missingParams;
1860                 logger.error(LoggingAnchor.FIVE, MessageEnum.RA_MISSING_PARAM.toString(), missingParams, OPENSTACK,
1861                         ErrorCode.DataError.getValue(), error);
1862                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
1863             } else {
1864                 logger.debug("found missing parameters - but checkRequiredParameters is false - will not block");
1865             }
1866         }
1867
1868         // Just submit the envt entry as received from the database
1869         String newEnvironmentString = null;
1870         if (mhee != null) {
1871             newEnvironmentString = mhee.getRawEntry().toString();
1872         }
1873         // Remove any extraneous parameters (don't throw an error)
1874         if (inputs != null) {
1875             List<String> extraParams = new ArrayList<>();
1876             extraParams.addAll(inputs.keySet());
1877             // This is not a valid parameter for this template
1878             extraParams.removeAll(paramList);
1879             if (!extraParams.isEmpty()) {
1880                 logger.warn(LoggingAnchor.SIX, MessageEnum.RA_VNF_EXTRA_PARAM.toString(), vnfType,
1881                         extraParams.toString(), OPENSTACK, ErrorCode.DataError.getValue(), "Extra params");
1882                 inputs.keySet().removeAll(extraParams);
1883             }
1884         }
1885         Map<String, Object> goldenInputs = copyStringInputs(inputs);
1886         // 1607 - when we get here - we have clean inputs. Create inputsTwo in case we have json
1887         Map<String, Object> inputsTwo = null;
1888         if (hasJson && jsonParams.size() > 0) {
1889             inputsTwo = new HashMap<>();
1890             for (Map.Entry<String, Object> entry : inputs.entrySet()) {
1891                 String keyParamName = entry.getKey();
1892                 Object value = entry.getValue();
1893                 if (jsonParams.containsKey(keyParamName)) {
1894                     inputsTwo.put(keyParamName, jsonParams.get(keyParamName));
1895                 } else {
1896                     inputsTwo.put(keyParamName, value);
1897                 }
1898             }
1899             goldenInputs = inputsTwo;
1900         }
1901
1902         // "Fix" the template if it has CR/LF (getting this from Oracle)
1903         String template = heatTemplate.getHeatTemplate();
1904         template = template.replaceAll("\r\n", "\n");
1905
1906         boolean isValetEnabled = this.checkBooleanProperty(MsoVnfAdapterImpl.VALET_ENABLED, false);
1907         boolean failRequestOnValetFailure =
1908                 this.checkBooleanProperty(MsoVnfAdapterImpl.FAIL_REQUESTS_ON_VALET_FAILURE, false);
1909         logger.debug("isValetEnabled={}, failRequestsOnValetFailure={}", isValetEnabled, failRequestOnValetFailure);
1910         if (isVolumeRequest) {
1911             isValetEnabled = false;
1912             logger.debug("never send a volume request to valet");
1913         }
1914         boolean sendResponseToValet = false;
1915         if (isValetEnabled) {
1916             Holder<Map<String, Object>> valetModifiedParamsHolder = new Holder<>();
1917             String parsedVfModuleName = this.getVfModuleNameFromModuleStackId(vfModuleStackId);
1918             // Make sure it is set to something.
1919             if (parsedVfModuleName == null || parsedVfModuleName.isEmpty()) {
1920                 parsedVfModuleName = "unknown";
1921             }
1922             sendResponseToValet = this.valetUpdateRequest(cloudSiteId, cloudOwner, tenantId, heatFilesObjects,
1923                     nestedTemplatesChecked, parsedVfModuleName, false, heatTemplate, newEnvironmentString,
1924                     (HashMap<String, Object>) goldenInputs, msoRequest, inputs, failRequestOnValetFailure,
1925                     valetModifiedParamsHolder);
1926             if (sendResponseToValet) {
1927                 goldenInputs = valetModifiedParamsHolder.value;
1928             }
1929         }
1930
1931         // Have the tenant. Now deploy the stack itself
1932         // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
1933         // because we already checked for those.
1934         try {
1935             heatStack = heatU.updateStack(cloudSiteId, cloudOwner, tenantId, vfModuleName, template, goldenInputs, true,
1936                     heatTemplate.getTimeoutMinutes(), newEnvironmentString,
1937                     // heatEnvironmentString,
1938                     nestedTemplatesChecked, heatFilesObjects);
1939         } catch (MsoException me) {
1940             me.addContext("UpdateVFModule");
1941             String error = "Update VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId
1942                     + ": " + me;
1943             logger.error(LoggingAnchor.EIGHT, MessageEnum.RA_UPDATE_VNF_ERR.toString(), vfModuleType, cloudOwner,
1944                     cloudSiteId, tenantId, OPENSTACK, ErrorCode.DataError.getValue(), "Exception - " + error, me);
1945             if (isValetEnabled && sendResponseToValet) {
1946                 logger.debug("valet is enabled, the orchestration failed - now sending rollback to valet");
1947                 try {
1948                     GenericValetResponse<ValetRollbackResponse> gvr =
1949                             this.vci.callValetRollbackRequest(msoRequest.getRequestId(), null, false, me.getMessage());
1950                     // Nothing to really do here whether it succeeded or not other than log it.
1951                     logger.debug("Return code from Rollback response is {}", gvr.getStatusCode());
1952                 } catch (Exception e) {
1953                     logger.error("Exception encountered while sending Rollback to Valet ", e);
1954                 }
1955             }
1956             throw new VnfException(me);
1957         }
1958
1959
1960         // Reach this point if updateStack is successful.
1961         // Populate remaining rollback info and response parameters.
1962         vfRollback.setVnfId(heatStack.getCanonicalName());
1963         vfRollback.setVnfCreated(true);
1964
1965         if (isValetEnabled && sendResponseToValet) {
1966             logger.debug("valet is enabled, the update succeeded - now send confirm to valet with stack id");
1967             try {
1968                 GenericValetResponse<ValetConfirmResponse> gvr =
1969                         this.vci.callValetConfirmRequest(msoRequest.getRequestId(), heatStack.getCanonicalName());
1970                 // Nothing to really do here whether it succeeded or not other than log it.
1971                 logger.debug("Return code from Confirm response is {}", gvr.getStatusCode());
1972             } catch (Exception e) {
1973                 logger.error("Exception encountered while sending Confirm to Valet ", e);
1974             }
1975         }
1976
1977         outputs.value = copyStringOutputs(heatStack.getOutputs());
1978         rollback.value = vfRollback;
1979     }
1980
1981     private String getVfModuleNameFromModuleStackId(String vfModuleStackId) {
1982         // expected format of vfModuleStackId is "MSOTEST51-vSAMP3_base_module-0/1fc1f86c-7b35-447f-99a6-c23ec176ae24"
1983         // before the "/" is the vfModuleName and after the "/" is the heat stack id in Openstack
1984         if (vfModuleStackId == null)
1985             return null;
1986         int index = vfModuleStackId.lastIndexOf('/');
1987         if (index <= 0)
1988             return null;
1989         String vfModuleName = null;
1990         try {
1991             vfModuleName = vfModuleStackId.substring(0, index);
1992         } catch (Exception e) {
1993             logger.debug("Exception", e);
1994             vfModuleName = null;
1995         }
1996         return vfModuleName;
1997     }
1998
1999     /*
2000      * Helper method to check a boolean property value - on error return provided default
2001      */
2002     private boolean checkBooleanProperty(String propertyName, boolean defaultValue) {
2003         boolean property = defaultValue;
2004         try {
2005             String propertyString = this.environment.getProperty(propertyName);
2006             if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
2007                 property = true;
2008             } else if ("false".equalsIgnoreCase(propertyString) || "n".equalsIgnoreCase(propertyString)) {
2009                 property = false;
2010             }
2011         } catch (Exception e) {
2012             logger.debug("An exception occured trying to get property {} - defaulting to ", propertyName, defaultValue,
2013                     e);
2014             property = defaultValue;
2015         }
2016         return property;
2017     }
2018
2019     /*
2020      * Helper method to combine getFiles and nestedTemplates in to a single Map
2021      */
2022     private Map<String, Object> combineGetFilesAndNestedTemplates(Map<String, Object> getFiles,
2023             Map<String, Object> nestedTemplates) {
2024         boolean haveGetFiles = true;
2025         boolean haveNestedTemplates = true;
2026         Map<String, Object> files = new HashMap<>();
2027         if (getFiles == null || getFiles.isEmpty()) {
2028             haveGetFiles = false;
2029         }
2030         if (nestedTemplates == null || nestedTemplates.isEmpty()) {
2031             haveNestedTemplates = false;
2032         }
2033         if (haveGetFiles && haveNestedTemplates) {
2034             for (String keyString : getFiles.keySet()) {
2035                 files.put(keyString, getFiles.get(keyString));
2036             }
2037             for (String keyString : nestedTemplates.keySet()) {
2038                 files.put(keyString, nestedTemplates.get(keyString));
2039             }
2040         } else {
2041             // Handle if we only have one or neither:
2042             if (haveGetFiles) {
2043                 files = getFiles;
2044             }
2045             if (haveNestedTemplates) {
2046                 files = nestedTemplates;
2047             }
2048         }
2049         return files;
2050     }
2051
2052     /*
2053      * Valet Create request
2054      */
2055     private boolean valetCreateRequest(String cloudSiteId, String cloudOwner, String tenantId,
2056             Map<String, Object> heatFilesObjects, Map<String, Object> nestedTemplatesChecked, String vfModuleName,
2057             boolean backout, HeatTemplate heatTemplate, String newEnvironmentString, Map<String, Object> goldenInputs,
2058             MsoRequest msoRequest, Map<String, Object> inputs, boolean failRequestOnValetFailure,
2059             Holder<Map<String, Object>> valetModifiedParamsHolder) throws VnfException {
2060         boolean valetSucceeded = false;
2061         String valetErrorMessage = "more detail not available";
2062         try {
2063             String keystoneUrl = heat.getCloudSiteKeystoneUrl(cloudSiteId);
2064             Map<String, Object> files =
2065                     this.combineGetFilesAndNestedTemplates(heatFilesObjects, nestedTemplatesChecked);
2066             HeatRequest heatRequest = new HeatRequest(vfModuleName, backout, heatTemplate.getTimeoutMinutes(),
2067                     heatTemplate.getTemplateBody(), newEnvironmentString, files, goldenInputs);
2068             GenericValetResponse<ValetCreateResponse> createReq = this.vci.callValetCreateRequest(
2069                     msoRequest.getRequestId(), cloudSiteId, cloudOwner, tenantId, msoRequest.getServiceInstanceId(),
2070                     (String) inputs.get("vnf_id"), (String) inputs.get("vnf_name"), (String) inputs.get("vf_module_id"),
2071                     (String) inputs.get("vf_module_name"), keystoneUrl, heatRequest);
2072             ValetCreateResponse vcr = createReq.getReturnObject();
2073             if (vcr != null && createReq.getStatusCode() == 200) {
2074                 ValetStatus status = vcr.getStatus();
2075                 if (status != null) {
2076                     String statusCode = status.getStatus(); // "ok" or "failed"
2077                     if ("ok".equalsIgnoreCase(statusCode)) {
2078                         Map<String, Object> newInputs = vcr.getParameters();
2079                         if (newInputs != null) {
2080                             Map<String, Object> oldGold = goldenInputs;
2081                             logger.debug("parameters before being modified by valet:{}", oldGold.toString());
2082                             goldenInputs = new HashMap<>();
2083                             for (String key : newInputs.keySet()) {
2084                                 goldenInputs.put(key, newInputs.get(key));
2085                             }
2086                             valetModifiedParamsHolder.value = goldenInputs;
2087                             logger.debug("parameters after being modified by valet:{}", goldenInputs.toString());
2088                             valetSucceeded = true;
2089                         }
2090                     } else {
2091                         valetErrorMessage = status.getMessage();
2092                     }
2093                 }
2094             } else {
2095                 logger.debug("Got a bad response back from valet");
2096                 valetErrorMessage = "Bad response back from Valet";
2097                 valetSucceeded = false;
2098             }
2099         } catch (Exception e) {
2100             logger.error("An exception occurred trying to call valet ...", e);
2101             valetSucceeded = false;
2102             valetErrorMessage = e.getMessage();
2103         }
2104         if (failRequestOnValetFailure && !valetSucceeded) {
2105             // The valet request failed - and property says to fail the request
2106             // TODO Create a new exception class for valet?
2107             throw new VnfException("A failure occurred with Valet: " + valetErrorMessage);
2108         }
2109         return valetSucceeded;
2110     }
2111
2112     /*
2113      * Valet update request
2114      */
2115
2116     private boolean valetUpdateRequest(String cloudSiteId, String cloudOwnerId, String tenantId,
2117             Map<String, Object> heatFilesObjects, Map<String, Object> nestedTemplatesChecked, String vfModuleName,
2118             boolean backout, HeatTemplate heatTemplate, String newEnvironmentString, Map<String, Object> goldenInputs,
2119             MsoRequest msoRequest, Map<String, Object> inputs, boolean failRequestOnValetFailure,
2120             Holder<Map<String, Object>> valetModifiedParamsHolder) throws VnfException {
2121
2122         boolean valetSucceeded = false;
2123         String valetErrorMessage = "more detail not available";
2124         try {
2125             String keystoneUrl = heat.getCloudSiteKeystoneUrl(cloudSiteId);
2126             Map<String, Object> files =
2127                     this.combineGetFilesAndNestedTemplates(heatFilesObjects, nestedTemplatesChecked);
2128             HeatRequest heatRequest = new HeatRequest(vfModuleName, false, heatTemplate.getTimeoutMinutes(),
2129                     heatTemplate.getTemplateBody(), newEnvironmentString, files, goldenInputs);
2130             // vnf name is not sent to MSO on update requests - so we will set it to the vf module name for now
2131             GenericValetResponse<ValetUpdateResponse> updateReq =
2132                     this.vci.callValetUpdateRequest(msoRequest.getRequestId(), cloudSiteId, cloudOwnerId, tenantId,
2133                             msoRequest.getServiceInstanceId(), (String) inputs.get("vnf_id"), vfModuleName,
2134                             (String) inputs.get("vf_module_id"), vfModuleName, keystoneUrl, heatRequest);
2135             ValetUpdateResponse vur = updateReq.getReturnObject();
2136             if (vur != null && updateReq.getStatusCode() == 200) {
2137                 ValetStatus status = vur.getStatus();
2138                 if (status != null) {
2139                     String statusCode = status.getStatus(); // "ok" or "failed"
2140                     if ("ok".equalsIgnoreCase(statusCode)) {
2141                         Map<String, Object> newInputs = vur.getParameters();
2142                         if (newInputs != null) {
2143                             Map<String, Object> oldGold = goldenInputs;
2144                             logger.debug("parameters before being modified by valet:{}", oldGold);
2145                             goldenInputs = new HashMap<>();
2146                             for (String key : newInputs.keySet()) {
2147                                 goldenInputs.put(key, newInputs.get(key));
2148                             }
2149                             valetModifiedParamsHolder.value = goldenInputs;
2150                             logger.debug("parameters after being modified by valet:{}", goldenInputs);
2151                             valetSucceeded = true;
2152                         }
2153                     } else {
2154                         valetErrorMessage = status.getMessage();
2155                     }
2156                 }
2157             } else {
2158                 logger.debug("Got a bad response back from valet");
2159                 valetErrorMessage = "Got a bad response back from valet";
2160                 valetSucceeded = false;
2161             }
2162         } catch (Exception e) {
2163             logger.error("An exception occurred trying to call valet - will continue processing for now...", e);
2164             valetErrorMessage = e.getMessage();
2165             valetSucceeded = false;
2166         }
2167         if (failRequestOnValetFailure && !valetSucceeded) {
2168             // The valet request failed - and property says to fail the request
2169             // TODO Create a new exception class for valet?
2170             throw new VnfException("A failure occurred with Valet: " + valetErrorMessage);
2171         }
2172         return valetSucceeded;
2173     }
2174
2175     /*
2176      * Valet delete request
2177      */
2178     private boolean valetDeleteRequest(String cloudSiteId, String cloudOwnerId, String tenantId, String vnfName,
2179             MsoRequest msoRequest, boolean failRequestOnValetFailure) {
2180         boolean valetDeleteRequestSucceeded = false;
2181         String valetErrorMessage = "more detail not available";
2182         try {
2183             String vfModuleId = vnfName;
2184             String vfModuleName = vnfName;
2185             try {
2186                 vfModuleName = vnfName.substring(0, vnfName.indexOf('/'));
2187                 vfModuleId = vnfName.substring(vnfName.indexOf('/') + 1);
2188             } catch (Exception e) {
2189                 // do nothing - send what we got for vnfName for both to valet
2190                 logger.error("An exception occurred trying to call MsoVnfAdapterImpl.valetDeleteRequest() method", e);
2191             }
2192             GenericValetResponse<ValetDeleteResponse> deleteReq = this.vci.callValetDeleteRequest(
2193                     msoRequest.getRequestId(), cloudSiteId, cloudOwnerId, tenantId, vfModuleId, vfModuleName);
2194             ValetDeleteResponse vdr = deleteReq.getReturnObject();
2195             if (vdr != null && deleteReq.getStatusCode() == 200) {
2196                 ValetStatus status = vdr.getStatus();
2197                 if (status != null) {
2198                     String statusCode = status.getStatus(); // "ok" or "failed"
2199                     if ("ok".equalsIgnoreCase(statusCode)) {
2200                         logger.debug("delete request to valet returned success");
2201                         valetDeleteRequestSucceeded = true;
2202                     } else {
2203                         logger.debug("delete request to valet returned failure");
2204                         valetDeleteRequestSucceeded = false;
2205                         valetErrorMessage = status.getMessage();
2206                     }
2207                 }
2208             } else {
2209                 logger.debug("Got a bad response back from valet - delete request failed");
2210                 valetDeleteRequestSucceeded = false;
2211                 valetErrorMessage = "Got a bad response back from valet - delete request failed";
2212             }
2213         } catch (Exception e) {
2214             logger.error("An exception occurred trying to call valet - valetDeleteRequest failed", e);
2215             valetDeleteRequestSucceeded = false;
2216             valetErrorMessage = e.getMessage();
2217         }
2218         if (!valetDeleteRequestSucceeded && failRequestOnValetFailure) {
2219             logger.error("ValetDeleteRequestFailed - del req still will be sent to openstack",
2220                     new VnfException("ValetDeleteRequestFailedError"));
2221         }
2222         return valetDeleteRequestSucceeded;
2223     }
2224 }