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