b99e34eb9d4b4685676fb4c8aaceae4ffb6c6772
[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 cloudOwner, 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             List<String> oobMgtNetNames = new ArrayList<>();
532
533             HeatBridgeApi heatBridgeClient = new HeatBridgeImpl(new AAIResourcesClient(), cloudIdentity,
534                  cloudOwner, cloudSiteId, tenantId);
535
536             OpenstackClient openstackClient = heatBridgeClient.authenticate();
537             List<Resource> stackResources = heatBridgeClient.queryNestedHeatStackResources(heatStackId);
538
539             List<Server> osServers = heatBridgeClient.getAllOpenstackServers(stackResources);
540
541             List<Image> osImages = heatBridgeClient.extractOpenstackImagesFromServers(osServers);
542
543             List<Flavor> osFlavors = heatBridgeClient.extractOpenstackFlavorsFromServers(osServers);
544
545             logger.debug("Successfully queried heat stack{} for resources.", heatStackId);
546             //os images
547             if (osImages != null && !osImages.isEmpty()) {
548                 heatBridgeClient.buildAddImagesToAaiAction(osImages);
549                 logger.debug("Successfully built AAI actions to add images.");
550             } else {
551                 logger.debug("No images to update to AAI.");
552             }
553             //flavors
554             if (osFlavors != null && !osFlavors.isEmpty()) {
555                 heatBridgeClient.buildAddFlavorsToAaiAction(osFlavors);
556                 logger.debug("Successfully built AAI actions to add flavors.");
557             } else {
558                 logger.debug("No flavors to update to AAI.");
559             }
560
561             //compute resources
562             heatBridgeClient.buildAddVserversToAaiAction(genericVnfName, vfModuleId, osServers);
563             logger.debug("Successfully queried compute resources and built AAI vserver actions.");
564
565             //neutron resources
566             List<String> oobMgtNetIds = new ArrayList<>();
567
568             //if no network-id list is provided, however network-name list is
569             if (!CollectionUtils.isEmpty(oobMgtNetNames)) {
570                 oobMgtNetIds = heatBridgeClient.extractNetworkIds(oobMgtNetNames);
571             }
572             heatBridgeClient.buildAddVserverLInterfacesToAaiAction(stackResources, oobMgtNetIds);
573             logger.debug(
574                 "Successfully queried neutron resources and built AAI actions to add l-interfaces to vservers.");
575
576             //Update AAI
577             heatBridgeClient.submitToAai();
578         } catch (Exception ex) {
579             logger.debug("Heatbrige failed for stackId: " + heatStack.getCanonicalName(), ex);
580         }
581     }
582
583     private String convertNode(final JsonNode node) {
584         try {
585             final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
586             return JSON_MAPPER.writeValueAsString(obj);
587         } catch (JsonParseException jpe) {
588             logger.debug("Error converting json to string: {}", jpe.getMessage(),jpe);
589         } catch (Exception e) {
590             logger.debug("Error converting json to string: {}", e.getMessage(),e);
591         }
592         return "[Error converting json to string]";
593     }
594
595     private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
596         if (objectMap == null) {
597             return null;
598         }
599         Map<String, String> stringMap = new HashMap<>();
600         for (String key : objectMap.keySet()) {
601             if (!stringMap.containsKey(key)) {
602                 Object obj = objectMap.get(key);
603                 if (obj instanceof String) {
604                     stringMap.put(key, (String) objectMap.get(key));
605                 } else if (obj instanceof JsonNode ){
606                     // This is a bit of mess - but I think it's the least impacting
607                     // let's convert it BACK to a string - then it will get converted back later
608                     try {
609                         String str = this.convertNode((JsonNode) obj);
610                         stringMap.put(key, str);
611                     } catch (Exception e) {
612                         logger.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode "+ key,e);
613                         //okay in this instance - only string values (fqdn) are expected to be needed
614                     }
615                 } else if (obj instanceof java.util.LinkedHashMap) {
616                     logger.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
617                     try {
618                         String str = JSON_MAPPER.writeValueAsString(obj);
619                         stringMap.put(key, str);
620                     } catch (Exception e) {
621                         logger.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap "+ key,e);
622                                         }
623                                 }  else if (obj instanceof Integer) {
624                                         try {
625                                                 String str = "" + obj;
626                                                 stringMap.put(key, str);
627                                         } catch (Exception e) {
628               logger.debug("DANGER WILL ROBINSON: unable to convert value for Integer "+ key,e);
629                     }
630                 } else {
631                     try {
632                                                 String str = obj.toString();
633                         stringMap.put(key, str);
634                     } catch (Exception e) {
635                         logger.debug("DANGER WILL ROBINSON: unable to convert value "+ key + " (" + e.getMessage() + ")",e);
636                     }
637                 }
638             }
639         }
640
641         return stringMap;
642     }
643
644     @Override
645     public void createVfModule(String cloudSiteId,
646             String cloudOwner,
647             String tenantId,
648             String vnfType,
649             String vnfVersion,
650             String genericVnfName,
651             String vnfName,
652             String vfModuleId,
653             String requestType,
654             String volumeGroupHeatStackId,
655             String baseVfHeatStackId,
656             String modelCustomizationUuid,
657             Map <String, Object> inputs,
658             Boolean failIfExists,
659             Boolean backout,
660             Boolean enableBridge,
661             MsoRequest msoRequest,
662             Holder <String> vnfId,
663             Holder <Map <String, String>> outputs,
664             Holder <VnfRollback> rollback) throws VnfException {
665         String vfModuleName = vnfName;
666         String vfModuleType = vnfType;
667         String vfVersion = vnfVersion;
668         String mcu = modelCustomizationUuid;
669         boolean useMCUuid = false;
670         if (mcu != null && !mcu.isEmpty()) {
671             if ("null".equalsIgnoreCase(mcu)) {
672                 logger.debug("modelCustomizationUuid: passed in as the string 'null' - will ignore: " + modelCustomizationUuid);
673                 useMCUuid = false;
674                 mcu = "";
675             } else {
676                 logger.debug("Found modelCustomizationUuid! Will use that: " + mcu);
677                 useMCUuid = true;
678             }
679         }
680
681         String requestTypeString = "";
682         if (requestType != null && !"".equals(requestType)) {
683                 requestTypeString = requestType;
684         }
685         String nestedStackId = null;
686         if (volumeGroupHeatStackId != null && !"".equals(volumeGroupHeatStackId) && !"null".equalsIgnoreCase(volumeGroupHeatStackId)) {
687                 nestedStackId = volumeGroupHeatStackId;
688         }
689         String nestedBaseStackId = null;
690         if (baseVfHeatStackId != null && !"".equals(baseVfHeatStackId) && !"null".equalsIgnoreCase(baseVfHeatStackId)) {
691                 nestedBaseStackId = baseVfHeatStackId;
692         }
693
694         //This method will also handle doing things the "old" way - i.e., just orchestrate a VNF
695         boolean oldWay = false;
696         if (requestTypeString.startsWith("X")) {
697                 oldWay = true;
698             logger.debug("orchestrating a VNF - *NOT* a module!");
699                 requestTypeString = requestTypeString.substring(1);
700         }
701
702         // 1607 - let's parse out the request type we're being sent
703         boolean isBaseRequest = false;
704         boolean isVolumeRequest = false;
705         if (requestTypeString.startsWith("VOLUME")) {
706                 isVolumeRequest = true;
707         }
708
709         logger.debug("requestTypeString = " + requestTypeString + ", nestedStackId = " + nestedStackId + ", nestedBaseStackId = " + nestedBaseStackId);
710         // Will capture execution time for metrics
711         long startTime = System.currentTimeMillis ();
712
713         // Build a default rollback object (no actions performed)
714         VnfRollback vfRollback = new VnfRollback();
715         vfRollback.setCloudSiteId(cloudSiteId);
716         vfRollback.setCloudOwner(cloudOwner);
717         vfRollback.setTenantId(tenantId);
718         vfRollback.setMsoRequest(msoRequest);
719         vfRollback.setRequestType(requestTypeString);
720         vfRollback.setVolumeGroupHeatStackId(volumeGroupHeatStackId);
721         vfRollback.setBaseGroupHeatStackId(baseVfHeatStackId);
722         vfRollback.setIsBase(isBaseRequest);
723         vfRollback.setModelCustomizationUuid(mcu);
724
725         // Put data into A&AI through Heatstack
726         if(enableBridge != null && enableBridge) {
727                 callHeatbridge(baseVfHeatStackId);
728         }
729
730         StackInfo heatStack = null;
731         long subStartTime1 = System.currentTimeMillis ();
732         try {
733             heatStack = heat.queryStack (cloudSiteId, cloudOwner, tenantId, vfModuleName);
734         } catch (MsoException me) {
735             String error = "Create VF Module: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
736             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner, cloudSiteId,
737                 tenantId, "OpenStack", "queryStack", ErrorCode.DataError.getValue(), "Exception - queryStack",
738                 me);
739             logger.debug(error);
740             // Failed to query the Stack due to an openstack exception.
741             // Convert to a generic VnfException
742             me.addContext ("CreateVFModule");
743             throw new VnfException (me);
744         }
745         // New with 1607 - more precise handling/messaging if the stack already exists
746         if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
747                 // INIT, CREATED, NOTFOUND, FAILED, BUILDING, DELETING, UNKNOWN, UPDATING, UPDATED
748                 HeatStatus status = heatStack.getStatus();
749                 if (status == HeatStatus.INIT || status == HeatStatus.BUILDING || status == HeatStatus.DELETING || status == HeatStatus.UPDATING) {
750                         // fail - it's in progress - return meaningful error
751                 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.";
752               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
753                   cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack", ErrorCode.DataError.getValue(),
754                   "Stack " + vfModuleName + " already exists");
755               logger.debug(error);
756                 throw new VnfAlreadyExists (vfModuleName, cloudOwner, cloudSiteId, tenantId, heatStack.getCanonicalName ());
757                 }
758                 if (status == HeatStatus.FAILED) {
759                         // fail - it exists and is in a FAILED state
760               String error = "Create VF: Stack " + vfModuleName + " already exists and is in FAILED state in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
761               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
762                   cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack", ErrorCode.DataError.getValue(),
763                   "Stack " + vfModuleName + " already exists and is " + "in FAILED state");
764               logger.debug(error);
765                 throw new VnfAlreadyExists (vfModuleName, cloudOwner, cloudSiteId, tenantId, heatStack.getCanonicalName ());
766                 }
767                 if (status == HeatStatus.UNKNOWN || status == HeatStatus.UPDATED) {
768                         // fail - it exists and is in a FAILED state
769               String error =
770                   "Create VF: Stack " + vfModuleName + " already exists and has status " + status.toString() + " in "
771                       + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
772               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
773                   cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack", ErrorCode.DataError.getValue(),
774                   "Stack " + vfModuleName + " already exists and is " + "in UPDATED or UNKNOWN state");
775               logger.debug(error);
776               throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId, heatStack.getCanonicalName());
777           }
778                 if (status == HeatStatus.CREATED) {
779                         // fail - it exists
780                         if (failIfExists != null && failIfExists) {
781                 String error =
782                     "Create VF: Stack " + vfModuleName + " already exists in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId;
783                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
784                     cloudOwner, cloudSiteId, tenantId, "OpenStack", "queryStack", ErrorCode.DataError.getValue(),
785                     "Stack " + vfModuleName + " already exists");
786                 logger.debug(error);
787                 throw new VnfAlreadyExists(vfModuleName, cloudOwner, cloudSiteId, tenantId, heatStack.getCanonicalName());
788             } else {
789                                 logger.debug ("Found Existing stack, status={}", heatStack.getStatus ());
790                                 // Populate the outputs from the existing stack.
791                                 vnfId.value = heatStack.getCanonicalName ();
792                                 outputs.value = copyStringOutputs (heatStack.getOutputs ());
793                                 rollback.value = vfRollback; // Default rollback - no updates performed
794                         }
795                 }
796             return;
797
798         }
799
800         // handle a nestedStackId if sent- this one would be for the volume - so applies to both Vf and Vnf
801         StackInfo nestedHeatStack = null;
802         long subStartTime2 = System.currentTimeMillis ();
803         Map<String, Object> nestedVolumeOutputs = null;
804         if (nestedStackId != null) {
805                 try {
806                         logger.debug("Querying for nestedStackId = {}", nestedStackId);
807                         nestedHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedStackId);
808                 } catch (MsoException me) {
809                     // Failed to query the Stack due to an openstack exception.
810                     // Convert to a generic VnfException
811                     me.addContext ("CreateVFModule");
812                     String error = "Create VFModule: Attached heatStack ID Query " + nestedStackId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
813               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner, cloudSiteId,
814                       tenantId, "OpenStack", "queryStack", ErrorCode.BusinessProcesssError.getValue(),
815                       "MsoException trying to query nested stack", me);
816               logger.debug("ERROR trying to query nested stack= {}", error);
817                     throw new VnfException (me);
818                 }
819                 if (nestedHeatStack == null || nestedHeatStack.getStatus() == HeatStatus.NOTFOUND) {
820                     String error = "Create VFModule: Attached heatStack ID DOES NOT EXIST " + nestedStackId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
821               logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
822                   cloudOwner, cloudSiteId, tenantId, error, "OpenStack", "queryStack",
823                   ErrorCode.BusinessProcesssError.getValue(),
824                   "Create VFModule: Attached heatStack ID " + "DOES NOT EXIST");
825               logger.debug(error);
826               throw new VnfException (error, MsoExceptionCategory.USERDATA);
827                 } else {
828                         logger.debug("Found nested volume heat stack - copying values to inputs *later*");
829                         nestedVolumeOutputs = nestedHeatStack.getOutputs();
830                 }
831         }
832
833         // handle a nestedBaseStackId if sent- this is the stack ID of the base. Should be null for VNF requests
834         StackInfo nestedBaseHeatStack = null;
835         long subStartTime3 = System.currentTimeMillis ();
836         Map<String, Object> baseStackOutputs = null;
837         if (nestedBaseStackId != null) {
838                 try {
839                         logger.debug("Querying for nestedBaseStackId = {}", nestedBaseStackId);
840                         nestedBaseHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedBaseStackId);
841                 } catch (MsoException me) {
842                     // Failed to query the Stack due to an openstack exception.
843                     // Convert to a generic VnfException
844                     me.addContext ("CreateVFModule");
845                     String error = "Create VFModule: Attached baseHeatStack ID Query " + nestedBaseStackId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
846               logger
847                   .error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner, cloudSiteId,
848                       tenantId, "OpenStack", "QueryStack", ErrorCode.BusinessProcesssError.getValue(),
849                       "MsoException trying to query nested base stack", me);
850               logger.debug("ERROR trying to query nested base stack= {}", error);
851               throw new VnfException (me);
852                 }
853                 if (nestedBaseHeatStack == null || nestedBaseHeatStack.getStatus() == HeatStatus.NOTFOUND) {
854                     String error = "Create VFModule: Attached base heatStack ID DOES NOT EXIST " + nestedBaseStackId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
855               logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
856                   cloudOwner, cloudSiteId, tenantId, error, "OpenStack", "QueryStack",
857                   ErrorCode.BusinessProcesssError.getValue(),
858                   "Create VFModule: Attached base heatStack ID DOES NOT EXIST");
859               logger.debug("Exception occurred", error);
860                     throw new VnfException (error, MsoExceptionCategory.USERDATA);
861                 } else {
862                         logger.debug("Found nested base heat stack - these values will be copied to inputs *later*");
863                         baseStackOutputs = nestedBaseHeatStack.getOutputs();
864                 }
865         }
866
867         // Ready to deploy the new VNF
868
869
870
871         try {
872             // Retrieve the VF
873                 VfModule vf = null;
874                 VnfResource vnfResource = null;
875                 VfModuleCustomization vfmc = null;
876                 logger.debug("version: {}", vfVersion);
877             if (useMCUuid) {
878                         // 1707 - db refactoring
879                         vfmc = vfModuleCustomRepo.findByModelCustomizationUUID(mcu);
880                         if(vfmc != null)
881                                 vf=vfmc.getVfModule();
882                         else
883                                 vf=null;
884
885                 // 1702 - this will be the new way going forward. We find the vf by mcu - otherwise, code is the same.
886                 if (vf == null) {
887                                 logger.debug("Unable to find vfModuleCust with modelCustomizationUuid={}", mcu);
888                                 String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid=" + mcu;
889                     logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
890                         "VF Module ModelCustomizationUuid", modelCustomizationUuid, "OpenStack",
891                         ErrorCode.DataError.getValue(),
892                         "Create VF Module: Unable to find vfModule with " + "modelCustomizationUuid=" + mcu);
893                     logger.debug(error);
894                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
895                 } else {
896                                 logger.trace("Found vfModuleCust entry {}", vfmc.toString());
897                 }
898                 if (vf.getIsBase()) {
899                     isBaseRequest = true;
900                     logger.debug("This is a BASE VF request!");
901                 } else {
902                     logger.debug("This is *not* a BASE VF request!");
903                     if (!isVolumeRequest && nestedBaseStackId == null) {
904                         logger.debug("DANGER WILL ROBINSON! This is unexpected - no nestedBaseStackId with this non-base request");
905                     }
906                 }
907                 }
908
909                 else { // This is to support gamma only - get info from vnf_resource table
910                                 if (vfVersion != null && !vfVersion.isEmpty()) {
911                                         vnfResource = vnfResourceRepo.findByModelNameAndModelVersion(vnfType, vnfVersion);
912                                 } else {
913                                         vnfResource = vnfResourceRepo.findByModelName(vnfType);
914                                 }
915                                 if (vnfResource == null) {
916             String error = "Create VNF: Unknown VNF Type: " + vnfType;
917             logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "VNF Type", vnfType,
918                 "OpenStack", ErrorCode.DataError.getValue(), "Create VNF: Unknown VNF Type");
919             logger.debug(error);
920             throw new VnfException(error, MsoExceptionCategory.USERDATA);
921         }
922                 logger.debug("Got VNF module definition from Catalog: {}", vnfResource.toString());
923             }
924                         // By here - we have either a vf or vnfResource
925
926             //1607 - Add version check
927             // First - see if it's in the VnfResource record
928             // if we have a vf Module - then we have to query to get the VnfResource record.
929             if (!oldWay && vf.getVnfResources() != null) {
930                         vnfResource = vf.getVnfResources();
931                         if (vnfResource == null) {
932                                 logger.debug("Unable to find vnfResource will not error for now...");
933                         }
934             }
935             String minVersionVnf = null;
936             String maxVersionVnf = null;
937             if (vnfResource != null) {
938                 try {
939                         minVersionVnf = vnfResource.getAicVersionMin();
940                         maxVersionVnf = vnfResource.getAicVersionMax();
941                 } catch (Exception e) {
942                         logger.debug("Unable to pull min/max version for this VNF Resource entry",e);
943                         minVersionVnf = null;
944                         maxVersionVnf = null;
945                 }
946                 if (minVersionVnf != null && "".equals(minVersionVnf)) {
947                         minVersionVnf = null;
948                 }
949                 if (maxVersionVnf != null && "".equals(maxVersionVnf)) {
950                         maxVersionVnf = null;
951                 }
952             }
953                         if (minVersionVnf != null && maxVersionVnf != null) {
954                                 MavenLikeVersioning aicV = new MavenLikeVersioning();
955
956                                 // double check
957                                 if (this.cloudConfig != null) {
958                     Optional<CloudSite> cloudSiteOpt = this.cloudConfig.getCloudSite(cloudSiteId);
959                     if (cloudSiteOpt.isPresent()) {
960                         aicV.setVersion(cloudSiteOpt.get().getCloudVersion());
961                                                 // Add code to handle unexpected values in here
962                                                 boolean moreThanMin = true;
963                                                 boolean equalToMin = true;
964                                                 boolean moreThanMax = true;
965                                                 boolean equalToMax = true;
966                                                 boolean doNotTest = false;
967                                                 try {
968                                                         moreThanMin = aicV.isMoreRecentThan(minVersionVnf);
969                                                         equalToMin = aicV.isTheSameVersion(minVersionVnf);
970                                                         moreThanMax = aicV.isMoreRecentThan(maxVersionVnf);
971                                                         equalToMax = aicV.isTheSameVersion(maxVersionVnf);
972                                                 } catch (Exception e) {
973                 logger.debug("An exception occurred while trying to test AIC Version {} - will default to not check",
974                     e.getMessage(), e);
975                 doNotTest = true;
976                                                 }
977                                                 if (!doNotTest) {
978                                                         if ((moreThanMin || equalToMin) // aic >= min
979                                                                         && (equalToMax || !(moreThanMax))) { //aic <= max
980                   logger.debug(
981                       "VNF Resource " + vnfResource.getModelName() + ", ModelUuid=" + vnfResource.getModelUUID()
982                           + " VersionMin=" + minVersionVnf + " VersionMax:" + maxVersionVnf + " supported on Cloud: "
983                           + cloudSiteId + " with AIC_Version:" + cloudSiteOpt.get().getCloudVersion());
984               } else {
985                                                                 // ERROR
986                                                                 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();
987                   logger.error("{} {} {} {} {}", MessageEnum.RA_CONFIG_EXC.toString(), error, "OpenStack",
988                       ErrorCode.BusinessProcesssError.getValue(), "Exception - setVersion");
989                   logger.debug(error);
990                                                                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
991                                                         }
992                                                 } else {
993                                                         logger.debug("bypassing testing AIC version...");
994                                                 }
995                                         } // let this error out downstream to avoid introducing uncertainty at this stage
996                                 } else {
997                                         logger.debug("cloudConfig is NULL - cannot check cloud site version");
998                                 }
999                         } else {
1000                                 logger.debug("AIC Version not set in VNF_Resource - this is expected thru 1607 - do not error here - not checked"
1001             + ".");
1002                         }
1003                         // End Version check 1607
1004
1005
1006
1007
1008          // By the time we get here - heatTemplateId and heatEnvtId should be populated (or null)
1009                         HeatTemplate heatTemplate = null;
1010             HeatEnvironment heatEnvironment = null;
1011             if (oldWay) {
1012                 //This will handle old Gamma BrocadeVCE VNF
1013                                 heatTemplate = vnfResource.getHeatTemplates();
1014             }
1015             else {
1016                 if (vf != null) {
1017                            if (isVolumeRequest) {
1018                                                 heatTemplate = vf.getVolumeHeatTemplate();
1019                                                 heatEnvironment = vfmc.getVolumeHeatEnv();
1020                                         } else {
1021                                                 heatTemplate = vf.getModuleHeatTemplate();
1022                                                 heatEnvironment = vfmc.getHeatEnvironment();
1023                                         }
1024                         }
1025             }
1026
1027                         if (heatTemplate == null) {
1028                                 String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType=" + requestTypeString;
1029           logger
1030               .error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID", vfModuleType,
1031                   "OpenStack", ErrorCode.DataError.getValue(), error);
1032           logger.debug(error);
1033                                 throw new VnfException(error, MsoExceptionCategory.INTERNAL);
1034                         } else {
1035                                 logger.debug("Got HEAT Template from DB: {}", heatTemplate.getHeatTemplate());
1036                         }
1037
1038                         if (oldWay) {
1039                                 //This will handle old Gamma BrocadeVCE VNF
1040                                 logger.debug("No environment parameter found for this Type " + vfModuleType);
1041                         } else {
1042                     if (heatEnvironment == null) {
1043                        String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType;
1044                   logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID",
1045                       "OpenStack", ErrorCode.DataError.getValue(), error);
1046                   logger.debug(error);
1047                             throw new VnfException (error, MsoExceptionCategory.INTERNAL);
1048                     } else {
1049                   logger.debug("Got Heat Environment from DB: {}", heatEnvironment.getEnvironment());
1050               }
1051                         }
1052
1053             logger.debug("In MsoVnfAdapterImpl, about to call db.getNestedTemplates avec templateId="
1054                           + heatTemplate.getArtifactUuid ());
1055
1056
1057             List<HeatTemplate> nestedTemplates = heatTemplate.getChildTemplates();
1058             Map <String, Object> nestedTemplatesChecked = new HashMap <> ();
1059             if (nestedTemplates != null && !nestedTemplates.isEmpty()) {
1060                 // for debugging print them out
1061                 logger.debug("Contents of nestedTemplates - to be added to files: on stack:");
1062                 for (HeatTemplate entry : nestedTemplates) {
1063                     nestedTemplatesChecked.put (entry.getTemplateName(), entry.getTemplateBody());
1064                     logger.debug(entry.getTemplateName() + " -> " + entry.getTemplateBody());
1065                 }
1066             } else {
1067                 logger.debug("No nested templates found - nothing to do here");
1068                 nestedTemplatesChecked = null;
1069             }
1070
1071             // 1510 - Also add the files: for any get_files associated with this vnf_resource_id
1072             // *if* there are any
1073             List<HeatFiles> heatFiles = null;
1074
1075                         Map<String, Object> heatFilesObjects = new HashMap<>();
1076
1077             // Add ability to turn on adding get_files with volume requests (by property).
1078             boolean addGetFilesOnVolumeReq = false;
1079             try {
1080                 String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ);
1081                 if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
1082                         addGetFilesOnVolumeReq = true;
1083                   logger.debug("AddGetFilesOnVolumeReq - setting to true! {}", propertyString);
1084                 }
1085             } catch (Exception e) {
1086                 logger.debug("An error occured trying to get property " + MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ
1087                     + " - default to false", e);
1088             }
1089
1090                         if (!isVolumeRequest || addGetFilesOnVolumeReq) {
1091                                 if (oldWay) {
1092                                         logger.debug("In MsoVnfAdapterImpl createVfModule, this should not happen - old way is gamma only - no heat "
1093               + "files!");
1094                                 } else {
1095                                         // 1607 - now use VF_MODULE_TO_HEAT_FILES table
1096                                         logger.debug("In MsoVnfAdapterImpl createVfModule, about to call db.getHeatFilesForVfModule avec vfModuleId="
1097                                                         + vf.getModelUUID());
1098                                         heatFiles = vf.getHeatFiles();
1099                                 }
1100                                 if (heatFiles != null && !heatFiles.isEmpty()) {
1101                                         // add these to stack - to be done in createStack
1102                                         // here, we will map them to Map<String, Object> from
1103                                         // Map<String, HeatFiles>
1104                                         // this will match the nested templates format
1105                                         logger.debug("Contents of heatFiles - to be added to files: on stack");
1106
1107             for (HeatFiles heatfile : heatFiles) {
1108                 logger.debug(heatfile.getFileName() + " -> " + heatfile.getFileBody());
1109                 heatFilesObjects.put(heatfile.getFileName(), heatfile.getFileBody());
1110             }
1111         } else {
1112             logger.debug("No heat files found -nothing to do here");
1113             heatFilesObjects = null;
1114         }
1115                         }
1116
1117             // Check that required parameters have been supplied
1118             String missingParams = null;
1119             List <String> paramList = new ArrayList <> ();
1120
1121             // New for 1510 - consult the PARAM_ALIAS field to see if we've been
1122             // supplied an alias. Only check if we don't find it initially.
1123             // Also new in 1510 - don't flag missing parameters if there's an environment - because they might be there.
1124             // And also new - add parameter to turn off checking all together if we find we're blocking orders we
1125             // shouldn't
1126             boolean checkRequiredParameters = true;
1127             try {
1128                 String propertyString = this.environment.getProperty (MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1129                 if ("false".equalsIgnoreCase (propertyString) || "n".equalsIgnoreCase (propertyString)) {
1130                     checkRequiredParameters = false;
1131                     logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking..."
1132                         + MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1133                 }
1134             } catch (Exception e) {
1135                 // No problem - default is true
1136                 logger.debug("An exception occured trying to get property {}", MsoVnfAdapterImpl.CHECK_REQD_PARAMS, e);
1137             }
1138             // 1604 - Add enhanced environment & parameter checking
1139             // Part 1: parse envt entries to see if reqd parameter is there (before used a simple grep
1140             // Part 2: only submit to openstack the parameters in the envt that are in the heat template
1141             // Note this also removes any comments
1142             MsoHeatEnvironmentEntry mhee = null;
1143             if (heatEnvironment != null && heatEnvironment.getEnvironment() != null && heatEnvironment.getEnvironment().contains ("parameters:")) {
1144
1145                 logger.debug("Enhanced environment checking enabled - 1604");
1146                 StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
1147
1148                 mhee = new MsoHeatEnvironmentEntry(sb);
1149                 StringBuilder sb2 = new StringBuilder("\nHeat Template Parameters:\n");
1150                 for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1151                         sb2.append("\t" + parm.getParamName() + ", required=" + parm.isRequired());
1152                 }
1153                 if (!mhee.isValid()) {
1154                         sb2.append("Environment says it's not valid! " + mhee.getErrorString());
1155                 } else {
1156                         sb2.append("\nEnvironment:");
1157                         sb2.append(mhee.toFullString());
1158                 }
1159                 logger.debug(sb2.toString());
1160             } else {
1161                 logger.debug("NO ENVIRONMENT for this entry");
1162             }
1163             // New with 1707 - all variables converted to their native object types
1164             Map<String, Object> goldenInputs = null;
1165
1166             logger.debug("Now handle the inputs....first convert");
1167             ArrayList<String> parameterNames = new ArrayList<>();
1168             HashMap<String, String> aliasToParam = new HashMap<>();
1169             StringBuilder sb = new StringBuilder("\nTemplate Parameters:\n");
1170             int cntr = 0;
1171             try {
1172                 for (HeatTemplateParam htp : heatTemplate.getParameters()) {
1173                         sb.append("param[" + cntr++ + "]=" + htp.getParamName());
1174                         parameterNames.add(htp.getParamName());
1175                         if (htp.getParamAlias() != null && !"".equals(htp.getParamAlias())) {
1176                                 aliasToParam.put(htp.getParamAlias(), htp.getParamName());
1177                                 sb.append(" ** (alias=" + htp.getParamAlias() + ")");
1178                         }
1179                         sb.append("\n");
1180                 }
1181                 logger.debug(sb.toString());
1182             } catch (Exception e) {
1183                 logger.debug("??An exception occurred trying to go through Parameter Names {}", e.getMessage(),e);
1184             }
1185             // Step 1 - convert what we got as inputs (Map<String, String>) to a
1186             // Map<String, Object> - where the object matches the param type identified in the template
1187             // This will also not copy over params that aren't identified in the template
1188             goldenInputs = heat.convertInputMap(inputs, heatTemplate);
1189             // Step 2 - now simply add the outputs as we received them - no need to convert to string
1190             logger.debug("Now add in the base stack outputs if applicable");
1191             heat.copyBaseOutputsToInputs(goldenInputs, baseStackOutputs, parameterNames, aliasToParam);
1192             // Step 3 - add the volume inputs if any
1193             logger.debug("Now add in the volume stack outputs if applicable");
1194             heat.copyBaseOutputsToInputs(goldenInputs, nestedVolumeOutputs, parameterNames, aliasToParam);
1195
1196             for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
1197                 logger.debug(
1198                     "Parameter:'" + parm.getParamName() + "', isRequired=" + parm.isRequired() + ", alias=" + parm
1199                         .getParamAlias());
1200
1201                 if (parm.isRequired () && (goldenInputs == null || !goldenInputs.containsKey (parm.getParamName ()))) {
1202                         // The check for an alias was moved to the method in MsoHeatUtils - when we converted the Map<String, String> to Map<String, Object>
1203                         logger.debug("**Parameter " + parm.getParamName() + " is required and not in the inputs...check "
1204                       + "environment");
1205                     if (mhee != null && mhee.containsParameter(parm.getParamName())) {
1206                         logger.debug("Required parameter {} appears to be in environment - do not count as missing",
1207                             parm.getParamName());
1208                     } else {
1209                         logger.debug("adding to missing parameters list: {}", parm.getParamName());
1210                         if (missingParams == null) {
1211                             missingParams = parm.getParamName ();
1212                         } else {
1213                             missingParams += "," + parm.getParamName ();
1214                         }
1215                     }
1216                 }
1217                 paramList.add (parm.getParamName ());
1218             }
1219             if (missingParams != null) {
1220                 if (checkRequiredParameters) {
1221                         // Problem - missing one or more required parameters
1222                         String error = "Create VFModule: Missing Required inputs: " + missingParams;
1223                   logger.error("{} {} {} {} {}", MessageEnum.RA_MISSING_PARAM.toString(), missingParams, "OpenStack",
1224                       ErrorCode.DataError.getValue(), "Create VFModule: Missing Required inputs");
1225                   logger.debug(error);
1226                         throw new VnfException (error, MsoExceptionCategory.USERDATA);
1227                 } else {
1228                         logger.debug ("found missing parameters - but checkRequiredParameters is false - will not block");
1229                 }
1230             } else {
1231                 logger.debug ("No missing parameters found - ok to proceed");
1232             }
1233             // We can now remove the recreating of the ENV with only legit params - that check is done for us,
1234             // and it causes problems with json that has arrays
1235             String newEnvironmentString = null;
1236             if (mhee != null) {
1237                 newEnvironmentString = mhee.getRawEntry().toString();
1238             }
1239
1240             // "Fix" the template if it has CR/LF (getting this from Oracle)
1241             String template = heatTemplate.getHeatTemplate ();
1242             template = template.replaceAll ("\r\n", "\n");
1243
1244             // Valet - 1806
1245             boolean isValetEnabled = this.checkBooleanProperty(MsoVnfAdapterImpl.VALET_ENABLED, false);
1246             boolean failRequestOnValetFailure = this.checkBooleanProperty(MsoVnfAdapterImpl.FAIL_REQUESTS_ON_VALET_FAILURE, false);
1247             logger.debug("isValetEnabled={}, failRequestsOnValetFailure={}", isValetEnabled, failRequestOnValetFailure);
1248             if (oldWay || isVolumeRequest) {
1249                 isValetEnabled = false;
1250                 logger.debug("do not send to valet for volume requests or brocade");
1251             }
1252             boolean sendResponseToValet = false;
1253             if (isValetEnabled) {
1254                                 Holder<Map<String, Object>> valetModifiedParamsHolder = new Holder<>();
1255                                 sendResponseToValet = this.valetCreateRequest(cloudSiteId, cloudOwner, tenantId, heatFilesObjects,
1256                                                 nestedTemplatesChecked, vfModuleName, backout, heatTemplate, newEnvironmentString, goldenInputs,
1257                                                 msoRequest, inputs, failRequestOnValetFailure, valetModifiedParamsHolder);
1258                                 if (sendResponseToValet) {
1259                                         goldenInputs = valetModifiedParamsHolder.value;
1260                                 }
1261             }
1262
1263             // Have the tenant. Now deploy the stack itself
1264             // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
1265             // because we already checked for those.
1266             long createStackStarttime = System.currentTimeMillis ();
1267             try {
1268                 // heatStack = heat.createStack(cloudSiteId, tenantId, vnfName, template, inputs, true,
1269                 // heatTemplate.getTimeoutMinutes());
1270                 if (backout == null) {
1271                         backout = true;
1272                 }
1273                 if (heat != null) {
1274                   logger.debug("heat is not null!!");
1275
1276                         heatStack = heat.createStack (cloudSiteId,
1277                                                   cloudOwner,
1278                                               tenantId,
1279                                               vfModuleName,
1280                                               null,
1281                                               template,
1282                                               goldenInputs,
1283                                               true,
1284                                               heatTemplate.getTimeoutMinutes(),
1285                                               newEnvironmentString,
1286                                               nestedTemplatesChecked,
1287                                               heatFilesObjects,
1288                                               backout.booleanValue());
1289                 }
1290                 else {
1291                   logger.debug("heat is null!");
1292                         throw new MsoHeatNotFoundException();
1293                 }
1294             } catch (MsoException me) {
1295                 me.addContext ("CreateVFModule");
1296                 String error = "Create VF Module " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1297                 logger
1298                     .error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner, cloudSiteId,
1299                         tenantId, "OpenStack", ErrorCode.DataError.getValue(), "MsoException - createStack",
1300                         me);
1301                 logger.debug(error);
1302                 if (isValetEnabled && sendResponseToValet) {
1303                         logger.debug("valet is enabled, the orchestration failed - now sending rollback to valet");
1304                         try {
1305                                 GenericValetResponse<ValetRollbackResponse> gvr = this.vci.callValetRollbackRequest(msoRequest.getRequestId(), null, backout, me.getMessage());
1306                                 // Nothing to really do here whether it succeeded or not other than log it.
1307                                 logger.debug("Return code from Rollback response is {}", gvr.getStatusCode());
1308                         } catch (Exception e) {
1309                                 logger.error("Exception encountered while sending Rollback to Valet ", e);
1310                         }
1311                 }
1312                 throw new VnfException (me);
1313             } catch (NullPointerException npe) {
1314                 String error = "Create VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + npe;
1315                 logger
1316                     .error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner, cloudSiteId,
1317                         tenantId, "OpenStack", ErrorCode.DataError.getValue(),
1318                         "NullPointerException - createStack", npe);
1319                 logger.debug(error);
1320                 logger.debug("NULL POINTER EXCEPTION at heat.createStack");
1321                 //npe.addContext ("CreateVNF");
1322                 throw new VnfException ("NullPointerException during heat.createStack");
1323             } catch (Exception e) {
1324                 logger.debug("unhandled exception at heat.createStack",e);
1325                 throw new VnfException("Exception during heat.createStack! " + e.getMessage());
1326             }
1327             // Reach this point if createStack is successful.
1328             // Populate remaining rollback info and response parameters.
1329             vfRollback.setVnfId (heatStack.getCanonicalName ());
1330             vfRollback.setVnfCreated (true);
1331
1332             vnfId.value = heatStack.getCanonicalName ();
1333             outputs.value = copyStringOutputs (heatStack.getOutputs ());
1334             rollback.value = vfRollback;
1335             if (isValetEnabled && sendResponseToValet) {
1336                 logger.debug("valet is enabled, the orchestration succeeded - now send confirm to valet with stack id");
1337                 try {
1338                     GenericValetResponse<ValetConfirmResponse> gvr = this.vci.callValetConfirmRequest(msoRequest.getRequestId(), heatStack.getCanonicalName());
1339                     // Nothing to really do here whether it succeeded or not other than log it.
1340                     logger.debug("Return code from Confirm response is {}", gvr.getStatusCode());
1341                 } catch (Exception e) {
1342                     logger.error("Exception encountered while sending Confirm to Valet ", e);
1343                 }
1344             }
1345             logger.debug ("VF Module {} successfully created", vfModuleName);
1346             //call heatbridge
1347             heatbridge(heatStack, cloudOwner, cloudSiteId, tenantId, genericVnfName, vfModuleId);
1348             return;
1349         } catch (Exception e) {
1350                 logger.debug("unhandled exception in create VF",e);
1351                 throw new VnfException("Exception during create VF " + e.getMessage());
1352         }
1353     }
1354
1355     @Override
1356     public void deleteVfModule (String cloudSiteId,
1357                            String cloudOwner,
1358                            String tenantId,
1359                            String vnfName,
1360                            MsoRequest msoRequest,
1361                            Holder <Map <String, String>> outputs) throws VnfException {
1362
1363         logger.debug("Deleting VF {} in ", vnfName, cloudOwner + "/" + cloudSiteId + "/" + tenantId);
1364         // Will capture execution time for metrics
1365         long startTime = System.currentTimeMillis ();
1366
1367         // 1702 capture the output parameters on a delete
1368         // so we'll need to query first
1369         Map<String, Object> stackOutputs = null;
1370         try {
1371             stackOutputs = heat.queryStackForOutputs(cloudSiteId, cloudOwner, tenantId, vnfName);
1372         } catch (MsoException me) {
1373             // Failed to query the Stack due to an openstack exception.
1374             // Convert to a generic VnfException
1375             me.addContext ("DeleteVFModule");
1376             String error = "Delete VFModule: Query to get outputs: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1377             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId,
1378                 tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(), "Exception - QueryStack",
1379                 me);
1380             logger.debug(error);
1381             throw new VnfException (me);
1382         }
1383         // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected Object types
1384         outputs.value = this.convertMapStringObjectToStringString(stackOutputs);
1385
1386         boolean isValetEnabled = this.checkBooleanProperty(MsoVnfAdapterImpl.VALET_ENABLED, false);
1387         boolean failRequestOnValetFailure = this.checkBooleanProperty(MsoVnfAdapterImpl.FAIL_REQUESTS_ON_VALET_FAILURE, false);
1388         logger.debug("isValetEnabled={}, failRequestsOnValetFailure={}", isValetEnabled, failRequestOnValetFailure);
1389         boolean valetDeleteRequestSucceeded = false;
1390         if (isValetEnabled) {
1391                 valetDeleteRequestSucceeded = this.valetDeleteRequest(cloudSiteId, cloudOwner, tenantId, vnfName, msoRequest, failRequestOnValetFailure);
1392         }
1393
1394         // Use the MsoHeatUtils to delete the stack. Set the polling flag to true.
1395         // The possible outcomes of deleteStack are a StackInfo object with status
1396         // of NOTFOUND (on success) or FAILED (on error). Also, MsoOpenstackException
1397         // could be thrown.
1398         long subStartTime = System.currentTimeMillis ();
1399         try {
1400             heat.deleteStack (tenantId, cloudOwner, cloudSiteId, vnfName, true);
1401         } catch (MsoException me) {
1402             me.addContext ("DeleteVNF");
1403             // Failed to query the Stack due to an openstack exception.
1404             // Convert to a generic VnfException
1405             String error = "Delete VF: " + vnfName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1406             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId,
1407                 tenantId, "OpenStack", "DeleteStack", ErrorCode.DataError.getValue(),
1408                 "Exception - deleteStack", me);
1409             logger.debug(error);
1410             if (isValetEnabled && valetDeleteRequestSucceeded) {
1411                 logger.debug("valet is enabled, the orchestration failed - now sending rollback to valet");
1412                 try {
1413                         GenericValetResponse<ValetRollbackResponse> gvr = this.vci.callValetRollbackRequest(msoRequest.getRequestId(), vnfName, false, me.getMessage());
1414                         // Nothing to really do here whether it succeeded or not other than log it.
1415                         logger.debug("Return code from Rollback response is {}", gvr.getStatusCode());
1416                 } catch (Exception e) {
1417                         logger.error("Exception encountered while sending Rollback to Valet ", e);
1418                 }
1419             }
1420             throw new VnfException (me);
1421         }
1422         if (isValetEnabled && valetDeleteRequestSucceeded) {
1423                 // only if the original request succeeded do we send a confirm
1424                 logger.debug("valet is enabled, the delete succeeded - now send confirm to valet");
1425                 try {
1426                 GenericValetResponse<ValetConfirmResponse> gvr = this.vci.callValetConfirmRequest(msoRequest.getRequestId(), vnfName);
1427                 // Nothing to really do here whether it succeeded or not other than log it.
1428               logger.debug("Return code from Confirm response is {}", gvr.getStatusCode());
1429             } catch (Exception e) {
1430               logger.error("Exception encountered while sending Confirm to Valet ", e);
1431             }
1432         }
1433
1434         // On success, nothing is returned.
1435         return;
1436     }
1437
1438     @Override
1439     public void updateVfModule (String cloudSiteId,
1440                            String cloudOwner,
1441                            String tenantId,
1442                            String vnfType,
1443                            String vnfVersion,
1444                            String vnfName,
1445                            String requestType,
1446                            String volumeGroupHeatStackId,
1447                            String baseVfHeatStackId,
1448                            String vfModuleStackId,
1449                            String modelCustomizationUuid,
1450                            Map <String, Object> inputs,
1451                            MsoRequest msoRequest,
1452                            Holder <Map <String, String>> outputs,
1453                            Holder <VnfRollback> rollback) throws VnfException {
1454         String vfModuleName = vnfName;
1455         String vfModuleType = vnfType;
1456         String methodName = "updateVfModule";
1457         String serviceName = VNF_ADAPTER_SERVICE_NAME + methodName;
1458
1459         StringBuilder sbInit = new StringBuilder();
1460         sbInit.append("updateVfModule: \n");
1461         sbInit.append("cloudOwner=" + cloudOwner + "\n");
1462         sbInit.append("cloudSiteId=" + cloudSiteId + "\n");
1463         sbInit.append("tenantId=" + tenantId + "\n");
1464         sbInit.append("vnfType=" + vnfType + "\n");
1465         sbInit.append("vnfVersion=" + vnfVersion + "\n");
1466         sbInit.append("vnfName=" + vnfName + "\n");
1467         sbInit.append("requestType=" + requestType + "\n");
1468         sbInit.append("volumeGroupHeatStackId=" + volumeGroupHeatStackId + "\n");
1469         sbInit.append("baseVfHeatStackId=" + baseVfHeatStackId + "\n");
1470         sbInit.append("vfModuleStackId=" + vfModuleStackId + "\n");
1471         sbInit.append("modelCustomizationUuid=" + modelCustomizationUuid + "\n");
1472       logger.debug(sbInit.toString());
1473
1474         String mcu = modelCustomizationUuid;
1475         boolean useMCUuid = false;
1476         if (mcu != null && !mcu.isEmpty()) {
1477             if ("null".equalsIgnoreCase(mcu)) {
1478                 logger.debug("modelCustomizationUuid: passed in as the string 'null' - will ignore: {}",
1479                     modelCustomizationUuid);
1480                 useMCUuid = false;
1481                 mcu = "";
1482             } else {
1483                 logger.debug("Found modelCustomizationUuid! Will use that: {}", mcu);
1484                 useMCUuid = true;
1485             }
1486         }
1487
1488         String requestTypeString = "";
1489         if (requestType != null && !"".equals(requestType)) {
1490                 requestTypeString = requestType;
1491         }
1492
1493         String nestedStackId = null;
1494         if (volumeGroupHeatStackId != null && !"".equals(volumeGroupHeatStackId) && !"null".equalsIgnoreCase(volumeGroupHeatStackId)) {
1495                 nestedStackId = volumeGroupHeatStackId;
1496         }
1497         String nestedBaseStackId = null;
1498         if (baseVfHeatStackId != null && !"".equals(baseVfHeatStackId) && !"null".equalsIgnoreCase(baseVfHeatStackId)) {
1499                 nestedBaseStackId = baseVfHeatStackId;
1500         }
1501
1502         if (inputs == null) {
1503                 // Create an empty set of inputs
1504                 inputs = new HashMap<>();
1505             logger.debug("inputs == null - setting to empty");
1506         }
1507
1508         boolean isBaseRequest = false;
1509         boolean isVolumeRequest = false;
1510         if (requestTypeString.startsWith("VOLUME")) {
1511                 isVolumeRequest = true;
1512         }
1513         if ((vfModuleName == null || "".equals(vfModuleName.trim())) && vfModuleStackId != null) {
1514                 vfModuleName = this.getVfModuleNameFromModuleStackId(vfModuleStackId);
1515         }
1516
1517         logger.debug ("Updating VFModule: " + vfModuleName + " of type " + vfModuleType + "in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId);
1518         logger.debug("requestTypeString = " + requestTypeString + ", nestedVolumeStackId = " + nestedStackId + ", nestedBaseStackId = " + nestedBaseStackId);
1519
1520         // Will capture execution time for metrics
1521         long startTime = System.currentTimeMillis ();
1522
1523         // Build a default rollback object (no actions performed)
1524         VnfRollback vfRollback = new VnfRollback ();
1525         vfRollback.setCloudSiteId (cloudSiteId);
1526         vfRollback.setCloudOwner (cloudOwner);
1527         vfRollback.setTenantId (tenantId);
1528         vfRollback.setMsoRequest (msoRequest);
1529         vfRollback.setRequestType(requestTypeString);
1530         vfRollback.setVolumeGroupHeatStackId(volumeGroupHeatStackId);
1531         vfRollback.setBaseGroupHeatStackId(baseVfHeatStackId);
1532         vfRollback.setIsBase(isBaseRequest);
1533         vfRollback.setVfModuleStackId(vfModuleStackId);
1534         vfRollback.setModelCustomizationUuid(mcu);
1535
1536         StackInfo heatStack = null;
1537         long queryStackStarttime = System.currentTimeMillis ();
1538         logger.debug("UpdateVfModule - querying for {}", vfModuleName);
1539         try {
1540             heatStack = heat.queryStack (cloudSiteId, cloudOwner, tenantId, vfModuleName);
1541         } catch (MsoException me) {
1542             // Failed to query the Stack due to an openstack exception.
1543             // Convert to a generic VnfException
1544             me.addContext ("UpdateVFModule");
1545             String error = "Update VFModule: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1546             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner, cloudSiteId,
1547                 tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(), "Exception - QueryStack",
1548                 me);
1549             logger.debug(error);
1550             throw new VnfException (me);
1551         }
1552
1553         //TODO - do we need to check for the other status possibilities?
1554         if (heatStack == null || heatStack.getStatus () == HeatStatus.NOTFOUND) {
1555             // Not Found
1556             String error = "Update VF: Stack " + vfModuleName + " does not exist in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId;
1557             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_NOT_EXIST.toString(), vfModuleName, cloudOwner, cloudSiteId,
1558                 tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(), error);
1559             throw new VnfNotFound (cloudSiteId, cloudOwner, tenantId, vfModuleName);
1560         } else {
1561             logger.debug("Found Existing stack, status={}", heatStack.getStatus());
1562             // Populate the outputs from the existing stack.
1563             outputs.value = copyStringOutputs (heatStack.getOutputs ());
1564             rollback.value = vfRollback; // Default rollback - no updates performed
1565         }
1566
1567         // 1604 Cinder Volume support - handle a nestedStackId if sent (volumeGroupHeatStackId):
1568         StackInfo nestedHeatStack = null;
1569         long queryStackStarttime2 = System.currentTimeMillis ();
1570         Map<String, Object> nestedVolumeOutputs = null;
1571         if (nestedStackId != null) {
1572                 try {
1573                         logger.debug("Querying for nestedStackId = {}", nestedStackId);
1574                         nestedHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedStackId);
1575                 } catch (MsoException me) {
1576                     // Failed to query the Stack due to an openstack exception.
1577                     // Convert to a generic VnfException
1578                     me.addContext ("UpdateVFModule");
1579                     String error = "Update VF: Attached heatStack ID Query " + nestedStackId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
1580               logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId,
1581                   tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(), "Exception - " + error,
1582                   me);
1583               logger.debug("ERROR trying to query nested stack= {}", error);
1584               throw new VnfException (me);
1585                 }
1586                 if (nestedHeatStack == null || nestedHeatStack.getStatus() == HeatStatus.NOTFOUND) {
1587                     String error = "Update VFModule: Attached volume heatStack ID DOES NOT EXIST " + nestedStackId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
1588               logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfName, cloudOwner, cloudSiteId,
1589                   tenantId, error, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(), error);
1590               logger.debug(error);
1591                     throw new VnfException (error, MsoExceptionCategory.USERDATA);
1592                 } else {
1593                         logger.debug("Found nested heat stack - copying values to inputs *later*");
1594                         nestedVolumeOutputs = nestedHeatStack.getOutputs();
1595                         heat.copyStringOutputsToInputs(inputs, nestedHeatStack.getOutputs(), false);
1596                 }
1597         }
1598         // handle a nestedBaseStackId if sent - this is the stack ID of the base.
1599         StackInfo nestedBaseHeatStack = null;
1600         Map<String, Object> baseStackOutputs = null;
1601         if (nestedBaseStackId != null) {
1602             long queryStackStarttime3 = System.currentTimeMillis ();
1603                 try {
1604                         logger.debug("Querying for nestedBaseStackId = {}", nestedBaseStackId);
1605                         nestedBaseHeatStack = heat.queryStack(cloudSiteId, cloudOwner, tenantId, nestedBaseStackId);
1606                 } catch (MsoException me) {
1607                     // Failed to query the Stack due to an openstack exception.
1608                     // Convert to a generic VnfException
1609                     me.addContext ("UpdateVfModule");
1610                     String error = "Update VFModule: Attached baseHeatStack ID Query " + nestedBaseStackId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
1611               logger
1612                   .error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, cloudOwner, cloudSiteId,
1613                       tenantId, "OpenStack", "QueryStack", ErrorCode.DataError.getValue(),
1614                       "Exception - " + error, me);
1615               logger.debug("ERROR trying to query nested base stack= {}", error);
1616                     throw new VnfException (me);
1617                 }
1618                 if (nestedBaseHeatStack == null || nestedBaseHeatStack.getStatus() == HeatStatus.NOTFOUND) {
1619                     String error = "Update VFModule: Attached base heatStack ID DOES NOT EXIST " + nestedBaseStackId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
1620                     logger.error ("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
1621                   cloudOwner, cloudSiteId, tenantId, error, "OpenStack",
1622                   "QueryStack", ErrorCode.DataError.getValue(), error);
1623                     logger.debug(error);
1624                     throw new VnfException (error, MsoExceptionCategory.USERDATA);
1625                 } else {
1626                         logger.debug("Found nested base heat stack - copying values to inputs *later*");
1627                         baseStackOutputs = nestedBaseHeatStack.getOutputs();
1628                         heat.copyStringOutputsToInputs(inputs, nestedBaseHeatStack.getOutputs(), false);
1629                 }
1630         }
1631
1632         // Ready to deploy the new VNF
1633
1634
1635
1636             // Retrieve the VF definition
1637             VnfResource vnfResource = null;
1638                 VfModule vf = null;
1639                 VfModuleCustomization vfmc = null;
1640             if (useMCUuid){
1641                         vfmc = vfModuleCustomRepo.findByModelCustomizationUUID(modelCustomizationUuid);
1642                         vf = vfmc != null ? vfmc.getVfModule() : null;
1643                 if (vf == null) {
1644                     logger.debug("Unable to find a vfModule matching modelCustomizationUuid={}", mcu);
1645                 }
1646                 } else {
1647                         logger.debug("1707 and later - MUST PROVIDE Model Customization UUID!");
1648             }
1649                 if (vf == null) {
1650                   String error = "Update VfModule: unable to find vfModule with modelCustomizationUuid=" + mcu;
1651                   logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "VF Module Type",
1652                       vfModuleType, "OpenStack", ErrorCode.DataError.getValue(), error);
1653                   throw new VnfException(error, MsoExceptionCategory.USERDATA);
1654             }
1655             logger.debug("Got VF module definition from Catalog: {}", vf.toString());
1656             if (vf.getIsBase()) {
1657                 isBaseRequest = true;
1658                 logger.debug("This a BASE update request");
1659             } else {
1660                 logger.debug("This is *not* a BASE VF update request");
1661                 if (!isVolumeRequest && nestedBaseStackId == null) {
1662                         logger.debug("DANGER WILL ROBINSON! This is unexpected - no nestedBaseStackId with this non-base request");
1663                 }
1664             }
1665
1666             //1607 - Add version check
1667             // First - see if it's in the VnfResource record
1668             // if we have a vf Module - then we have to query to get the VnfResource record.
1669             if (vf.getModelUUID() != null) {
1670                 String vnfResourceModelUuid = vf.getModelUUID();
1671
1672                 vnfResource = vf.getVnfResources();
1673                 if (vnfResource == null) {
1674                         logger.debug("Unable to find vnfResource at ? will not error for now...", vnfResourceModelUuid);
1675                 }
1676             }
1677
1678             String minVersionVnf = null;
1679             String maxVersionVnf = null;
1680             if (vnfResource != null) {
1681                 try {
1682                         minVersionVnf = vnfResource.getAicVersionMin();
1683                         maxVersionVnf = vnfResource.getAicVersionMax();
1684                 } catch (Exception e) {
1685                         logger.debug("Unable to pull min/max version for this VNF Resource entry",e);
1686                         minVersionVnf = null;
1687                         maxVersionVnf = null;
1688                         }
1689                 if (minVersionVnf != null && "".equals(minVersionVnf)) {
1690                         minVersionVnf = null;
1691                 }
1692                 if (maxVersionVnf != null && "".equals(maxVersionVnf)) {
1693                         maxVersionVnf = null;
1694                         }
1695                 }
1696                         if (minVersionVnf != null && maxVersionVnf != null) {
1697                                 MavenLikeVersioning aicV = new MavenLikeVersioning();
1698
1699                                 // double check
1700                         if (this.cloudConfig != null) {
1701                                 Optional<CloudSite> cloudSiteOpt = this.cloudConfig.getCloudSite(cloudSiteId);
1702                                 if (cloudSiteOpt.isPresent()) {
1703                                         aicV.setVersion(cloudSiteOpt.get().getCloudVersion());
1704                                         boolean moreThanMin = true;
1705                                         boolean equalToMin = true;
1706                                         boolean moreThanMax = true;
1707                                         boolean equalToMax = true;
1708                                         boolean doNotTest = false;
1709                                         try {
1710                                                 moreThanMin = aicV.isMoreRecentThan(minVersionVnf);
1711                                                 equalToMin = aicV.isTheSameVersion(minVersionVnf);
1712                                                 moreThanMax = aicV.isMoreRecentThan(maxVersionVnf);
1713                                                 equalToMax = aicV.isTheSameVersion(maxVersionVnf);
1714                                         } catch (Exception e) {
1715               logger.debug("An exception occured while trying to test AIC Version {} - will default to not check",
1716                   e.getMessage(), e);
1717               doNotTest = true;
1718                                         }
1719                                         if (!doNotTest) {
1720                                                 if ((moreThanMin || equalToMin) // aic >= min
1721                                                                 && ((equalToMax) || !(moreThanMax))) { // aic <= max
1722                 logger.debug(
1723                     "VNF Resource " + vnfResource.getModelName() + " VersionMin=" + minVersionVnf + " VersionMax:"
1724                         + maxVersionVnf + " supported on Cloud: " + cloudSiteId + " with AIC_Version:" + aicV);
1725             } else {
1726                                                         // ERROR
1727                                                         String error = "VNF Resource type: " + vnfResource.getModelName() + " VersionMin="
1728                                                                         + minVersionVnf + " VersionMax:" + maxVersionVnf + " NOT supported on Cloud: "
1729                                                                         + cloudSiteId + " with AIC_Version:" + aicV;
1730                 logger.error("{} {} {} {} {}", MessageEnum.RA_CONFIG_EXC.toString(), error, "OpenStack",
1731                     ErrorCode.BusinessProcesssError.getValue(), "Exception - setVersion");
1732                 logger.debug(error);
1733                                                         throw new VnfException(error, MsoExceptionCategory.USERDATA);
1734                                                 }
1735                                         } else {
1736                                                 logger.debug("bypassing testing AIC version...");
1737                                         }
1738                                 } // let this error out downstream to avoid introducing uncertainty at this stage
1739                 } else {
1740                                         logger.debug("cloudConfig is NULL - cannot check cloud site version");
1741                 }
1742
1743                         } else {
1744                                 logger.debug("AIC Version not set in VNF_Resource - do not error for now - not checked.");
1745             }
1746                         // End Version check 1607
1747
1748             HeatTemplate heatTemplate = null;
1749             HeatEnvironment heatEnvironment = null;
1750             if (isVolumeRequest) {
1751                                 heatTemplate = vf.getVolumeHeatTemplate();
1752                                 heatEnvironment = vfmc.getVolumeHeatEnv();
1753                         } else {
1754                                 heatTemplate = vf.getModuleHeatTemplate();
1755                                 heatEnvironment = vfmc.getHeatEnvironment();
1756                         }
1757
1758                         if (heatTemplate == null) {
1759                                 String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType=" + requestTypeString;
1760           logger
1761               .error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID", vfModuleType,
1762                   "OpenStack", ErrorCode.DataError.getValue(), error);
1763           throw new VnfException(error, MsoExceptionCategory.INTERNAL);
1764                         } else {
1765           logger.debug("Got HEAT Template from DB: {}", heatTemplate.getHeatTemplate());
1766       }
1767
1768             if (heatEnvironment == null) {
1769                String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType;
1770                 logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID",
1771                     "OpenStack", ErrorCode.DataError.getValue(), error);
1772                     throw new VnfException (error, MsoExceptionCategory.INTERNAL);
1773             } else {
1774                 logger.debug ("Got Heat Environment from DB: {}", heatEnvironment.getEnvironment());
1775             }
1776
1777         logger.debug("In MsoVnfAdapterImpl, about to call db.getNestedTemplates avec templateId={}",
1778             heatTemplate.getArtifactUuid());
1779
1780
1781             List<HeatTemplate> nestedTemplates = heatTemplate.getChildTemplates();
1782             Map <String, Object> nestedTemplatesChecked = new HashMap <> ();
1783             if (nestedTemplates != null && !nestedTemplates.isEmpty()) {
1784                 // for debugging print them out
1785                 logger.debug("Contents of nestedTemplates - to be added to files: on stack:");
1786                 for (HeatTemplate entry : nestedTemplates) {
1787
1788                     nestedTemplatesChecked.put (entry.getTemplateName(), entry.getTemplateBody());
1789                     logger.debug(entry.getTemplateName() + " -> " + entry.getTemplateBody());
1790                 }
1791             } else {
1792                 logger.debug("No nested templates found - nothing to do here");
1793                 nestedTemplatesChecked = null;
1794             }
1795
1796             // Also add the files: for any get_files associated with this VfModule
1797             // *if* there are any
1798             logger.debug("In MsoVnfAdapterImpl.updateVfModule, about to call db.getHeatFiles avec vfModuleId={}",
1799             vf.getModelUUID());
1800
1801             List<HeatFiles> heatFiles = null;
1802             Map <String, Object> heatFilesObjects = new HashMap <> ();
1803
1804             // Add ability to turn on adding get_files with volume requests (by property).
1805             boolean addGetFilesOnVolumeReq = false;
1806             try {
1807                 String propertyString = this.environment.getProperty(MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ);
1808                 if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
1809                         addGetFilesOnVolumeReq = true;
1810                         logger.debug("AddGetFilesOnVolumeReq - setting to true! {}", propertyString);
1811                 }
1812             } catch (Exception e) {
1813                 logger.debug("An error occured trying to get property {} - default to false",
1814                     MsoVnfAdapterImpl.ADD_GET_FILES_ON_VOLUME_REQ, e);
1815             }
1816             if (!isVolumeRequest || addGetFilesOnVolumeReq) {
1817             logger.debug("In MsoVnfAdapterImpl updateVfModule, about to call db.getHeatFilesForVfModule avec "
1818                 + "vfModuleId={}", vf.getModelUUID());
1819
1820                 heatFiles = vf.getHeatFiles();
1821                 if (heatFiles != null && !heatFiles.isEmpty()) {
1822                     // add these to stack - to be done in createStack
1823                     // here, we will map them to Map<String, Object> from Map<String, HeatFiles>
1824                     // this will match the nested templates format
1825                     logger.debug("Contents of heatFiles - to be added to files: on stack:");
1826                     for (HeatFiles heatfile : heatFiles) {
1827                         logger.debug(heatfile.getFileName() + " -> " + heatfile.getFileBody());
1828                         heatFilesObjects.put(heatfile.getFileName(), heatfile.getFileBody());
1829                     }
1830                 } else {
1831                     logger.debug("No heat files found -nothing to do here");
1832                     heatFilesObjects = null;
1833                 }
1834             }
1835
1836             // Check that required parameters have been supplied
1837             String missingParams = null;
1838             List <String> paramList = new ArrayList <> ();
1839
1840             // New for 1510 - consult the PARAM_ALIAS field to see if we've been
1841             // supplied an alias. Only check if we don't find it initially.
1842             // Also new in 1510 - don't flag missing parameters if there's an environment - because they might be there.
1843             // And also new - add parameter to turn off checking all together if we find we're blocking orders we
1844             // shouldn't
1845             boolean checkRequiredParameters = true;
1846             try {
1847                 String propertyString = this.environment.getProperty (MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1848                 if ("false".equalsIgnoreCase (propertyString) || "n".equalsIgnoreCase (propertyString)) {
1849                     checkRequiredParameters = false;
1850                     logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking...",
1851                         MsoVnfAdapterImpl.CHECK_REQD_PARAMS);
1852                 }
1853             } catch (Exception e) {
1854                 // No problem - default is true
1855                 logger.debug ("An exception occured trying to get property {}", MsoVnfAdapterImpl.CHECK_REQD_PARAMS,
1856                     e);
1857             }
1858          // 1604 - Add enhanced environment & parameter checking
1859             // Part 1: parse envt entries to see if reqd parameter is there (before used a simple grep
1860             // Part 2: only submit to openstack the parameters in the envt that are in the heat template
1861             // Note this also removes any comments
1862             MsoHeatEnvironmentEntry mhee = null;
1863             if (heatEnvironment != null && heatEnvironment.getEnvironment().toLowerCase ().contains ("parameters:")) {
1864                 logger.debug("Enhanced environment checking enabled - 1604");
1865                 StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
1866                 mhee = new MsoHeatEnvironmentEntry(sb);
1867                 StringBuilder sb2 = new StringBuilder("\nHeat Template Parameters:\n");
1868                 for (HeatTemplateParam parm : heatTemplate.getParameters()) {
1869                         sb2.append("\t" + parm.getParamName() + ", required=" + parm.isRequired());
1870                 }
1871                 if (!mhee.isValid()) {
1872                         sb2.append("Environment says it's not valid! " + mhee.getErrorString());
1873                 } else {
1874                         sb2.append("\nEnvironment:");
1875                         sb2.append(mhee.toFullString());
1876                 }
1877                 logger.debug(sb2.toString());
1878             } else {
1879                 logger.debug("NO ENVIRONMENT for this entry");
1880             }
1881             // New for 1607 - support params of json type
1882             HashMap<String, JsonNode> jsonParams = new HashMap<>();
1883             boolean hasJson = false;
1884
1885             for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
1886                 logger.debug ("Parameter:'" + parm.getParamName ()
1887                               + "', isRequired="
1888                               + parm.isRequired ()
1889                               + ", alias="
1890                               + parm.getParamAlias ());
1891                 // handle json
1892                 String parameterType = parm.getParamType();
1893                 if (parameterType == null || "".equals(parameterType.trim())) {
1894                         parameterType = "String";
1895                 }
1896                 JsonNode jsonNode = null;
1897                 if ("json".equalsIgnoreCase(parameterType) && inputs != null) {
1898                         if (inputs.containsKey(parm.getParamName()) ) {
1899                                 hasJson = true;
1900                                 String jsonString = null;
1901                                 try {
1902                                         jsonString = JSON_MAPPER.writeValueAsString(inputs.get(parm.getParamName()));
1903                                         jsonNode = JSON_MAPPER.readTree(jsonString);
1904                                 } catch (JsonParseException jpe) {
1905                                         //TODO - what to do here?
1906                                         //for now - send the error to debug
1907                                         logger.debug("Json Error Converting {} - {}", parm.getParamName(), jpe.getMessage(), jpe);
1908                                         hasJson = false;
1909                                         jsonNode = null;
1910                                 } catch (Exception e) {
1911                         // or here?
1912                         logger.debug("Json Error Converting {} {}", parm.getParamName(), e.getMessage(), e);
1913                         hasJson = false;
1914                         jsonNode = null;
1915                     }
1916                                 if (jsonNode != null) {
1917                                         jsonParams.put(parm.getParamName(), jsonNode);
1918                                 }
1919                         } else if (inputs.containsKey(parm.getParamAlias())) {
1920                                 hasJson = true;
1921                                 String jsonString = null;
1922                                 try {
1923                                         jsonString = (String)inputs.get(parm.getParamAlias());
1924                                         jsonNode = JSON_MAPPER.readTree(jsonString);
1925                                 } catch (JsonParseException jpe) {
1926                                         //TODO - what to do here?
1927                                         //for now - send the error to debug, but just leave it as a String
1928                                         String errorMessage = jpe.getMessage();
1929                                         logger.debug("Json Error Converting " + parm.getParamName() + " - " + errorMessage,jpe);
1930                                         hasJson = false;
1931                                         jsonNode = null;
1932                                 } catch (Exception e) {
1933                           // or here?
1934                           logger.debug("Json Error Converting " + parm.getParamName() + " " + e.getMessage(), e);
1935                           hasJson = false;
1936                           jsonNode = null;
1937                       }
1938                                 if (jsonNode != null) {
1939                                         // Notice here - we add it to the jsonParams hashMap with the actual name -
1940                                         // then manipulate the inputs so when we check for aliases below - it will not
1941                                         // get flagged.
1942                                         jsonParams.put(parm.getParamName(), jsonNode);
1943                                         inputs.remove(parm.getParamAlias());
1944                                         inputs.put(parm.getParamName(), jsonString);
1945                                 }
1946                         } //TODO add a check for the parameter in the env file
1947                 }
1948
1949                 if (parm.isRequired () && (inputs == null || !inputs.containsKey (parm.getParamName ()))) {
1950                     if (inputs.containsKey (parm.getParamAlias ())) {
1951                         // They've submitted using an alias name. Remove that from inputs, and add back using real name.
1952                         String realParamName = parm.getParamName ();
1953                         String alias = parm.getParamAlias ();
1954                         Object value = inputs.get (alias);
1955                         logger.debug ("*Found an Alias: paramName=" + realParamName
1956                                       + ",alias="
1957                                       + alias
1958                                       + ",value="
1959                                       + value);
1960                         inputs.remove (alias);
1961                         inputs.put (realParamName, value);
1962                         logger.debug ("{} entry removed from inputs, added back using {}", alias, realParamName);
1963                     }
1964                     // enhanced - check if it's in the Environment (note: that method
1965                     else if (mhee != null && mhee.containsParameter(parm.getParamName())) {
1966
1967                         logger.debug("Required parameter {} appears to be in environment - do not count as missing",
1968                             parm.getParamName());
1969                     }
1970                     else {
1971                         logger.debug("adding to missing parameters list: {}", parm.getParamName());
1972                         if (missingParams == null) {
1973                             missingParams = parm.getParamName ();
1974                         } else {
1975                             missingParams += "," + parm.getParamName ();
1976                         }
1977                     }
1978                 }
1979                 paramList.add (parm.getParamName ());
1980             }
1981
1982
1983             if (missingParams != null) {
1984                 // Problem - missing one or more required parameters
1985                 if (checkRequiredParameters) {
1986                 String error = "Update VNF: Missing Required inputs: " + missingParams;
1987                   logger.error("{} {} {} {} {}", MessageEnum.RA_MISSING_PARAM.toString(), missingParams, "OpenStack",
1988                       ErrorCode.DataError.getValue(), error);
1989                 throw new VnfException (error, MsoExceptionCategory.USERDATA);
1990                 } else {
1991                   logger.debug("found missing parameters - but checkRequiredParameters is false - will not block");
1992               }
1993             } else {
1994                 logger.debug("No missing parameters found - ok to proceed");
1995             }
1996
1997             // Just submit the envt entry as received from the database
1998             String newEnvironmentString = null;
1999             if (mhee != null) {
2000                 newEnvironmentString = mhee.getRawEntry().toString();
2001             }
2002             // Remove any extraneous parameters (don't throw an error)
2003             if (inputs != null) {
2004                 List <String> extraParams = new ArrayList <> ();
2005                 extraParams.addAll (inputs.keySet ());
2006                 // This is not a valid parameter for this template
2007                 extraParams.removeAll (paramList);
2008                 if (!extraParams.isEmpty ()) {
2009                     logger.warn("{} {} {} {} {} {}", MessageEnum.RA_VNF_EXTRA_PARAM.toString(), vnfType,
2010                         extraParams.toString(), "OpenStack", ErrorCode.DataError.getValue(), "Extra params");
2011                     inputs.keySet ().removeAll (extraParams);
2012                 }
2013             }
2014             Map<String, Object> goldenInputs = copyStringInputs(inputs);
2015             // 1607 - when we get here - we have clean inputs. Create inputsTwo in case we have json
2016             Map<String, Object> inputsTwo = null;
2017             if (hasJson && jsonParams.size() > 0) {
2018                 inputsTwo = new HashMap<>();
2019                 for (Map.Entry<String, Object> entry : inputs.entrySet()) {
2020                         String keyParamName = entry.getKey();
2021                         Object value = entry.getValue();
2022                         if (jsonParams.containsKey(keyParamName)) {
2023                                 inputsTwo.put(keyParamName, jsonParams.get(keyParamName));
2024                         } else {
2025                                 inputsTwo.put(keyParamName, value);
2026                         }
2027                 }
2028                 goldenInputs = inputsTwo;
2029             }
2030
2031             // "Fix" the template if it has CR/LF (getting this from Oracle)
2032             String template = heatTemplate.getHeatTemplate ();
2033             template = template.replaceAll ("\r\n", "\n");
2034
2035             boolean isValetEnabled = this.checkBooleanProperty(MsoVnfAdapterImpl.VALET_ENABLED, false);
2036             boolean failRequestOnValetFailure = this.checkBooleanProperty(MsoVnfAdapterImpl.FAIL_REQUESTS_ON_VALET_FAILURE, false);
2037             logger.debug("isValetEnabled={}, failRequestsOnValetFailure={}", isValetEnabled, failRequestOnValetFailure);
2038             if (isVolumeRequest) {
2039                 isValetEnabled = false;
2040                 logger.debug("never send a volume request to valet");
2041             }
2042             boolean sendResponseToValet = false;
2043             if (isValetEnabled) {
2044                                 Holder<Map<String, Object>> valetModifiedParamsHolder = new Holder<>();
2045                         String parsedVfModuleName = this.getVfModuleNameFromModuleStackId(vfModuleStackId);
2046                         // Make sure it is set to something.
2047                         if (parsedVfModuleName == null || parsedVfModuleName.isEmpty()) {
2048                                 parsedVfModuleName = "unknown";
2049                         }
2050                                 sendResponseToValet = this.valetUpdateRequest(cloudSiteId, cloudOwner, tenantId, heatFilesObjects,
2051                                         nestedTemplatesChecked, parsedVfModuleName, false, heatTemplate, newEnvironmentString, (HashMap<String, Object>) goldenInputs,
2052                                                 msoRequest, inputs, failRequestOnValetFailure, valetModifiedParamsHolder);
2053                                 if (sendResponseToValet) {
2054                                         goldenInputs = valetModifiedParamsHolder.value;
2055                                 }
2056             }
2057
2058             // Have the tenant. Now deploy the stack itself
2059             // Ignore MsoTenantNotFound and MsoStackAlreadyExists exceptions
2060             // because we already checked for those.
2061             long updateStackStarttime = System.currentTimeMillis ();
2062             try {
2063                                 heatStack = heatU.updateStack(
2064                                         cloudSiteId,
2065                                         cloudOwner,
2066                                         tenantId,
2067                                         vfModuleName,
2068                                         template,
2069                                         goldenInputs,
2070                                         true,
2071                                         heatTemplate.getTimeoutMinutes(),
2072                                         newEnvironmentString,
2073                                         //heatEnvironmentString,
2074                                         nestedTemplatesChecked,
2075                                         heatFilesObjects
2076                                 );
2077             } catch (MsoException me) {
2078                 me.addContext ("UpdateVFModule");
2079                 String error = "Update VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
2080                 logger
2081                     .error("{} {} {} {} {} {} {} {}", MessageEnum.RA_UPDATE_VNF_ERR.toString(), vfModuleType, cloudOwner, cloudSiteId,
2082                         tenantId, "OpenStack", ErrorCode.DataError.getValue(), "Exception - " + error, me);
2083                 if (isValetEnabled && sendResponseToValet) {
2084                         logger.debug("valet is enabled, the orchestration failed - now sending rollback to valet");
2085                         try {
2086                                 GenericValetResponse<ValetRollbackResponse> gvr = this.vci.callValetRollbackRequest(msoRequest.getRequestId(), null, false, me.getMessage());
2087                                 // Nothing to really do here whether it succeeded or not other than log it.
2088                                 logger.debug("Return code from Rollback response is {}", gvr.getStatusCode());
2089                         } catch (Exception e) {
2090                                 logger.error("Exception encountered while sending Rollback to Valet ", e);
2091                         }
2092                 }
2093                 throw new VnfException (me);
2094             }
2095
2096
2097         // Reach this point if updateStack is successful.
2098         // Populate remaining rollback info and response parameters.
2099         vfRollback.setVnfId (heatStack.getCanonicalName ());
2100         vfRollback.setVnfCreated (true);
2101
2102         if (isValetEnabled && sendResponseToValet) {
2103                 logger.debug("valet is enabled, the update succeeded - now send confirm to valet with stack id");
2104                 try {
2105                 GenericValetResponse<ValetConfirmResponse> gvr = this.vci.callValetConfirmRequest(msoRequest.getRequestId(), heatStack.getCanonicalName());
2106                 // Nothing to really do here whether it succeeded or not other than log it.
2107                 logger.debug("Return code from Confirm response is {}", gvr.getStatusCode());
2108             } catch (Exception e) {
2109                 logger.error("Exception encountered while sending Confirm to Valet ", e);
2110             }
2111         }
2112
2113         outputs.value = copyStringOutputs (heatStack.getOutputs ());
2114         rollback.value = vfRollback;
2115         return;
2116     }
2117
2118     private String getVfModuleNameFromModuleStackId(String vfModuleStackId) {
2119         // expected format of vfModuleStackId is "MSOTEST51-vSAMP3_base_module-0/1fc1f86c-7b35-447f-99a6-c23ec176ae24"
2120         // before the "/" is the vfModuleName and after the "/" is the heat stack id in Openstack
2121         if (vfModuleStackId == null)
2122                 return null;
2123         int index = vfModuleStackId.lastIndexOf('/');
2124         if (index <= 0)
2125                 return null;
2126         String vfModuleName = null;
2127         try {
2128                 vfModuleName = vfModuleStackId.substring(0, index);
2129         } catch (Exception e) {
2130                 logger.debug("Exception", e);
2131                 vfModuleName = null;
2132         }
2133         return vfModuleName;
2134     }
2135
2136     /*
2137      * Helper method to check a boolean property value - on error return provided default
2138      */
2139     private boolean checkBooleanProperty(String propertyName, boolean defaultValue) {
2140         boolean property = defaultValue;
2141         try {
2142                 String propertyString = this.environment.getProperty(propertyName);
2143                 if ("true".equalsIgnoreCase(propertyString) || "y".equalsIgnoreCase(propertyString)) {
2144                         property = true;
2145                 } else if ("false".equalsIgnoreCase(propertyString) || "n".equalsIgnoreCase(propertyString)) {
2146                         property = false;
2147                 }
2148         } catch (Exception e) {
2149           logger.debug("An exception occured trying to get property {} - defaulting to ", propertyName, defaultValue, e);
2150           property = defaultValue;
2151         }
2152         return property;
2153     }
2154
2155     /*
2156      * Helper method to combine getFiles and nestedTemplates in to a single Map
2157      */
2158     private Map<String, Object> combineGetFilesAndNestedTemplates(Map<String, Object> getFiles, Map<String, Object> nestedTemplates) {
2159                 boolean haveGetFiles = true;
2160                 boolean haveNestedTemplates = true;
2161                 Map<String, Object> files = new HashMap<String, Object>();
2162                 if (getFiles == null || getFiles.isEmpty()) {
2163                         haveGetFiles = false;
2164                 }
2165                 if (nestedTemplates == null || nestedTemplates.isEmpty()) {
2166                         haveNestedTemplates = false;
2167                 }
2168         if (haveGetFiles && haveNestedTemplates) {
2169             for (String keyString : getFiles.keySet ()) {
2170                 files.put (keyString, getFiles.get (keyString));
2171             }
2172             for (String keyString : nestedTemplates.keySet ()) {
2173                 files.put (keyString, nestedTemplates.get (keyString));
2174             }
2175         } else {
2176             // Handle if we only have one or neither:
2177             if (haveGetFiles) {
2178                 files = getFiles;
2179             }
2180             if (haveNestedTemplates) {
2181                 files = nestedTemplates;
2182             }
2183         }
2184         return files;
2185     }
2186
2187     /*
2188      * Valet Create request
2189      */
2190     private boolean valetCreateRequest(String cloudSiteId, String cloudOwner, String tenantId, Map<String, Object> heatFilesObjects, Map<String, Object> nestedTemplatesChecked,
2191                 String vfModuleName, boolean backout, HeatTemplate heatTemplate, String newEnvironmentString, Map<String, Object> goldenInputs,
2192                 MsoRequest msoRequest, Map<String, Object> inputs, boolean failRequestOnValetFailure, Holder<Map<String, Object>> valetModifiedParamsHolder) throws VnfException {
2193                 boolean valetSucceeded = false;
2194                 String valetErrorMessage = "more detail not available";
2195                 try {
2196                         String keystoneUrl = heat.getCloudSiteKeystoneUrl(cloudSiteId);
2197                         Map<String, Object> files = this.combineGetFilesAndNestedTemplates(heatFilesObjects,
2198                                         nestedTemplatesChecked);
2199                         HeatRequest heatRequest = new HeatRequest(vfModuleName, backout, heatTemplate.getTimeoutMinutes(),
2200                                         heatTemplate.getTemplateBody(), newEnvironmentString, files, goldenInputs);
2201                         GenericValetResponse<ValetCreateResponse> createReq = this.vci.callValetCreateRequest(msoRequest.getRequestId(),
2202                                         cloudSiteId, cloudOwner, tenantId, msoRequest.getServiceInstanceId(), (String)inputs.get("vnf_id"),
2203                                         (String)inputs.get("vnf_name"), (String)inputs.get("vf_module_id"), (String)inputs.get("vf_module_name"), keystoneUrl,
2204                                         heatRequest);
2205                         ValetCreateResponse vcr = createReq.getReturnObject();
2206                         if (vcr != null && createReq.getStatusCode() == 200) {
2207                                 ValetStatus status = vcr.getStatus();
2208                                 if (status != null) {
2209                                         String statusCode = status.getStatus(); // "ok" or "failed"
2210                                         if ("ok".equalsIgnoreCase(statusCode)) {
2211                                                 Map<String, Object> newInputs = vcr.getParameters();
2212                                                 if (newInputs != null) {
2213                                                         Map<String, Object> oldGold = goldenInputs;
2214                                                         logger.debug("parameters before being modified by valet:{}", oldGold.toString());
2215                                                         goldenInputs = new HashMap<String, Object>();
2216                                                         for (String key : newInputs.keySet()) {
2217                                                                 goldenInputs.put(key, newInputs.get(key));
2218                                                         }
2219                                                         valetModifiedParamsHolder.value = goldenInputs;
2220                                                         logger.debug("parameters after being modified by valet:{}", goldenInputs.toString());
2221                                                         valetSucceeded = true;
2222                                                 }
2223                                         } else {
2224                                                 valetErrorMessage = status.getMessage();
2225                                         }
2226                                 }
2227                         } else {
2228                                 logger.debug("Got a bad response back from valet");
2229                                 valetErrorMessage = "Bad response back from Valet";
2230                                 valetSucceeded = false;
2231                         }
2232                 } catch (Exception e) {
2233         logger.error("An exception occurred trying to call valet ...", e);
2234         valetSucceeded = false;
2235         valetErrorMessage = e.getMessage();
2236     }
2237                 if (failRequestOnValetFailure && !valetSucceeded) {
2238                         // The valet request failed - and property says to fail the request
2239                         //TODO Create a new exception class for valet?
2240                         throw new VnfException("A failure occurred with Valet: " + valetErrorMessage);
2241                 }
2242                 return valetSucceeded;
2243     }
2244
2245     /*
2246      * Valet update request
2247      */
2248
2249         private boolean valetUpdateRequest(String cloudSiteId, String cloudOwnerId, String tenantId,
2250                         Map<String, Object> heatFilesObjects, Map<String, Object> nestedTemplatesChecked, String vfModuleName,
2251                         boolean backout, HeatTemplate heatTemplate, String newEnvironmentString,
2252                         Map<String, Object> goldenInputs, MsoRequest msoRequest, Map<String, Object> inputs,
2253                         boolean failRequestOnValetFailure, Holder<Map<String, Object>> valetModifiedParamsHolder) throws VnfException {
2254
2255                 boolean valetSucceeded = false;
2256                 String valetErrorMessage = "more detail not available";
2257                 try {
2258                         String keystoneUrl = heat.getCloudSiteKeystoneUrl(cloudSiteId);
2259                         Map<String, Object> files = this.combineGetFilesAndNestedTemplates(heatFilesObjects,
2260                                         nestedTemplatesChecked);
2261                         HeatRequest heatRequest = new HeatRequest(vfModuleName, false, heatTemplate.getTimeoutMinutes(),
2262                                         heatTemplate.getTemplateBody(), newEnvironmentString, files, goldenInputs);
2263                         // vnf name is not sent to MSO on update requests - so we will set it to the vf module name for now
2264                         GenericValetResponse<ValetUpdateResponse> updateReq = this.vci.callValetUpdateRequest(msoRequest.getRequestId(),
2265                                         cloudSiteId, cloudOwnerId, tenantId, msoRequest.getServiceInstanceId(), (String)inputs.get("vnf_id"),
2266                                         vfModuleName, (String)inputs.get("vf_module_id"), vfModuleName, keystoneUrl,
2267                                         heatRequest);
2268                         ValetUpdateResponse vur = updateReq.getReturnObject();
2269                         if (vur != null && updateReq.getStatusCode() == 200) {
2270                                 ValetStatus status = vur.getStatus();
2271                                 if (status != null) {
2272                                         String statusCode = status.getStatus(); // "ok" or "failed"
2273                                         if ("ok".equalsIgnoreCase(statusCode)) {
2274                                                 Map<String, Object> newInputs = vur.getParameters();
2275                                                 if (newInputs != null) {
2276                                                         Map<String, Object> oldGold = goldenInputs;
2277                                                         logger.debug("parameters before being modified by valet:{}", oldGold.toString());
2278                                                         goldenInputs = new HashMap<String, Object>();
2279                                                         for (String key : newInputs.keySet()) {
2280                                                                 goldenInputs.put(key, newInputs.get(key));
2281                                                         }
2282                                                         valetModifiedParamsHolder.value = goldenInputs;
2283                                                         logger.debug("parameters after being modified by valet:{}", goldenInputs.toString());
2284                                                         valetSucceeded = true;
2285                                                 }
2286                                         } else {
2287                                                 valetErrorMessage = status.getMessage();
2288                                         }
2289                                 }
2290                         } else {
2291                                 logger.debug("Got a bad response back from valet");
2292                                 valetErrorMessage = "Got a bad response back from valet";
2293                                 valetSucceeded = false;
2294                         }
2295                 } catch (Exception e) {
2296         logger.error("An exception occurred trying to call valet - will continue processing for now...", e);
2297         valetErrorMessage = e.getMessage();
2298         valetSucceeded = false;
2299     }
2300                 if (failRequestOnValetFailure && !valetSucceeded) {
2301                         // The valet request failed - and property says to fail the request
2302                         // TODO Create a new exception class for valet?
2303                         throw new VnfException("A failure occurred with Valet: " + valetErrorMessage);
2304                 }
2305                 return valetSucceeded;
2306         }
2307
2308         /*
2309          * Valet delete request
2310          */
2311         private boolean valetDeleteRequest(String cloudSiteId, String cloudOwnerId, String tenantId, String vnfName,
2312                         MsoRequest msoRequest, boolean failRequestOnValetFailure) {
2313                 boolean valetDeleteRequestSucceeded = false;
2314                 String valetErrorMessage = "more detail not available";
2315                 try {
2316                         String vfModuleId = vnfName;
2317                         String vfModuleName = vnfName;
2318                         try {
2319                                 vfModuleName = vnfName.substring(0, vnfName.indexOf('/'));
2320                                 vfModuleId = vnfName.substring(vnfName.indexOf('/') + 1);
2321                         } catch (Exception e) {
2322                                 // do nothing - send what we got for vnfName for both to valet
2323                                 logger.error("An exception occurred trying to call MsoVnfAdapterImpl.valetDeleteRequest() method", e);
2324                         }
2325                         GenericValetResponse<ValetDeleteResponse> deleteReq = this.vci.callValetDeleteRequest(msoRequest.getRequestId(),
2326                                         cloudSiteId, cloudOwnerId, tenantId, vfModuleId, vfModuleName);
2327                         ValetDeleteResponse vdr = deleteReq.getReturnObject();
2328                         if (vdr != null && deleteReq.getStatusCode() == 200) {
2329                                 ValetStatus status = vdr.getStatus();
2330                                 if (status != null) {
2331                                         String statusCode = status.getStatus(); // "ok" or "failed"
2332             if ("ok".equalsIgnoreCase(statusCode)) {
2333                 logger.debug("delete request to valet returned success");
2334                 valetDeleteRequestSucceeded = true;
2335             } else {
2336                 logger.debug("delete request to valet returned failure");
2337                 valetDeleteRequestSucceeded = false;
2338                 valetErrorMessage = status.getMessage();
2339             }
2340         }
2341                         } else {
2342                                 logger.debug("Got a bad response back from valet - delete request failed");
2343                                 valetDeleteRequestSucceeded = false;
2344                                 valetErrorMessage = "Got a bad response back from valet - delete request failed";
2345                         }
2346                 } catch (Exception e) {
2347         logger.error("An exception occurred trying to call valet - valetDeleteRequest failed", e);
2348         valetDeleteRequestSucceeded = false;
2349         valetErrorMessage = e.getMessage();
2350     }
2351                 if (valetDeleteRequestSucceeded == false && failRequestOnValetFailure == true) {
2352                         logger.error("ValetDeleteRequestFailed - del req still will be sent to openstack", new VnfException
2353           ("ValetDeleteRequestFailedError"));
2354                 }
2355                 return valetDeleteRequestSucceeded;
2356         }
2357 }