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