Merge "Fix serialize DelegateExecutionImpl object bug"
[so.git] / adapters / mso-openstack-adapters / src / main / java / org / onap / so / adapters / vnf / MsoVnfPluginAdapterImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright (c) 2019 Samsung
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 /**
24  * This VNF Adapter implementation is based on the VDU Plugin model.  It assumes that each
25  * VF Module definition in the MSO catalog is expressed via a set of template and/or file
26  * artifacts that are appropriate for some specific sub-orchestrator that provides an
27  * implementation of the VduPlugin interface.  This adapter handles all of the common
28  * VF Module logic, including:
29  * - catalog lookups for artifact retrieval
30  * - parameter filtering and validation
31  * - base and volume module queries
32  * - rollback logic
33  * - logging and error handling
34  *
35  * Then based on the orchestration mode of the VNF, it will invoke different VDU plug-ins
36  * to perform the low level instantiations, deletions, and queries.  At this time, the
37  * set of available plug-ins is hard-coded, though in the future a dynamic selection
38  * is expected (e.g. via a service-provider interface).
39  */
40 package org.onap.so.adapters.vnf;
41
42
43 import java.util.ArrayList;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Optional;
48 import java.util.Set;
49
50 import javax.jws.WebService;
51 import javax.xml.ws.Holder;
52
53 import org.onap.so.adapters.vdu.CloudInfo;
54 import org.onap.so.adapters.vdu.VduException;
55 import org.onap.so.adapters.vdu.VduInstance;
56 import org.onap.so.adapters.vdu.VduModelInfo;
57 import org.onap.so.adapters.vdu.VduPlugin;
58 import org.onap.so.adapters.vdu.VduStateType;
59 import org.onap.so.adapters.vdu.VduStatus;
60 import org.onap.so.adapters.vdu.mapper.VfModuleCustomizationToVduMapper;
61 import org.onap.so.adapters.vnf.exceptions.VnfAlreadyExists;
62 import org.onap.so.adapters.vnf.exceptions.VnfException;
63 import org.onap.so.cloud.CloudConfig;
64 import org.onap.so.db.catalog.beans.CloudSite;
65 import org.onap.so.cloudify.utils.MsoCloudifyUtils;
66 import org.onap.so.db.catalog.beans.HeatEnvironment;
67 import org.onap.so.db.catalog.beans.HeatTemplate;
68 import org.onap.so.db.catalog.beans.HeatTemplateParam;
69 import org.onap.so.db.catalog.beans.VfModule;
70 import org.onap.so.db.catalog.beans.VfModuleCustomization;
71 import org.onap.so.db.catalog.beans.VnfResource;
72 import org.onap.so.db.catalog.data.repository.VFModuleCustomizationRepository;
73 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
74 import org.onap.so.entity.MsoRequest;
75 import org.onap.so.logger.ErrorCode;
76 import org.onap.so.logger.MessageEnum;
77
78 import org.onap.so.openstack.beans.VnfRollback;
79 import org.onap.so.openstack.beans.VnfStatus;
80 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
81 import org.onap.so.openstack.exceptions.MsoException;
82 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
83 import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry;
84 import org.onap.so.openstack.utils.MsoHeatUtils;
85 import org.onap.so.openstack.utils.MsoKeystoneUtils;
86 import org.onap.so.openstack.utils.MsoMulticloudUtils;
87 import org.slf4j.Logger;
88 import org.slf4j.LoggerFactory;
89 import org.springframework.beans.factory.annotation.Autowired;
90 import org.springframework.core.env.Environment;
91 import org.springframework.stereotype.Component;
92 import org.springframework.transaction.annotation.Transactional;
93
94 import com.fasterxml.jackson.core.JsonParseException;
95 import com.fasterxml.jackson.databind.JsonNode;
96 import com.fasterxml.jackson.databind.ObjectMapper;
97
98 @WebService(serviceName = "VnfAdapter", endpointInterface = "org.onap.so.adapters.vnf.MsoVnfAdapter", targetNamespace = "http://org.onap.so/vnf")
99 @Component
100 @Transactional
101 public class MsoVnfPluginAdapterImpl implements MsoVnfAdapter {
102
103     private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
104     private static Logger logger = LoggerFactory.getLogger(MsoVnfPluginAdapterImpl.class);
105
106     private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters";
107     private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
108
109     @Autowired
110     protected CloudConfig cloudConfig;
111
112     @Autowired
113     private VFModuleCustomizationRepository vfModuleCustomRepo;
114
115     @Autowired
116     private Environment environment;
117
118     @Autowired
119     protected MsoKeystoneUtils keystoneUtils;
120
121     @Autowired
122     protected MsoCloudifyUtils cloudifyUtils;
123
124     @Autowired
125     protected MsoHeatUtils heatUtils;
126
127     @Autowired
128     protected MsoMulticloudUtils multicloudUtils;
129
130         @Autowired
131         protected VfModuleCustomizationToVduMapper vduMapper;
132
133         /**
134      * Health Check web method. Does nothing but return to show the adapter is deployed.
135      */
136     @Override
137     public void healthCheck () {
138         logger.debug("Health check call in VNF Plugin Adapter");
139     }
140
141     /**
142      * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL.
143      * @see MsoVnfPluginAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory)
144      */
145     public MsoVnfPluginAdapterImpl() {
146
147     }
148
149     /**
150      * This is the "Create VNF" web service implementation.
151      * This function is now unsupported and will return an error.
152      *
153      */
154     @Override
155     public void createVnf (String cloudSiteId,
156                            String cloudOwner,
157                            String tenantId,
158                            String vnfType,
159                            String vnfVersion,
160                            String vnfName,
161                            String requestType,
162                            String volumeGroupHeatStackId,
163                            Map <String, Object> inputs,
164                            Boolean failIfExists,
165                            Boolean backout,
166                            Boolean enableBridge,
167                            MsoRequest msoRequest,
168                            Holder <String> vnfId,
169                            Holder <Map <String, String>> outputs,
170                            Holder <VnfRollback> rollback)
171         throws VnfException
172     {
173         // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
174         logger.debug("CreateVNF command attempted but not supported");
175         throw new VnfException("CreateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
176     }
177
178     /**
179      * This is the "Update VNF" web service implementation.
180      * This function is now unsupported and will return an error.
181      *
182      */
183     @Override
184     public void updateVnf (String cloudSiteId,
185                            String cloudOwner,
186                            String tenantId,
187                            String vnfType,
188                            String vnfVersion,
189                            String vnfName,
190                            String requestType,
191                            String volumeGroupHeatStackId,
192                            Map <String, Object> inputs,
193                            MsoRequest msoRequest,
194                            Holder <Map <String, String>> outputs,
195                            Holder <VnfRollback> rollback)
196                 throws VnfException
197         {
198         // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
199         logger.debug("UpdateVNF command attempted but not supported");
200         throw new VnfException ("UpdateVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
201     }
202
203     /**
204      * This is the "Query VNF" web service implementation.
205      *
206      * This really should be QueryVfModule, but nobody ever changed it.
207      *
208      * The method returns an indicator that the VNF exists, along with its status and outputs.
209      * The input "vnfName" will also be reflected back as its ID.
210      *
211      * @param cloudSiteId CLLI code of the cloud site in which to query
212      * @param tenantId Openstack tenant identifier
213      * @param vnfNameOrId VNF Name or ID to query
214      * @param msoRequest Request tracking information for logs
215      * @param vnfExists Flag reporting the result of the query
216      * @param vnfId Holder for output VNF ID
217      * @param outputs Holder for Map of outputs from the deployed VF Module (assigned IPs, etc)
218      */
219     @Override
220     public void queryVnf (String cloudSiteId,
221                           String cloudOwner,
222                           String tenantId,
223                           String vnfNameOrId,
224                           MsoRequest msoRequest,
225                           Holder <Boolean> vnfExists,
226                           Holder <String> vnfId,
227                           Holder <VnfStatus> status,
228                           Holder <Map <String, String>> outputs)
229         throws VnfException
230     {
231         logger.debug("Querying VNF " + vnfNameOrId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId);
232
233         // Will capture execution time for metrics
234         long startTime = System.currentTimeMillis ();
235         long subStartTime = System.currentTimeMillis ();
236
237         VduInstance vduInstance = null;
238         CloudInfo cloudInfo = new CloudInfo(cloudSiteId, cloudOwner, tenantId, null);
239
240         VduPlugin vduPlugin = getVduPlugin(cloudSiteId, cloudOwner);
241
242         try {
243                 vduInstance = vduPlugin.queryVdu (cloudInfo, vnfNameOrId);
244         }
245         catch (VduException e) {
246           // Failed to query the VDU due to a plugin exception.
247           // Convert to a generic VnfException
248           e.addContext("QueryVNF");
249           String error = "Query VNF (VDU): " + vnfNameOrId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + e;
250           logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfNameOrId, cloudOwner, cloudSiteId,
251               tenantId, "VDU", "QueryVNF", ErrorCode.DataError.getValue(), "Exception - queryVDU", e);
252           logger.debug(error);
253           throw new VnfException(e);
254       }
255
256         if (vduInstance != null  &&  vduInstance.getStatus().getState() != VduStateType.NOTFOUND) {
257           vnfExists.value = Boolean.TRUE;
258           status.value = vduStatusToVnfStatus(vduInstance);
259           vnfId.value = vduInstance.getVduInstanceId();
260           outputs.value = copyStringOutputs(vduInstance.getOutputs());
261
262           logger.debug("VNF {} found, ID = {}", vnfNameOrId, vnfId.value);
263         }
264         else {
265           vnfExists.value = Boolean.FALSE;
266           status.value = VnfStatus.NOTFOUND;
267           vnfId.value = null;
268           outputs.value = new HashMap<String, String>(); // Return as an empty map
269
270           logger.debug("VNF {} not found", vnfNameOrId);
271       }
272         return;
273     }
274
275
276     /**
277      * This is the "Delete VNF" web service implementation.
278      * This function is now unsupported and will return an error.
279      *
280      */
281     @Override
282     public void deleteVnf (String cloudSiteId,
283                            String cloudOwner,
284                            String tenantId,
285                            String vnfName,
286                            MsoRequest msoRequest) throws VnfException {
287
288         // This operation is no longer supported at the VNF level.  The adapter is only called to deploy modules.
289         logger.debug("DeleteVNF command attempted but not supported");
290         throw new VnfException ("DeleteVNF:  Unsupported command", MsoExceptionCategory.USERDATA);
291     }
292
293     /**
294      * This web service endpoint will rollback a previous Create VNF operation.
295      * A rollback object is returned to the client in a successful creation
296      * response. The client can pass that object as-is back to the rollbackVnf
297      * operation to undo the creation.
298      *
299      * TODO: This should be rollbackVfModule and/or rollbackVolumeGroup,
300      * but APIs were apparently never updated.
301      */
302     @Override
303     public void rollbackVnf (VnfRollback rollback) throws VnfException {
304         long startTime = System.currentTimeMillis ();
305         // rollback may be null (e.g. if stack already existed when Create was called)
306         if (rollback == null) {
307             logger.info("{} {} {}", MessageEnum.RA_ROLLBACK_NULL.toString(), "OpenStack", "rollbackVnf");
308             return;
309         }
310
311         // Don't rollback if nothing was done originally
312         if (!rollback.getVnfCreated()) {
313             return;
314         }
315
316         // Get the elements of the VnfRollback object for easier access
317         String cloudSiteId = rollback.getCloudSiteId ();
318         String cloudOwner = rollback.getCloudOwner();
319         String tenantId = rollback.getTenantId ();
320         CloudInfo cloudInfo = new CloudInfo (cloudSiteId, cloudOwner, tenantId, null);
321
322         String vfModuleId = rollback.getVfModuleStackId ();
323
324         logger.debug("Rolling Back VF Module " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId);
325
326         VduInstance vduInstance = null;
327
328         // Use the VduPlugin to delete the VF Module.
329         VduPlugin vduPlugin = getVduPlugin(cloudSiteId, cloudOwner);
330
331         long subStartTime = System.currentTimeMillis ();
332         try {
333                 // TODO: Get a reasonable timeout.  Use a global property, or store the creation timeout in rollback object and use that.
334             vduInstance = vduPlugin.deleteVdu(cloudInfo, vfModuleId, 5);
335
336             logger.debug("Rolled back VDU instantiation: {}", vduInstance.getVduInstanceId());
337         }
338         catch (VduException ve) {
339             // Failed to rollback the VF Module due to a plugin exception.
340             // Convert to a generic VnfException
341             ve.addContext ("RollbackVFModule");
342             String error = "Rollback VF Module: " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + ve;
343             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vfModuleId, cloudOwner, cloudSiteId,
344                 tenantId, "VDU", "DeleteVdu", ErrorCode.DataError.getValue(), "Exception - DeleteVdu", ve);
345             logger.debug(error);
346             throw new VnfException (ve);
347         }
348         return;
349     }
350
351
352     private VnfStatus vduStatusToVnfStatus (VduInstance vdu) {
353         // Determine the status based on last action & status
354         // DeploymentInfo object should be enhanced to report a better status internally.
355         VduStatus vduStatus = vdu.getStatus();
356         VduStateType status = vduStatus.getState();
357
358         if (status == null) {
359                 return VnfStatus.UNKNOWN;
360         }
361         else if (status == VduStateType.NOTFOUND) {
362                         return VnfStatus.NOTFOUND;
363         }
364         else if (status == VduStateType.INSTANTIATED) {
365                         return VnfStatus.ACTIVE;
366         }
367         else if (status == VduStateType.FAILED) {
368                 return VnfStatus.FAILED;
369         }
370
371         return VnfStatus.UNKNOWN;
372     }
373
374         /*
375          * Normalize an input value to an Object, based on the target parameter type.
376          * If the type is not recognized, it will just be returned unchanged (as a string).
377          */
378         private Object convertInputValue (Object inputValue, HeatTemplateParam templateParam)
379         {
380                 String type = templateParam.getParamType();
381                 logger.debug("Parameter: {} is of type ",  templateParam.getParamName(), type);
382
383                 if (type.equalsIgnoreCase("number")) {
384                         try {
385                                 return Integer.valueOf(inputValue.toString());
386                         }
387                         catch (Exception e) {
388                                 logger.debug("Unable to convert " + inputValue + " to an integer!" , e);
389                                 return null;
390                         }
391                 } else if (type.equalsIgnoreCase("json")) {
392                         try {
393                                 JsonNode jsonNode = JSON_MAPPER.readTree(JSON_MAPPER.writeValueAsString(inputValue));
394                                 return jsonNode;
395                         }
396                         catch (Exception e) {
397                                 logger.debug("Unable to convert " + inputValue + " to a JsonNode!", e);
398                                 return null;
399                         }
400                 } else if (type.equalsIgnoreCase("boolean")) {
401                         return new Boolean(inputValue.toString());
402                 }
403
404                 // Nothing else matched.  Return the original string
405                 return inputValue;
406         }
407
408     private Map <String, String> copyStringOutputs (Map <String, Object> stackOutputs) {
409         Map <String, String> stringOutputs = new HashMap <String, String> ();
410         for (String key : stackOutputs.keySet ()) {
411             if (stackOutputs.get (key) instanceof String) {
412                 stringOutputs.put (key, (String) stackOutputs.get (key));
413             } else if (stackOutputs.get(key) instanceof Integer)  {
414                 try {
415                         String str = "" + stackOutputs.get(key);
416                         stringOutputs.put(key, str);
417                 } catch (Exception e) {
418                         logger.debug("Unable to add {} to outputs", key, e);
419                 }
420             } else if (stackOutputs.get(key) instanceof JsonNode) {
421                 try {
422                         String str = this.convertNode((JsonNode) stackOutputs.get(key));
423                         stringOutputs.put(key, str);
424                 } catch (Exception e) {
425                         logger.debug("Unable to add {} to outputs - exception converting JsonNode", key, e);
426                 }
427             } else if (stackOutputs.get(key) instanceof java.util.LinkedHashMap) {
428                 try {
429                                         String str = JSON_MAPPER.writeValueAsString(stackOutputs.get(key));
430                         stringOutputs.put(key, str);
431                 } catch (Exception e) {
432                   logger.debug("Unable to add {} to outputs - exception converting LinkedHashMap", key, e);
433               }
434             } else {
435                 try {
436                         String str = stackOutputs.get(key).toString();
437                         stringOutputs.put(key, str);
438                 } catch (Exception e) {
439                   logger
440                       .debug("Unable to add " + key + " to outputs - unable to call .toString() " + e.getMessage(), e);
441               }
442             }
443         }
444         return stringOutputs;
445     }
446
447
448     private void sendMapToDebug(Map<String, Object> inputs, String optionalName) {
449         int i = 0;
450         StringBuilder sb = new StringBuilder(optionalName == null ? "\ninputs" : "\n" + optionalName);
451         if (inputs == null) {
452                 sb.append("\tNULL");
453         }
454         else if (inputs.size() < 1) {
455                 sb.append("\tEMPTY");
456         } else {
457                 for (String str : inputs.keySet()) {
458                         String outputString;
459                         try {
460                                 outputString = inputs.get(str).toString();
461                         } catch (Exception e) {
462                                 outputString = "Unable to call toString() on the value for " + str;
463                         }
464                         sb.append("\t\nitem " + i++ + ": '" + str + "'='" + outputString + "'");
465                 }
466         }
467       logger.debug(sb.toString());
468         return;
469     }
470
471     private void sendMapToDebug(Map<String, Object> inputs) {
472         int i = 0;
473         StringBuilder sb = new StringBuilder("inputs:");
474         if (inputs == null) {
475                 sb.append("\tNULL");
476         }
477         else if (inputs.size() < 1) {
478                 sb.append("\tEMPTY");
479         } else {
480                 for (String str : inputs.keySet()) {
481                         sb.append("\titem " + i++ + ": " + str + "=" + inputs.get(str));
482                 }
483         }
484       logger.debug(sb.toString());
485         return;
486     }
487
488     private String convertNode(final JsonNode node) {
489         try {
490             final Object obj = JSON_MAPPER.treeToValue(node, Object.class);
491             final String json = JSON_MAPPER.writeValueAsString(obj);
492             return json;
493         } catch (JsonParseException jpe) {
494             logger.debug("Error converting json to string " + jpe.getMessage());
495         } catch (Exception e) {
496             logger.debug("Error converting json to string " + e.getMessage());
497         }
498         return "[Error converting json to string]";
499     }
500
501     private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) {
502         if (objectMap == null) {
503             return null;
504         }
505         Map<String, String> stringMap = new HashMap<String, String>();
506         for (String key : objectMap.keySet()) {
507             if (!stringMap.containsKey(key)) {
508                 Object obj = objectMap.get(key);
509                 if (obj instanceof String) {
510                     stringMap.put(key, (String) objectMap.get(key));
511                 } else if (obj instanceof JsonNode ){
512                     // This is a bit of mess - but I think it's the least impacting
513                     // let's convert it BACK to a string - then it will get converted back later
514                     try {
515                         String str = this.convertNode((JsonNode) obj);
516                         stringMap.put(key, str);
517                     } catch (Exception e) {
518                                                 logger.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode "+ key, e);
519                         //okay in this instance - only string values (fqdn) are expected to be needed
520                     }
521                 } else if (obj instanceof java.util.LinkedHashMap) {
522                     logger.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode");
523                     try {
524                         String str = JSON_MAPPER.writeValueAsString(obj);
525                         stringMap.put(key, str);
526                     } catch (Exception e) {
527                         logger.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap "+ key, e);
528                                         }
529                                 }  else if (obj instanceof Integer) {
530                                         try {
531                                                 String str = "" + obj;
532                                                 stringMap.put(key, str);
533                                         } catch (Exception e) {
534               logger.debug("DANGER WILL ROBINSON: unable to convert value for Integer "+ key, e);
535                     }
536                 } else {
537                     try {
538                                                 String str = obj.toString();
539                         stringMap.put(key, str);
540                     } catch (Exception e) {
541                         logger.debug("DANGER WILL ROBINSON: unable to convert value "+ key + " (" + e.getMessage() + ")", e);
542                     }
543                 }
544             }
545         }
546
547         return stringMap;
548     }
549
550     /**
551      * This is the "Create VF Module" web service implementation.
552      * It will instantiate a new VF Module of the requested type in the specified cloud
553      * and tenant. The tenant must exist before this service is called.
554      *
555      * If a VF Module with the same name already exists, this can be considered a
556      * success or failure, depending on the value of the 'failIfExists' parameter.
557      *
558      * All VF Modules are defined in the MSO catalog. The caller must request one of
559      * the pre-defined module types or an error will be returned. Within the catalog,
560      * each VF Module references (among other things) a collection of artifacts that
561      * are used to deploy the required cloud resources (VMs, networks, etc.).
562      *
563      * Depending on the module templates, a variable set of input parameters will
564      * be defined, some of which are required. The caller is responsible to
565      * pass the necessary input data for the module or an error will be thrown.
566      *
567      * The method returns the vfModuleId, a Map of output attributes, and a VnfRollback
568      * object. This last object can be passed as-is to the rollbackVnf operation to
569      * undo everything that was created for the Module. This is useful if a VF module
570      * is successfully created but the orchestration fails on a subsequent step.
571      *
572      * @param cloudSiteId CLLI code of the cloud site in which to create the VNF
573      * @param cloudOwner cloud owner of the cloud site in which to create the VNF
574      * @param tenantId Openstack tenant identifier
575      * @param vfModuleType VF Module type key, should match a VNF definition in catalog DB.
576      *        Deprecated - should use modelCustomizationUuid
577      * @param vnfVersion VNF version key, should match a VNF definition in catalog DB
578      *        Deprecated - VF Module versions also captured by modelCustomizationUuid
579      * @param vnfId - VNF ID
580      * @param vfModuleName Name to be assigned to the new VF Module
581      * @param vfModuleId Id fo the new VF Module
582      * @param requestType Indicates if this is a Volume Group or Module request
583      * @param volumeGroupId Identifier (i.e. deployment ID) for a Volume Group
584      *        to attach to a VF Module
585      * @param baseVfModuleId Identifier (i.e. deployment ID) of the Base Module if
586      *        this is an Add-on module
587      * @param modelCustomizationUuid Unique ID for the VF Module's model.  Replaces
588      *        the use of vfModuleType.
589      * @param inputs Map of key=value inputs for VNF stack creation
590      * @param failIfExists Flag whether already existing VNF should be considered
591      * @param backout Flag whether to suppress automatic backout (for testing)
592      * @param msoRequest Request tracking information for logs
593      * @param vnfId Holder for output VF Module instance ID in the cloud
594      * @param outputs Holder for Map of VNF outputs from Deployment (assigned IPs, etc)
595      * @param rollback Holder for returning VnfRollback object
596      */
597     @Override
598     public void createVfModule(String cloudSiteId,
599             String cloudOwner,
600             String tenantId,
601             String vfModuleType,
602             String vnfVersion,
603             String genericVnfId,
604             String vfModuleName,
605             String vfModuleId,
606             String requestType,
607             String volumeGroupId,
608             String baseVfModuleId,
609             String modelCustomizationUuid,
610             Map <String, Object> inputs,
611             Boolean failIfExists,
612             Boolean backout,
613             Boolean enableBridge,
614             MsoRequest msoRequest,
615             Holder <String> vnfId,
616             Holder <Map <String, String>> outputs,
617             Holder <VnfRollback> rollback)
618         throws VnfException
619     {
620         // Will capture execution time for metrics
621         long startTime = System.currentTimeMillis ();
622
623         // Require a model customization ID.  Every VF Module definition must have one.
624         if (modelCustomizationUuid == null  ||  modelCustomizationUuid.isEmpty()) {
625             logger.debug("Missing required input: modelCustomizationUuid");
626             String error = "Create vfModule error: Missing required input: modelCustomizationUuid";
627             logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
628                 "VF Module ModelCustomizationUuid", "VDU", ErrorCode.DataError,
629                 "Create VF Module: " + "Missing required input: modelCustomizationUuid");
630             logger.debug(error);
631             throw new VnfException(error, MsoExceptionCategory.USERDATA);
632         }
633
634         // Clean up some inputs to make comparisons easier
635         if (requestType == null)
636                 requestType = "";
637
638         if ("".equals(volumeGroupId) || "null".equals(volumeGroupId))
639                 volumeGroupId = null;
640
641         if ("".equals(baseVfModuleId) || "null".equals(baseVfModuleId))
642                 baseVfModuleId = null;
643
644         if (inputs == null) {
645                 // Create an empty set of inputs
646                 inputs = new HashMap<>();
647           logger.debug("inputs == null - setting to empty");
648         } else {
649                 this.sendMapToDebug(inputs);
650         }
651
652         // Check if this is for a "Volume" module
653         boolean isVolumeRequest = false;
654         if (requestType.startsWith("VOLUME")) {
655                 isVolumeRequest = true;
656         }
657
658         logger.debug("requestType = " + requestType + ", volumeGroupStackId = " + volumeGroupId + ", baseStackId = " +
659             baseVfModuleId);
660
661         // Build a default rollback object (no actions performed)
662         VnfRollback vfRollback = new VnfRollback();
663         vfRollback.setCloudSiteId(cloudSiteId);
664         vfRollback.setCloudOwner(cloudOwner);
665         vfRollback.setTenantId(tenantId);
666         vfRollback.setMsoRequest(msoRequest);
667         vfRollback.setRequestType(requestType);
668         vfRollback.setIsBase(false);    // Until we know better
669         vfRollback.setVolumeGroupHeatStackId(volumeGroupId);
670         vfRollback.setBaseGroupHeatStackId(baseVfModuleId);
671         vfRollback.setModelCustomizationUuid(modelCustomizationUuid);
672         vfRollback.setMode("CFY");
673
674                 rollback.value = vfRollback; // Default rollback - no updates performed
675
676         // Get the VNF/VF Module definition from the Catalog DB first.
677         // There are three relevant records:  VfModule, VfModuleCustomization, VnfResource
678
679         VfModule vfModule = null;
680         VnfResource vnfResource = null;
681         VfModuleCustomization vfModuleCust = null;
682
683         try {
684             vfModuleCust = vfModuleCustomRepo.findByModelCustomizationUUID(modelCustomizationUuid);
685
686             if (vfModuleCust == null) {
687                 String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid="
688                     + modelCustomizationUuid;
689                 logger.debug(error);
690                 logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(),
691                     "VF Module ModelCustomizationUuid", modelCustomizationUuid, "CatalogDb",
692                     ErrorCode.DataError, error);
693                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
694             } else {
695                 logger.debug("Found vfModuleCust entry {}", vfModuleCust.toString());
696             }
697
698             // Get the vfModule and vnfResource records
699                 vfModule = vfModuleCust.getVfModule();
700                 vnfResource = vfModuleCust.getVfModule().getVnfResources();
701         }
702         catch (Exception e) {
703
704                 logger.debug("unhandled exception in create VF - [Query]" + e.getMessage());
705                 throw new VnfException("Exception during create VF " + e.getMessage());
706         }
707
708         //  Perform a version check against cloudSite
709         // Obtain the cloud site information where we will create the VF Module
710         Optional<CloudSite> cloudSiteOp = cloudConfig.getCloudSite (cloudSiteId);
711         if (!cloudSiteOp.isPresent()) {
712             throw new VnfException (new MsoCloudSiteNotFound (cloudSiteId));
713         }
714         CloudSite cloudSite = cloudSiteOp.get();
715                 MavenLikeVersioning aicV = new MavenLikeVersioning();
716                 aicV.setVersion(cloudSite.getCloudVersion());
717                 Boolean usingMulticloud = getUsingMulticloud(cloudSite);
718
719                 String vnfMin = vnfResource.getAicVersionMin();
720                 String vnfMax = vnfResource.getAicVersionMax();
721
722         if ((vnfMin != null && !(aicV.isMoreRecentThan(vnfMin) || aicV.isTheSameVersion(vnfMin))) || (vnfMax != null
723             && aicV.isMoreRecentThan(vnfMax))) {
724             // ERROR
725             String error =
726                 "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid=" + vnfResource.getModelUUID()
727                     + " VersionMin=" + vnfMin + " VersionMax:" + vnfMax + " NOT supported on Cloud: " + cloudSiteId
728                     + " with AIC_Version:" + cloudSite.getCloudVersion();
729             logger.error("{} {} {} {} {}", MessageEnum.RA_CONFIG_EXC.toString(), error, "OpenStack",
730                 ErrorCode.BusinessProcesssError.getValue(), "Exception - setVersion");
731             logger.debug(error);
732             throw new VnfException(error, MsoExceptionCategory.USERDATA);
733         }
734         // End Version check
735
736
737         VduInstance vduInstance = null;
738         CloudInfo cloudInfo = new CloudInfo (cloudSiteId, cloudOwner, tenantId, null);
739
740         // Use the VduPlugin.
741         VduPlugin vduPlugin = getVduPlugin(cloudSiteId, cloudOwner);
742
743         // First, look up to see if the VF already exists, unless using multicloud adapter
744
745         long subStartTime1 = System.currentTimeMillis ();
746         if (!usingMulticloud) {
747             try {
748                 vduInstance = vduPlugin.queryVdu (cloudInfo, vfModuleName);
749             }
750             catch (VduException me) {
751                 // Failed to query the VDU due to a plugin exception.
752                 String error = "Create VF Module: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
753                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName,
754                     cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(),
755                     "Exception - queryVdu", me);
756                 logger.debug(error);
757                 // Convert to a generic VnfException
758                 me.addContext ("CreateVFModule");
759                 throw new VnfException (me);
760             }
761         }
762
763         // More precise handling/messaging if the Module already exists
764         if (vduInstance != null && !(vduInstance.getStatus().getState() == VduStateType.NOTFOUND)) {
765                 VduStateType status = vduInstance.getStatus().getState();
766             logger.debug("Found Existing VDU, status=" + status);
767
768             if (status == VduStateType.INSTANTIATED) {
769                 if (failIfExists != null && failIfExists) {
770                     // fail - it exists
771                     String error =
772                         "Create VF: Deployment " + vfModuleName + " already exists in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId;
773                     logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
774                         cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(),
775                         "VF Module " + vfModuleName + " already exists");
776                     logger.debug(error);
777                     throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, vduInstance.getVduInstanceId());
778                 } else {
779                     // Found existing deployment and client has not requested "failIfExists".
780                     // Populate the outputs from the existing deployment.
781
782                     vnfId.value = vduInstance.getVduInstanceId();
783                     outputs.value = copyStringOutputs(vduInstance.getOutputs());
784                     return;
785                 }
786             }
787             // Check through various detailed error cases
788             else if (status == VduStateType.INSTANTIATING || status == VduStateType.DELETING
789                 || status == VduStateType.UPDATING) {
790                 // fail - it's in progress - return meaningful error
791                 String error =
792                     "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString()
793                         + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; please wait for it to complete, or fix manually.";
794                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
795                     cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(),
796                     "VF Module " + vfModuleName + " already exists");
797                 logger.debug(error);
798                 throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, vduInstance.getVduInstanceId());
799             } else if (status == VduStateType.FAILED) {
800                 // fail - it exists and is in a FAILED state
801                 String error =
802                     "Create VF: Deployment " + vfModuleName + " already exists and is in FAILED state in " + cloudOwner + "/" + cloudSiteId
803                         + "/" + tenantId + "; requires manual intervention.";
804                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
805                     cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(),
806                     "VF Module " + vfModuleName + " already exists and is in FAILED state");
807                 logger.debug(error);
808                 throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, vduInstance.getVduInstanceId());
809             } else if (status == VduStateType.UNKNOWN) {
810                 // fail - it exists and is in a UNKNOWN state
811                 String error =
812                     "Create VF: Deployment " + vfModuleName + " already exists and has status " + status.toString()
813                         + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
814                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
815                     cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(),
816                     "VF Module " + vfModuleName + " already exists and is in " + status.toString() + " state");
817                 logger.debug(error);
818                 throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, vduInstance.getVduInstanceId());
819             } else {
820                 // Unexpected, since all known status values have been tested for
821                 String error =
822                     "Create VF: Deployment " + vfModuleName + " already exists with unexpected status " + status
823                         .toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention.";
824                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName,
825                     cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(),
826                     "VF Module " + vfModuleName + " already exists and is in an unknown state");
827                 logger.debug(error);
828                 throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, vduInstance.getVduInstanceId());
829             }
830         }
831
832
833         // Collect outputs from Base Modules and Volume Modules
834         Map<String, Object> baseModuleOutputs = null;
835         Map<String, Object> volumeGroupOutputs = null;
836
837         // If a Volume Group was provided, query its outputs for inclusion in Module input parameters
838         if (!usingMulticloud && volumeGroupId != null) {
839             long subStartTime2 = System.currentTimeMillis ();
840             VduInstance volumeVdu = null;
841             try {
842                 volumeVdu = vduPlugin.queryVdu (cloudInfo, volumeGroupId);
843             }
844             catch (VduException me) {
845                 // Failed to query the Volume Group VDU due to a plugin exception.
846                 String error = "Create VF Module: Query Volume Group " + volumeGroupId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
847                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId,
848                     cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu(volume)", ErrorCode.DataError.getValue(),
849                     "Exception - queryVdu(volume)", me);
850                 logger.debug(error);
851                 // Convert to a generic VnfException
852                 me.addContext ("CreateVFModule(QueryVolume)");
853                 throw new VnfException (me);
854             }
855
856                 if (volumeVdu == null || volumeVdu.getStatus().getState() == VduStateType.NOTFOUND) {
857                     String error = "Create VFModule: Attached Volume Group DOES NOT EXIST " + volumeGroupId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR"  ;
858               logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId,
859                   cloudOwner, cloudSiteId, tenantId, error, "VDU", "queryVdu(volume)",
860                   ErrorCode.BusinessProcesssError.getValue(),
861                   "Create VFModule: Attached Volume Group " + "DOES NOT EXIST");
862               logger.debug(error);
863                     throw new VnfException (error, MsoExceptionCategory.USERDATA);
864                 } else {
865                         logger.debug("Found nested volume group");
866                         volumeGroupOutputs = volumeVdu.getOutputs();
867                         this.sendMapToDebug(volumeGroupOutputs, "volumeGroupOutputs");
868                 }
869         }
870
871         // If this is an Add-On Module, query the Base Module outputs
872         // Note: This will be performed whether or not the current request is for an
873         //       Add-On Volume Group or Add-On VF Module
874
875         if (vfModule.getIsBase()) {
876             logger.debug("This is a BASE Module request");
877             vfRollback.setIsBase(true);
878         } else {
879             logger.debug("This is an Add-On Module request");
880
881             // Add-On Modules should always have a Base, but just treat as a warning if not provided.
882             // Add-on Volume requests may or may not specify a base.
883             if (!isVolumeRequest && baseVfModuleId == null) {
884                 logger.debug("WARNING:  Add-on Module request - no Base Module ID provided");
885             }
886
887             // Need to verify if multicloud needs to have the vaseVfModuleId passed to it.  Ignoring this for now.
888             if (!usingMulticloud && baseVfModuleId != null) {
889                     long subStartTime2 = System.currentTimeMillis ();
890                     VduInstance baseVdu = null;
891                     try {
892                         baseVdu = vduPlugin.queryVdu (cloudInfo, baseVfModuleId);
893                     }
894                     catch (MsoException me) {
895                         // Failed to query the Base VF Module due to a Vdu Plugin exception.
896                         String error = "Create VF Module: Query Base " + baseVfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me ;
897                   logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), baseVfModuleId,
898                       cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu(Base)", ErrorCode.DataError.getValue(),
899                       "Exception - queryVdu(Base)", me);
900                   logger.debug(error);
901                   // Convert to a generic VnfException
902                         me.addContext ("CreateVFModule(QueryBase)");
903                         throw new VnfException (me);
904                     }
905
906                 if (baseVdu == null || baseVdu.getStatus().getState() == VduStateType.NOTFOUND) {
907                     String error =
908                         "Create VFModule: Base Module DOES NOT EXIST " + baseVfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/"
909                             + tenantId + " USER ERROR";
910                     logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), baseVfModuleId,
911                         cloudOwner, cloudSiteId, tenantId, error, "VDU", "queryVdu(Base)",
912                         ErrorCode.BusinessProcesssError.getValue(),
913                         "Create VFModule: Base Module DOES NOT EXIST");
914                     logger.debug(error);
915                     throw new VnfException(error, MsoExceptionCategory.USERDATA);
916                 } else {
917                     logger.debug("Found base module");
918                     baseModuleOutputs = baseVdu.getOutputs();
919                     this.sendMapToDebug(baseModuleOutputs, "baseModuleOutputs");
920                 }
921             }
922         }
923
924
925         // NOTE:  For this section, heatTemplate is used for all template artifacts.
926         // In final implementation (post-POC), the template object would either be generic or there would
927         // be a separate DB Table/Object for different sub-orchestrators.
928
929         // NOTE: The template is fixed for the VF Module.  The environment is part of the customization.
930
931         HeatTemplate heatTemplate = null;
932         HeatEnvironment heatEnvironment = null;
933         if (isVolumeRequest) {
934                         heatTemplate = vfModule.getVolumeHeatTemplate();
935                         heatEnvironment = vfModuleCust.getVolumeHeatEnv();
936                 } else {
937                         heatTemplate = vfModule.getModuleHeatTemplate();
938                         heatEnvironment = vfModuleCust.getHeatEnvironment();
939                 }
940
941                 if (heatTemplate == null) {
942         String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + ", reqType="
943             + requestType;
944         logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID", vfModuleType,
945             "VNF", ErrorCode.DataError.getValue(), error);
946         logger.debug(error);
947         throw new VnfException(error, MsoExceptionCategory.INTERNAL);
948     } else {
949         logger.debug("Got HEAT Template from DB: " + heatTemplate.getHeatTemplate());
950     }
951
952         if (heatEnvironment == null) {
953             String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType;
954             logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID",
955                 "OpenStack", ErrorCode.DataError.getValue(), error);
956             throw new VnfException(error, MsoExceptionCategory.INTERNAL);
957         } else {
958             logger.debug("Got Heat Environment from DB: " + heatEnvironment.getEnvironment());
959         }
960
961
962         // Create the combined set of parameters from the incoming request, base-module outputs,
963         // volume-module outputs.  Also, convert all variables to their native object types.
964
965         HashMap<String, Object> goldenInputs = new HashMap<String,Object>();
966         List<String> extraInputs = new ArrayList<String>();
967
968                 Boolean skipInputChecks = false;
969
970                 if (skipInputChecks) {
971                         goldenInputs = new HashMap<String,Object>();
972                         for (String key : inputs.keySet()) {
973                                 goldenInputs.put(key, inputs.get(key));
974                         }
975                 }
976                 else {
977                         // Build maps for the parameters (including aliases) to simplify checks
978                         HashMap<String, HeatTemplateParam> params = new HashMap<String, HeatTemplateParam>();
979
980                         Set<HeatTemplateParam> paramSet = heatTemplate.getParameters();
981                         logger.debug("paramSet has " + paramSet.size() + " entries");
982
983                         for (HeatTemplateParam htp : paramSet) {
984                                 params.put(htp.getParamName(), htp);
985
986                                 // Include aliases.
987                                 String alias = htp.getParamAlias();
988                                 if (alias != null && !alias.equals("") && !params.containsKey(alias)) {
989                                         params.put(alias, htp);
990                                 }
991                         }
992
993                         // First, convert all inputs to their "template" type
994                         for (String key : inputs.keySet()) {
995                                 if (params.containsKey(key)) {
996                                         Object value = convertInputValue(inputs.get(key), params.get(key));
997                                         if (value != null) {
998                                                 goldenInputs.put(key, value);
999                                         }
1000                                         else {
1001                                                 logger.debug("Failed to convert input " + key + "='" + inputs.get(key) + "' to " + params.get(key)
1002                 .getParamType());
1003                                         }
1004                                 } else {
1005                                         extraInputs.add(key);
1006                                 }
1007                         }
1008
1009                         if (!extraInputs.isEmpty()) {
1010                                 // Add multicloud inputs
1011                             boolean multicloudInputs = false;
1012                                 for (String key : MsoMulticloudUtils.MULTICLOUD_INPUTS) {
1013                                         if (extraInputs.contains(key)) {
1014                                                 goldenInputs.put(key, inputs.get(key));
1015                                                 extraInputs.remove(key);
1016                                                 multicloudInputs = true;
1017                                                 if (extraInputs.isEmpty()) {
1018                                                         break;
1019                                                 }
1020                                         }
1021                                 }
1022                                 logger.debug("Ignoring extra inputs: " + extraInputs);
1023                         }
1024
1025                         // Next add in Volume Group Outputs if there are any.  Copy directly without conversions.
1026                         if (volumeGroupOutputs != null  &&  !volumeGroupOutputs.isEmpty()) {
1027                                 for (String key : volumeGroupOutputs.keySet()) {
1028                                         if (params.containsKey(key)  &&  !goldenInputs.containsKey(key)) {
1029                                                 goldenInputs.put(key, volumeGroupOutputs.get(key));
1030                                         }
1031                                 }
1032                         }
1033
1034                         // Next add in Base Module Outputs if there are any.  Copy directly without conversions.
1035                         if (baseModuleOutputs != null  &&  !baseModuleOutputs.isEmpty()) {
1036                                 for (String key : baseModuleOutputs.keySet()) {
1037                                         if (params.containsKey(key)  &&  !goldenInputs.containsKey(key)) {
1038                                                 goldenInputs.put(key, baseModuleOutputs.get(key));
1039                                         }
1040                                 }
1041                         }
1042
1043                         // TODO:  The model should support a mechanism to pre-assign default parameter values
1044                         // per "customization" (i.e. usage) of a given module.  In HEAT, this is specified by
1045                         // an Environment file.  There is not a general mechanism in the model to handle this.
1046                         // For the general case, any such parameter/values can be added dynamically to the
1047                         // inputs (only if not already specified).
1048
1049             // Check that required parameters have been supplied from any of the sources
1050             String missingParams = null;
1051             boolean checkRequiredParameters = true;
1052             try {
1053                 String propertyString = this.environment.getProperty(MsoVnfPluginAdapterImpl.CHECK_REQD_PARAMS);
1054                 if ("false".equalsIgnoreCase (propertyString) || "n".equalsIgnoreCase (propertyString)) {
1055                     checkRequiredParameters = false;
1056                     logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking..."
1057                                   + MsoVnfPluginAdapterImpl.CHECK_REQD_PARAMS);
1058                 }
1059             } catch (Exception e) {
1060                 // No problem - default is true
1061                 logger.debug ("An exception occured trying to get property " + MsoVnfPluginAdapterImpl.CHECK_REQD_PARAMS,
1062                     e);
1063             }
1064
1065             // Do the actual parameter checking.
1066             // Include looking at the ENV file as a valid definition of a parameter value.
1067             // TODO:  This handling of ENV applies only to Heat.  A general mechanism to
1068             // support pre-set parameter/values does not yet exist in the model.
1069             //
1070                         StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment());
1071                         MsoHeatEnvironmentEntry mhee = new MsoHeatEnvironmentEntry (sb);
1072             for (HeatTemplateParam parm : heatTemplate.getParameters ()) {
1073                 if (parm.isRequired () && (!goldenInputs.containsKey (parm.getParamName ()))) {
1074                     if (mhee != null && mhee.containsParameter(parm.getParamName())) {
1075                         logger.debug ("Required parameter " + parm.getParamName ()
1076                                       + " appears to be in environment - do not count as missing");
1077                     } else {
1078                             logger.debug("adding to missing parameters list: " + parm.getParamName ());
1079                             if (missingParams == null) {
1080                                 missingParams = parm.getParamName ();
1081                             } else {
1082                                 missingParams += "," + parm.getParamName ();
1083                             }
1084                     }
1085                 }
1086             }
1087
1088         if (missingParams != null) {
1089             if (checkRequiredParameters) {
1090                 // Problem - missing one or more required parameters
1091                 String error = "Create VFModule: Missing Required inputs: " + missingParams;
1092                 logger.error("{} {} {} {} {}", MessageEnum.RA_MISSING_PARAM.toString(), missingParams, "VDU",
1093                     ErrorCode.DataError.getValue(), "Create VFModule: Missing Required inputs");
1094                 logger.debug(error);
1095                 throw new VnfException(error, MsoExceptionCategory.USERDATA);
1096             } else {
1097                 logger.debug(
1098                     "found missing parameters [" + missingParams + "] - but checkRequiredParameters is false - "
1099                         + "will not block");
1100             }
1101         } else {
1102             logger.debug("No missing parameters found - ok to proceed");
1103         }
1104
1105                 } // NOTE: END PARAMETER CHECKING
1106
1107
1108                 // Here we go...  ready to deploy the VF Module.
1109         long instantiateVduStartTime = System.currentTimeMillis ();
1110         if (backout == null) backout = true;
1111
1112         try {
1113             // Construct the VDU Model structure to pass to the targeted VduPlugin
1114             VduModelInfo vduModel = null;
1115             if (!isVolumeRequest) {
1116                 vduModel = vduMapper.mapVfModuleCustomizationToVdu(vfModuleCust);
1117             } else {
1118                 vduModel = vduMapper.mapVfModuleCustVolumeToVdu(vfModuleCust);
1119             }
1120
1121             // Invoke the VduPlugin to instantiate the VF Module
1122             vduInstance = vduPlugin.instantiateVdu(cloudInfo, vfModuleName, goldenInputs, vduModel, backout);
1123
1124         } catch (VduException me) {
1125             // Failed to instantiate the VDU.
1126             me.addContext("CreateVFModule");
1127             String error = "Create VF Module " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1128             logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner, cloudSiteId,
1129                 tenantId, "VDU", ErrorCode.DataError.getValue(), "MsoException - instantiateVdu", me);
1130             logger.debug(error);
1131             // Convert to a generic VnfException
1132             throw new VnfException(me);
1133         } catch (NullPointerException npe) {
1134             String error = "Create VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + npe;
1135             logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner, cloudSiteId,
1136                 tenantId, "VDU", ErrorCode.DataError.getValue(), "NullPointerException - instantiateVdu",
1137                 npe);
1138             logger.debug(error);
1139             logger.debug("NULL POINTER EXCEPTION at vduPlugin.instantiateVdu", npe);
1140             throw new VnfException("NullPointerException during instantiateVdu");
1141         } catch (Exception e) {
1142             String error = "Create VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + e;
1143             logger.debug("Unhandled exception at vduPlugin.instantiateVdu", e);
1144             logger.debug(error);
1145             throw new VnfException("Exception during instantiateVdu: " + e.getMessage());
1146         }
1147
1148
1149         // Reach this point if create is successful.
1150         // Populate remaining rollback info and response parameters.
1151         vfRollback.setVnfCreated (true);
1152         vfRollback.setVnfId (vduInstance.getVduInstanceId());
1153         vnfId.value = vduInstance.getVduInstanceId();
1154         outputs.value = copyStringOutputs (vduInstance.getOutputs ());
1155
1156         rollback.value = vfRollback;
1157
1158         logger.debug("VF Module " + vfModuleName + " successfully created");
1159         return;
1160     }
1161
1162
1163     public void deleteVfModule (String cloudSiteId,
1164                            String cloudOwner,
1165                            String tenantId,
1166                            String vfModuleId,
1167                            MsoRequest msoRequest,
1168                            Holder <Map <String, String>> outputs) throws VnfException
1169     {
1170
1171         logger.debug("Deleting VF Module " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId);
1172         // Will capture execution time for metrics
1173         long startTime = System.currentTimeMillis ();
1174
1175         // Capture the output parameters on a delete, so need to query first
1176         VduInstance vduInstance = null;
1177         CloudInfo cloudInfo = new CloudInfo(cloudSiteId, cloudOwner, tenantId, null);
1178
1179         // Use the VduPlugin.
1180         VduPlugin vduPlugin = getVduPlugin(cloudSiteId, cloudOwner);
1181
1182         try {
1183                 vduInstance = vduPlugin.queryVdu (cloudInfo, vfModuleId);
1184         }
1185         catch (VduException e) {
1186           // Failed to query the VDU due to a plugin exception.
1187           // Convert to a generic VnfException
1188           e.addContext("QueryVFModule");
1189           String error = "Query VfModule (VDU): " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + e;
1190           logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleId, cloudOwner, cloudSiteId,
1191               tenantId, "VDU", "QueryVFModule", ErrorCode.DataError.getValue(), "Exception - queryVDU", e);
1192           logger.debug(error);
1193           throw new VnfException(e);
1194       }
1195
1196         // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected Object types
1197         outputs.value = convertMapStringObjectToStringString(vduInstance.getOutputs());
1198
1199         // Use the VduPlugin to delete the VDU.
1200         // The possible outcomes of deleteVdu are
1201         // - a vnfInstance object with status of DELETED (success)
1202         // - a vnfInstance object with status of NOTFOUND (VDU did not exist, treat as success)
1203         // - a vnfInstance object with status of FAILED (error)
1204         // Also, VduException could be thrown.
1205         long subStartTime = System.currentTimeMillis ();
1206         try {
1207                 // TODO:  Get an appropriate timeout value - require access to the model
1208             vduPlugin.deleteVdu(cloudInfo, vfModuleId, 5);
1209         } catch (VduException me) {
1210             me.addContext ("DeleteVfModule");
1211             // Convert to a generic VnfException
1212             String error = "Delete VF: " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me;
1213             logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vfModuleId, cloudOwner, cloudSiteId,
1214                 tenantId, "VDU", "DeleteVdu", ErrorCode.DataError.getValue(),
1215                 "Exception - DeleteVdu: " + me.getMessage());
1216             logger.debug(error);
1217             throw new VnfException (me);
1218         }
1219
1220         // On success, nothing is returned.
1221         return;
1222     }
1223
1224     // Update VF Module not yet implemented for generic VDU plug-in model.
1225     @Override
1226     public void updateVfModule (String cloudSiteId,
1227                            String cloudOwner,
1228                            String tenantId,
1229                            String vnfType,
1230                            String vnfVersion,
1231                            String vnfName,
1232                            String requestType,
1233                            String volumeGroupHeatStackId,
1234                            String baseVfHeatStackId,
1235                            String vfModuleStackId,
1236                            String modelCustomizationUuid,
1237                            Map <String, Object> inputs,
1238                            MsoRequest msoRequest,
1239                            Holder <Map <String, String>> outputs,
1240                            Holder <VnfRollback> rollback) throws VnfException
1241         {
1242                 // This operation is not currently supported for VduPlugin-orchestrated VF Modules.
1243             logger.debug("Update VF Module command attempted but not supported");
1244             throw new VnfException ("UpdateVfModule:  Unsupported command", MsoExceptionCategory.USERDATA);
1245         }
1246
1247     /*
1248      * Dynamic selection of a VduPlugin version.  For initial tests, base on the "orchestrator"
1249      * defined for the target cloud.  Should really be looking at the VNF Model (ochestration_mode)
1250      * but we don't currently have access to that in Query and Delete cases.
1251      */
1252     private VduPlugin getVduPlugin (String cloudSiteId, String cloudOwner) {
1253         Optional<CloudSite> cloudSiteOp = cloudConfig.getCloudSite(cloudSiteId);
1254         if (cloudSiteOp.isPresent()) {
1255                 CloudSite cloudSite = cloudSiteOp.get();
1256                 String orchestrator = cloudSite.getOrchestrator();
1257
1258                 if (orchestrator.equalsIgnoreCase("CLOUDIFY")) {
1259                         return cloudifyUtils;
1260                 }
1261                 else if (orchestrator.equalsIgnoreCase("HEAT")) {
1262                         return heatUtils;
1263                 }
1264             if (orchestrator.equalsIgnoreCase("MULTICLOUD")) {
1265                 logger.debug ("Got MulticloudUtils for vduPlugin");
1266                 return multicloudUtils; }
1267         }
1268         // Default - return HEAT plugin, though will fail later
1269         return heatUtils;
1270     }
1271
1272     private Boolean getUsingMulticloud (CloudSite cloudSite) {
1273         if (cloudSite.getOrchestrator().equalsIgnoreCase("MULTICLOUD")) {
1274             return true;
1275         } else {
1276             return false;
1277         }
1278     }
1279 }