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