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