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