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