diff options
Diffstat (limited to 'adapters')
18 files changed, 1543 insertions, 90 deletions
diff --git a/adapters/mso-catalog-db-adapter/src/main/resources/db/migration/R__MacroData.sql b/adapters/mso-catalog-db-adapter/src/main/resources/db/migration/R__MacroData.sql index 622a9c93c5..b355bb6782 100644 --- a/adapters/mso-catalog-db-adapter/src/main/resources/db/migration/R__MacroData.sql +++ b/adapters/mso-catalog-db-adapter/src/main/resources/db/migration/R__MacroData.sql @@ -228,6 +228,7 @@ values (902, 'VNF-Macro-Modify',2,'ControllerExecutionBB',1,500,'vnf','config-de INSERT INTO orchestration_flow_reference(COMPOSITE_ACTION, SEQ_NO, FLOW_NAME, FLOW_VERSION, NB_REQ_REF_LOOKUP_ID, SCOPE, ACTION) VALUES ('Service-Macro-Create', '10', 'ControllerExecutionBB', 1.0,(SELECT id from northbound_request_ref_lookup WHERE MACRO_ACTION = 'Service-Macro-Create' and CLOUD_OWNER = 'DEFAULT'), 'pnf', 'config-assign'); + INSERT INTO orchestration_flow_reference(COMPOSITE_ACTION, SEQ_NO, FLOW_NAME, FLOW_VERSION, NB_REQ_REF_LOOKUP_ID, SCOPE, ACTION) VALUES ('Service-Macro-Create', '11', 'ControllerExecutionBB', 1.0,(SELECT id from northbound_request_ref_lookup WHERE MACRO_ACTION = 'Service-Macro-Create' and CLOUD_OWNER = 'DEFAULT'), 'pnf', 'config-deploy'); diff --git a/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/BaseNssmfManager.java b/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/BaseNssmfManager.java index 789f4f239d..acaa3945bd 100644 --- a/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/BaseNssmfManager.java +++ b/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/BaseNssmfManager.java @@ -193,7 +193,7 @@ public abstract class BaseNssmfManager implements NssmfManager { return doQuerySubnetCapability(nbiRequest.getSubnetCapabilityQuery()); } - protected abstract <T> RestResponse doQuerySubnetCapability(T req) throws ApplicationException; + protected abstract <T> RestResponse doQuerySubnetCapability(QuerySubnetCapability req) throws ApplicationException; /** * send request to nssmf diff --git a/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/ExternalNssmfManager.java b/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/ExternalNssmfManager.java index 72d1b53781..9dcd2d4cfb 100644 --- a/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/ExternalNssmfManager.java +++ b/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/ExternalNssmfManager.java @@ -20,7 +20,11 @@ package org.onap.so.adapters.nssmf.manager.impl; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.http.Header; +import org.apache.http.HttpStatus; import org.apache.http.message.BasicHeader; import org.onap.aai.domain.yang.ServiceInstance; import org.onap.so.adapters.nssmf.entity.NssmfInfo; @@ -31,14 +35,23 @@ import org.onap.so.adapters.nssmf.exceptions.ApplicationException; import org.onap.so.adapters.nssmf.util.NssmfAdapterUtil; import org.onap.so.beans.nsmf.*; import org.onap.so.db.request.beans.ResourceOperationStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static java.lang.String.valueOf; import static org.onap.so.adapters.nssmf.enums.JobStatus.*; -import static org.onap.so.adapters.nssmf.util.NssmfAdapterUtil.StatusDesc.*; +import static org.onap.so.adapters.nssmf.util.NssmfAdapterUtil.StatusDesc.QUERY_JOB_STATUS_FAILED; +import static org.onap.so.adapters.nssmf.util.NssmfAdapterUtil.StatusDesc.QUERY_JOB_STATUS_SUCCESS; import static org.onap.so.adapters.nssmf.util.NssmfAdapterUtil.marshal; import static org.onap.so.adapters.nssmf.util.NssmfAdapterUtil.unMarshal; public abstract class ExternalNssmfManager extends BaseNssmfManager { + private static final Logger logger = LoggerFactory.getLogger(ExternalNssmfManager.class); + @Override protected String wrapAllocateReqBody(NssmfAdapterNBIRequest nbiRequest) throws ApplicationException { return doWrapExtAllocateReqBody(nbiRequest); @@ -167,11 +180,34 @@ public abstract class ExternalNssmfManager extends BaseNssmfManager { } @Override - protected <T> RestResponse doQuerySubnetCapability(T req) throws ApplicationException { - RestResponse response = new RestResponse(); - response.setStatus(200); - response.setResponseContent(null); - return response; + protected <T> RestResponse doQuerySubnetCapability(QuerySubnetCapability req) throws ApplicationException { + + ObjectMapper oMapper = new ObjectMapper(); + InputStream inputStream = TypeReference.class.getResourceAsStream("/subnetCapability.json"); + Map<String, Object> subnetCapability = new HashMap<>(); + try { + subnetCapability = oMapper.readValue(inputStream, Map.class); + } catch (Exception e) { + logger.debug("Exception while reading subnet capability value from json", e); + } + Map<String, Object> responseMap = new HashMap<>(); + List<String> subnetTypes = req.getSubnetTypes(); + for (String value : subnetTypes) { + if (subnetCapability.containsKey(value)) { + responseMap.put(value, subnetCapability.get(value)); + } + } + String response = null; + try { + response = oMapper.writeValueAsString(responseMap); + } catch (JsonProcessingException e) { + logger.debug("Exception while converting subnet capability object to String {}", e); + } + + RestResponse rsp = new RestResponse(); + rsp.setStatus(HttpStatus.SC_OK); + rsp.setResponseContent(response); + return rsp; } /** diff --git a/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/InternalNssmfManager.java b/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/InternalNssmfManager.java index 88b5071371..296c30c1d8 100644 --- a/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/InternalNssmfManager.java +++ b/adapters/mso-nssmf-adapter/src/main/java/org/onap/so/adapters/nssmf/manager/impl/InternalNssmfManager.java @@ -122,7 +122,7 @@ public abstract class InternalNssmfManager extends BaseNssmfManager { protected abstract String doWrapModifyReqBody(NssmfAdapterNBIRequest nbiRequest) throws ApplicationException; @Override - protected <T> RestResponse doQuerySubnetCapability(T req) throws ApplicationException { + protected <T> RestResponse doQuerySubnetCapability(QuerySubnetCapability req) throws ApplicationException { // handler return sendRequest(marshal(req)); } diff --git a/adapters/mso-nssmf-adapter/src/main/resources/subnetCapability.json b/adapters/mso-nssmf-adapter/src/main/resources/subnetCapability.json new file mode 100644 index 0000000000..8db52168ae --- /dev/null +++ b/adapters/mso-nssmf-adapter/src/main/resources/subnetCapability.json @@ -0,0 +1,19 @@ +{ + "AN": { + "latency": "5", + "maxNumberofUEs": "100" + }, + "CN": { + "latency": "10", + "maxNumberofConns": "100" + }, + "TN_FH": { + "latency": "10" + }, + "TN_MH": { + "latency": "5" + }, + "TN_BH": { + "latency": "10" + } +} diff --git a/adapters/mso-nssmf-adapter/src/test/java/org/onap/so/adapters/nssmf/service/impl/NssmfManagerServiceImplTest.java b/adapters/mso-nssmf-adapter/src/test/java/org/onap/so/adapters/nssmf/service/impl/NssmfManagerServiceImplTest.java index 4a659e1ca3..0d86056821 100644 --- a/adapters/mso-nssmf-adapter/src/test/java/org/onap/so/adapters/nssmf/service/impl/NssmfManagerServiceImplTest.java +++ b/adapters/mso-nssmf-adapter/src/test/java/org/onap/so/adapters/nssmf/service/impl/NssmfManagerServiceImplTest.java @@ -38,6 +38,7 @@ import org.onap.so.adapters.nssmf.entity.TokenResponse; import org.onap.so.adapters.nssmf.enums.HttpMethod; import org.onap.so.adapters.nssmf.util.RestUtil; import org.onap.so.beans.nsmf.*; +import org.onap.so.beans.nsmf.oof.SubnetCapability; import org.onap.so.db.request.beans.ResourceOperationStatus; import org.onap.so.db.request.data.repository.ResourceOperationStatusRepository; import org.springframework.http.ResponseEntity; @@ -429,7 +430,9 @@ public class NssmfManagerServiceImplTest { public void querySubnetCapability() { NssmfAdapterNBIRequest nbiRequest = createNbiRequest(); - String subnetCapabilityQuery = "\"subnetTypes\": [\"TN-FH\",\"TN-MH\",\"TN-BH\"]"; + QuerySubnetCapability subnetCapabilityQuery = new QuerySubnetCapability(); + List<String> subnetTypes = Arrays.asList("TN-FH", "TN-MH", "TN-BH"); + subnetCapabilityQuery.setSubnetTypes(subnetTypes); nbiRequest.setSubnetCapabilityQuery(subnetCapabilityQuery); ResponseEntity res = nssiManagerService.queryNSSISelectionCapability(nbiRequest); assertNotNull(res); diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/inventory/CreateAAIInventory.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/inventory/CreateAAIInventory.java index a6194517c2..14d0b0afc4 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/inventory/CreateAAIInventory.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/inventory/CreateAAIInventory.java @@ -53,12 +53,18 @@ public class CreateAAIInventory { @Autowired protected CloudConfig cloudConfig; + private static final String MULTICLOUD_MODE = "MULTICLOUD"; + @Autowired protected Environment env; public void heatbridge(CloudInformation cloudInformation) throws HeatBridgeException, MsoCloudSiteNotFound { CloudSite cloudSite = cloudConfig.getCloudSite(cloudInformation.getRegionId()) .orElseThrow(() -> new MsoCloudSiteNotFound(cloudInformation.getRegionId())); + if (cloudSite.getOrchestrator() != null && MULTICLOUD_MODE.equalsIgnoreCase(cloudSite.getOrchestrator())) { + logger.debug("Skipping Heatbridge as CloudSite orchestrator is: " + MULTICLOUD_MODE); + return; + } CloudIdentity cloudIdentity = cloudSite.getIdentityService(); String heatStackId = cloudInformation.getTemplateInstanceId().split("/")[1]; diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/PollService.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/PollService.java index 6a5381cf3e..c319fdc23c 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/PollService.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/PollService.java @@ -40,6 +40,7 @@ import org.onap.so.adapters.vnfrest.DeleteVfModuleRequest; import org.onap.so.adapters.vnfrest.DeleteVolumeGroupRequest; import org.onap.so.adapters.nwrest.DeleteNetworkRequest; import org.onap.so.adapters.nwrest.UpdateNetworkRequest; +import org.onap.so.adapters.vnf.VnfAdapterUtils; import org.onap.so.logging.tasks.AuditMDCSetup; import org.onap.so.openstack.exceptions.MsoException; import org.onap.so.openstack.utils.MsoHeatUtils; @@ -60,6 +61,9 @@ public class PollService extends ExternalTaskUtils { private MsoHeatUtils msoHeatUtils; @Autowired + private VnfAdapterUtils vnfAdapterUtils; + + @Autowired private AuditMDCSetup mdcSetup; public PollService() { @@ -84,12 +88,22 @@ public class PollService extends ExternalTaskUtils { logger.debug("Executing External Task Poll Service for Delete Vf Module"); DeleteVfModuleRequest req = JAXB.unmarshal(new StringReader(xmlRequest), DeleteVfModuleRequest.class); - pollDeleteResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + boolean isMulticloud = vnfAdapterUtils.isMulticloudMode(null, req.getCloudSiteId()); + if (!isMulticloud) { + pollDeleteResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + } else { + success.setTrue(); + } } else if ("deleteVolumeGroupRequest".equals(requestType.get())) { logger.debug("Executing External Task Poll Service for Delete Volume Group"); DeleteVolumeGroupRequest req = JAXB.unmarshal(new StringReader(xmlRequest), DeleteVolumeGroupRequest.class); - pollDeleteResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + boolean isMulticloud = vnfAdapterUtils.isMulticloudMode(null, req.getCloudSiteId()); + if (!isMulticloud) { + pollDeleteResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + } else { + success.setTrue(); + } } else if ("createNetworkRequest".equals(requestType.get())) { determineCreateNetworkStatus(xmlRequest, externalTask, success); } else if ("deleteNetworkRequest".equals(requestType.get())) { @@ -133,25 +147,35 @@ public class PollService extends ExternalTaskUtils { private void determineCreateVolumeGroupStatus(String xmlRequest, ExternalTask externalTask, MutableBoolean success) throws MsoException { CreateVolumeGroupRequest req = JAXB.unmarshal(new StringReader(xmlRequest), CreateVolumeGroupRequest.class); - boolean pollRollbackStatus = externalTask.getVariable("PollRollbackStatus"); - if (pollRollbackStatus) { - logger.debug("Executing External Task Poll Service for Rollback Create Volume Group"); - pollDeleteResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + boolean isMulticloud = vnfAdapterUtils.isMulticloudMode(null, req.getCloudSiteId()); + if (!isMulticloud) { + boolean pollRollbackStatus = externalTask.getVariable("PollRollbackStatus"); + if (pollRollbackStatus) { + logger.debug("Executing External Task Poll Service for Rollback Create Volume Group"); + pollDeleteResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + } else { + pollCreateResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + } } else { - pollCreateResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + success.setTrue(); } } private void determineCreateVfModuleStatus(String xmlRequest, ExternalTask externalTask, MutableBoolean success) throws MsoException { CreateVfModuleRequest req = JAXB.unmarshal(new StringReader(xmlRequest), CreateVfModuleRequest.class); - boolean pollRollbackStatus = externalTask.getVariable("PollRollbackStatus"); - if (pollRollbackStatus) { - logger.debug("Executing External Task Poll Service for Rollback Create Vf Module"); - pollDeleteResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + boolean isMulticloud = vnfAdapterUtils.isMulticloudMode(null, req.getCloudSiteId()); + if (!isMulticloud) { + boolean pollRollbackStatus = externalTask.getVariable("PollRollbackStatus"); + if (pollRollbackStatus) { + logger.debug("Executing External Task Poll Service for Rollback Create Vf Module"); + pollDeleteResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + } else { + logger.debug("Executing External Task Poll Service for Create Vf Module"); + pollCreateResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + } } else { - logger.debug("Executing External Task Poll Service for Create Vf Module"); - pollCreateResource(req.getCloudSiteId(), req.getTenantId(), externalTask, success); + success.setTrue(); } } diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/RollbackService.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/RollbackService.java index c25d752cc4..d6d2264217 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/RollbackService.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/RollbackService.java @@ -14,6 +14,8 @@ import org.camunda.bpm.client.task.ExternalTaskService; import org.onap.so.adapters.network.MsoNetworkAdapterImpl; import org.onap.so.adapters.nwrest.CreateNetworkRequest; import org.onap.so.adapters.vnf.MsoVnfAdapterImpl; +import org.onap.so.adapters.vnf.MsoVnfPluginAdapterImpl; +import org.onap.so.adapters.vnf.VnfAdapterUtils; import org.onap.so.adapters.vnfrest.CreateVfModuleRequest; import org.onap.so.adapters.vnfrest.CreateVolumeGroupRequest; import org.onap.so.logging.tasks.AuditMDCSetup; @@ -35,6 +37,12 @@ public class RollbackService extends ExternalTaskUtils { private MsoNetworkAdapterImpl networkAdapterImpl; @Autowired + private VnfAdapterUtils vnfAdapterUtils; + + @Autowired + private MsoVnfPluginAdapterImpl vnfPluginImpl; + + @Autowired private AuditMDCSetup mdcSetup; public void executeExternalTask(ExternalTask externalTask, ExternalTaskService externalTaskService) { @@ -51,19 +59,38 @@ public class RollbackService extends ExternalTaskUtils { logger.debug("Executing External Task Rollback Service for Create Volume Group"); CreateVolumeGroupRequest req = JAXB.unmarshal(new StringReader(xmlRequest), CreateVolumeGroupRequest.class); - vnfAdapterImpl.deleteVnf(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), - req.getVolumeGroupName(), req.getMsoRequest(), false); - pollRollbackStatus = true; - success = true; + boolean isMulticloud = vnfAdapterUtils.isMulticloudMode(null, req.getCloudSiteId()); + if (!isMulticloud) { + vnfAdapterImpl.deleteVnf(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + req.getVolumeGroupName(), req.getMsoRequest(), false); + pollRollbackStatus = true; + success = true; + } else { + vnfPluginImpl.deleteVnf(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + req.getVolumeGroupName(), req.getMsoRequest()); + pollRollbackStatus = false; + success = true; + } } else if ("createVfModuleRequest".equals(requestType.get())) { logger.debug("Executing External Task Rollback Service for Create Vf Module"); CreateVfModuleRequest req = JAXB.unmarshal(new StringReader(xmlRequest), CreateVfModuleRequest.class); - vnfAdapterImpl.deleteVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), - req.getVfModuleName(), req.getVnfId(), req.getVfModuleId(), req.getModelCustomizationUuid(), - req.getMsoRequest(), new Holder<>()); - pollRollbackStatus = true; - success = true; + boolean isMulticloud = vnfAdapterUtils.isMulticloudMode(null, req.getCloudSiteId()); + if (!isMulticloud) { + vnfAdapterImpl.deleteVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + req.getVfModuleName(), req.getVnfId(), req.getVfModuleId(), + req.getModelCustomizationUuid(), req.getMsoRequest(), new Holder<>()); + pollRollbackStatus = true; + success = true; + } else { + /* + * vnfPluginImpl.deleteVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + * req.getVfModuleName(),req.getMsoRequest(), new Holder<>()); TODO: Figure out how to properly + * rollback + */ + pollRollbackStatus = false; + success = true; + } } else if ("createNetworkRequest".equals(requestType.get())) { logger.debug("Executing External Task Rollback Service for Create Network"); Holder<Boolean> networkDeleted = new Holder<>(); diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/StackService.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/StackService.java index c9fa30d949..7be1620b0e 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/StackService.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/tasks/orchestration/StackService.java @@ -48,6 +48,8 @@ import org.onap.so.adapters.nwrest.RollbackNetworkResponse; import org.onap.so.adapters.nwrest.UpdateNetworkRequest; import org.onap.so.adapters.nwrest.UpdateNetworkResponse; import org.onap.so.adapters.vnf.MsoVnfAdapterImpl; +import org.onap.so.adapters.vnf.MsoVnfPluginAdapterImpl; +import org.onap.so.adapters.vnf.VnfAdapterUtils; import org.onap.so.adapters.vnf.exceptions.VnfException; import org.onap.so.adapters.vnfrest.CreateVfModuleRequest; import org.onap.so.adapters.vnfrest.CreateVfModuleResponse; @@ -81,6 +83,12 @@ public class StackService extends ExternalTaskUtils { private MsoNetworkAdapterImpl networkAdapterImpl; @Autowired + private MsoVnfPluginAdapterImpl vnfPluginImpl; + + @Autowired + private VnfAdapterUtils vnfAdapterUtils; + + @Autowired private AuditMDCSetup mdcSetup; private static final String SHARED = "shared"; @@ -160,10 +168,18 @@ public class StackService extends ExternalTaskUtils { Holder<String> stackId = new Holder<>(); CreateVolumeGroupRequest req = JAXB.unmarshal(new StringReader(xmlRequest), CreateVolumeGroupRequest.class); String completeVnfVfModuleType = req.getVnfType() + "::" + req.getVfModuleType(); - vnfAdapterImpl.createVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), - completeVnfVfModuleType, req.getVnfVersion(), "", req.getVolumeGroupName(), "", "VOLUME", null, null, - req.getModelCustomizationUuid(), req.getVolumeGroupParams(), false, true, req.getEnableBridge(), - req.getMsoRequest(), stackId, outputs, vnfRollback); + boolean isMulticloud = vnfAdapterUtils.isMulticloudMode(null, req.getCloudSiteId()); + if (isMulticloud) { + vnfPluginImpl.createVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + completeVnfVfModuleType, req.getVnfVersion(), "", req.getVolumeGroupName(), "", "VOLUME", null, + null, req.getModelCustomizationUuid(), req.getVolumeGroupParams(), false, true, + req.getEnableBridge(), req.getMsoRequest(), stackId, outputs, vnfRollback); + } else { + vnfAdapterImpl.createVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + completeVnfVfModuleType, req.getVnfVersion(), "", req.getVolumeGroupName(), "", "VOLUME", null, + null, req.getModelCustomizationUuid(), req.getVolumeGroupParams(), false, true, + req.getEnableBridge(), req.getMsoRequest(), stackId, outputs, vnfRollback); + } success.setTrue(); backout.setValue(!req.getSuppressBackout()); VolumeGroupRollback rb = new VolumeGroupRollback(req.getVolumeGroupId(), stackId.value, @@ -181,11 +197,20 @@ public class StackService extends ExternalTaskUtils { Holder<String> stackId = new Holder<>(); CreateVfModuleRequest req = JAXB.unmarshal(new StringReader(xmlRequest), CreateVfModuleRequest.class); String completeVnfVfModuleType = req.getVnfType() + "::" + req.getVfModuleType(); - vnfAdapterImpl.createVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), - completeVnfVfModuleType, req.getVnfVersion(), req.getVnfId(), req.getVfModuleName(), - req.getVfModuleId(), req.getRequestType(), req.getVolumeGroupStackId(), req.getBaseVfModuleStackId(), - req.getModelCustomizationUuid(), req.getVfModuleParams(), false, false, req.getEnableBridge(), - req.getMsoRequest(), stackId, outputs, vnfRollback); + boolean isMulticloud = vnfAdapterUtils.isMulticloudMode(null, req.getCloudSiteId()); + if (isMulticloud) { + vnfPluginImpl.createVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + completeVnfVfModuleType, req.getVnfVersion(), req.getVnfId(), req.getVfModuleName(), + req.getVfModuleId(), req.getRequestType(), req.getVolumeGroupStackId(), + req.getBaseVfModuleStackId(), req.getModelCustomizationUuid(), req.getVfModuleParams(), false, + false, req.getEnableBridge(), req.getMsoRequest(), stackId, outputs, vnfRollback); + } else { + vnfAdapterImpl.createVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + completeVnfVfModuleType, req.getVnfVersion(), req.getVnfId(), req.getVfModuleName(), + req.getVfModuleId(), req.getRequestType(), req.getVolumeGroupStackId(), + req.getBaseVfModuleStackId(), req.getModelCustomizationUuid(), req.getVfModuleParams(), false, + false, req.getEnableBridge(), req.getMsoRequest(), stackId, outputs, vnfRollback); + } success.setTrue(); backout.setValue(req.getBackout()); canonicalStackId.value = stackId.value; @@ -201,9 +226,15 @@ public class StackService extends ExternalTaskUtils { MutableBoolean success) throws VnfException { backout.setFalse(); DeleteVfModuleRequest req = JAXB.unmarshal(new StringReader(xmlRequest), DeleteVfModuleRequest.class); - vnfAdapterImpl.deleteVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), - req.getVfModuleStackId(), req.getVnfId(), req.getVfModuleId(), req.getModelCustomizationUuid(), - req.getMsoRequest(), outputs); + boolean isMulticloud = vnfAdapterUtils.isMulticloudMode(null, req.getCloudSiteId()); + if (isMulticloud) { + vnfPluginImpl.deleteVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + req.getVfModuleStackId(), req.getMsoRequest(), outputs); + } else { + vnfAdapterImpl.deleteVfModule(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), + req.getVfModuleStackId(), req.getVnfId(), req.getVfModuleId(), req.getModelCustomizationUuid(), + req.getMsoRequest(), outputs); + } success.setTrue(); if (outputs != null && outputs.value != null) { canonicalStackId.value = outputs.value.get("canonicalStackId"); @@ -220,7 +251,6 @@ public class StackService extends ExternalTaskUtils { MutableBoolean success) throws VnfException { backout.setFalse(); DeleteVolumeGroupRequest req = JAXB.unmarshal(new StringReader(xmlRequest), DeleteVolumeGroupRequest.class); - vnfAdapterImpl.deleteVnf(req.getCloudSiteId(), req.getCloudOwner(), req.getTenantId(), req.getVolumeGroupStackId(), req.getMsoRequest(), false); success.setTrue(); diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImpl.java new file mode 100644 index 0000000000..6b979833eb --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfPluginAdapterImpl.java @@ -0,0 +1,1175 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - SO + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Modifications Copyright (c) 2019 Samsung + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +/** + * This VNF Adapter implementation is based on the VDU Plugin model. It assumes that each VF Module definition in the + * MSO catalog is expressed via a set of template and/or file artifacts that are appropriate for some specific + * sub-orchestrator that provides an implementation of the VduPlugin interface. This adapter handles all of the common + * VF Module logic, including: - catalog lookups for artifact retrieval - parameter filtering and validation - base and + * volume module queries - rollback logic - logging and error handling + * + * Then based on the orchestration mode of the VNF, it will invoke different VDU plug-ins to perform the low level + * instantiations, deletions, and queries. At this time, the set of available plug-ins is hard-coded, though in the + * future a dynamic selection is expected (e.g. via a service-provider interface). + */ +package org.onap.so.adapters.vnf; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import javax.jws.WebService; +import javax.xml.ws.Holder; +import org.onap.so.adapters.vdu.CloudInfo; +import org.onap.so.adapters.vdu.VduException; +import org.onap.so.adapters.vdu.VduInstance; +import org.onap.so.adapters.vdu.VduModelInfo; +import org.onap.so.adapters.vdu.VduPlugin; +import org.onap.so.adapters.vdu.VduStateType; +import org.onap.so.adapters.vdu.VduStatus; +import org.onap.so.adapters.vdu.mapper.VfModuleCustomizationToVduMapper; +import org.onap.so.adapters.vnf.exceptions.VnfAlreadyExists; +import org.onap.so.adapters.vnf.exceptions.VnfException; +import org.onap.so.cloud.CloudConfig; +import org.onap.so.db.catalog.beans.CloudSite; +import org.onap.so.db.catalog.beans.HeatEnvironment; +import org.onap.so.db.catalog.beans.HeatTemplate; +import org.onap.so.db.catalog.beans.HeatTemplateParam; +import org.onap.so.db.catalog.beans.VfModule; +import org.onap.so.db.catalog.beans.VfModuleCustomization; +import org.onap.so.db.catalog.beans.VnfResource; +import org.onap.so.db.catalog.data.repository.VFModuleCustomizationRepository; +import org.onap.so.db.catalog.utils.MavenLikeVersioning; +import org.onap.so.entity.MsoRequest; +import org.onap.logging.filter.base.ErrorCode; +import org.onap.so.logger.MessageEnum; +import org.onap.so.openstack.beans.VnfRollback; +import org.onap.so.openstack.beans.VnfStatus; +import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound; +import org.onap.so.openstack.exceptions.MsoException; +import org.onap.so.openstack.exceptions.MsoExceptionCategory; +import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry; +import org.onap.so.openstack.utils.MsoHeatUtils; +import org.onap.so.openstack.utils.MsoKeystoneUtils; +import org.onap.so.openstack.utils.MsoMulticloudUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +@WebService(serviceName = "VnfAdapter", endpointInterface = "org.onap.so.adapters.vnf.MsoVnfAdapter", + targetNamespace = "http://org.onap.so/vnf") +@Component +@Transactional +public class MsoVnfPluginAdapterImpl { + + private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError"; + private static Logger logger = LoggerFactory.getLogger(MsoVnfPluginAdapterImpl.class); + + private static final String CHECK_REQD_PARAMS = "org.onap.so.adapters.vnf.checkRequiredParameters"; + private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); + + @Autowired + protected CloudConfig cloudConfig; + + @Autowired + private VFModuleCustomizationRepository vfModuleCustomRepo; + + @Autowired + private Environment environment; + + @Autowired + protected MsoKeystoneUtils keystoneUtils; + + @Autowired + protected MsoHeatUtils heatUtils; + + @Autowired + protected MsoMulticloudUtils multicloudUtils; + + @Autowired + protected VfModuleCustomizationToVduMapper vduMapper; + + /** + * Health Check web method. Does nothing but return to show the adapter is deployed. + */ + + public void healthCheck() { + logger.debug("Health check call in VNF Plugin Adapter"); + } + + /** + * DO NOT use that constructor to instantiate this class, the msoPropertiesfactory will be NULL. + * + * @see MsoVnfPluginAdapterImpl#MsoVnfAdapterImpl(MsoPropertiesFactory, CloudConfigFactory) + */ + public MsoVnfPluginAdapterImpl() { + + } + + /** + * This is the "Create VNF" web service implementation. This function is now unsupported and will return an error. + * + */ + public void createVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfType, String vnfVersion, + String vnfName, String requestType, String volumeGroupHeatStackId, Map<String, Object> inputs, + Boolean failIfExists, Boolean backout, Boolean enableBridge, MsoRequest msoRequest, Holder<String> vnfId, + Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback) throws VnfException { + // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules. + logger.debug("CreateVNF command attempted but not supported"); + throw new VnfException("CreateVNF: Unsupported command", MsoExceptionCategory.USERDATA); + } + + /** + * This is the "Update VNF" web service implementation. This function is now unsupported and will return an error. + * + */ + public void updateVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfType, String vnfVersion, + String vnfName, String requestType, String volumeGroupHeatStackId, Map<String, Object> inputs, + MsoRequest msoRequest, Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback) + throws VnfException { + // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules. + logger.debug("UpdateVNF command attempted but not supported"); + throw new VnfException("UpdateVNF: Unsupported command", MsoExceptionCategory.USERDATA); + } + + /** + * This is the "Query VNF" web service implementation. + * + * This really should be QueryVfModule, but nobody ever changed it. + * + * The method returns an indicator that the VNF exists, along with its status and outputs. The input "vnfName" will + * also be reflected back as its ID. + * + * @param cloudSiteId CLLI code of the cloud site in which to query + * @param tenantId Openstack tenant identifier + * @param vnfNameOrId VNF Name or ID to query + * @param msoRequest Request tracking information for logs + * @param vnfExists Flag reporting the result of the query + * @param vnfId Holder for output VNF ID + * @param outputs Holder for Map of outputs from the deployed VF Module (assigned IPs, etc) + */ + public void queryVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfNameOrId, + MsoRequest msoRequest, Holder<Boolean> vnfExists, Holder<String> vnfId, Holder<VnfStatus> status, + Holder<Map<String, String>> outputs) throws VnfException { + logger.debug("Querying VNF " + vnfNameOrId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId); + + // Will capture execution time for metrics + long startTime = System.currentTimeMillis(); + long subStartTime = System.currentTimeMillis(); + + VduInstance vduInstance = null; + CloudInfo cloudInfo = new CloudInfo(cloudSiteId, cloudOwner, tenantId, null); + + VduPlugin vduPlugin = getVduPlugin(cloudSiteId, cloudOwner); + + try { + vduInstance = vduPlugin.queryVdu(cloudInfo, vnfNameOrId); + } catch (VduException e) { + // Failed to query the VDU due to a plugin exception. + // Convert to a generic VnfException + e.addContext("QueryVNF"); + String error = "Query VNF (VDU): " + vnfNameOrId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + + ": " + e; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vnfNameOrId, cloudOwner, + cloudSiteId, tenantId, "VDU", "QueryVNF", ErrorCode.DataError.getValue(), "Exception - queryVDU", + e); + logger.debug(error); + throw new VnfException(e); + } + + if (vduInstance != null && vduInstance.getStatus().getState() != VduStateType.NOTFOUND) { + vnfExists.value = Boolean.TRUE; + status.value = vduStatusToVnfStatus(vduInstance); + vnfId.value = vduInstance.getVduInstanceId(); + outputs.value = copyStringOutputs(vduInstance.getOutputs()); + + logger.debug("VNF {} found, ID = {}", vnfNameOrId, vnfId.value); + } else { + vnfExists.value = Boolean.FALSE; + status.value = VnfStatus.NOTFOUND; + vnfId.value = null; + outputs.value = new HashMap<String, String>(); // Return as an empty map + + logger.debug("VNF {} not found", vnfNameOrId); + } + return; + } + + + /** + * This is the "Delete VNF" web service implementation. This function is now unsupported and will return an error. + * + */ + + public void deleteVnf(String cloudSiteId, String cloudOwner, String tenantId, String vnfName, MsoRequest msoRequest) + throws VnfException { + + // This operation is no longer supported at the VNF level. The adapter is only called to deploy modules. + logger.debug("DeleteVNF command attempted but not supported"); + throw new VnfException("DeleteVNF: Unsupported command", MsoExceptionCategory.USERDATA); + } + + /** + * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the + * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation + * to undo the creation. + * + * TODO: This should be rollbackVfModule and/or rollbackVolumeGroup, but APIs were apparently never updated. + */ + + public void rollbackVnf(VnfRollback rollback) throws VnfException { + long startTime = System.currentTimeMillis(); + // rollback may be null (e.g. if stack already existed when Create was called) + if (rollback == null) { + logger.info("{} {} {}", MessageEnum.RA_ROLLBACK_NULL.toString(), "OpenStack", "rollbackVnf"); + return; + } + + // Don't rollback if nothing was done originally + if (!rollback.getVnfCreated()) { + return; + } + + // Get the elements of the VnfRollback object for easier access + String cloudSiteId = rollback.getCloudSiteId(); + String cloudOwner = rollback.getCloudOwner(); + String tenantId = rollback.getTenantId(); + CloudInfo cloudInfo = new CloudInfo(cloudSiteId, cloudOwner, tenantId, null); + + String vfModuleId = rollback.getVfModuleStackId(); + + logger.debug("Rolling Back VF Module " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId); + + VduInstance vduInstance = null; + + // Use the VduPlugin to delete the VF Module. + VduPlugin vduPlugin = getVduPlugin(cloudSiteId, cloudOwner); + + long subStartTime = System.currentTimeMillis(); + try { + // TODO: Get a reasonable timeout. Use a global property, or store the creation timeout in rollback object + // and use that. + vduInstance = vduPlugin.deleteVdu(cloudInfo, vfModuleId, 5); + + logger.debug("Rolled back VDU instantiation: {}", vduInstance.getVduInstanceId()); + } catch (VduException ve) { + // Failed to rollback the VF Module due to a plugin exception. + // Convert to a generic VnfException + ve.addContext("RollbackVFModule"); + String error = "Rollback VF Module: " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + + tenantId + ": " + ve; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vfModuleId, cloudOwner, + cloudSiteId, tenantId, "VDU", "DeleteVdu", ErrorCode.DataError.getValue(), "Exception - DeleteVdu", + ve); + logger.debug(error); + throw new VnfException(ve); + } + return; + } + + + private VnfStatus vduStatusToVnfStatus(VduInstance vdu) { + // Determine the status based on last action & status + // DeploymentInfo object should be enhanced to report a better status internally. + VduStatus vduStatus = vdu.getStatus(); + VduStateType status = vduStatus.getState(); + + if (status == null) { + return VnfStatus.UNKNOWN; + } else if (status == VduStateType.NOTFOUND) { + return VnfStatus.NOTFOUND; + } else if (status == VduStateType.INSTANTIATED) { + return VnfStatus.ACTIVE; + } else if (status == VduStateType.FAILED) { + return VnfStatus.FAILED; + } + + return VnfStatus.UNKNOWN; + } + + /* + * Normalize an input value to an Object, based on the target parameter type. If the type is not recognized, it will + * just be returned unchanged (as a string). + */ + private Object convertInputValue(Object inputValue, HeatTemplateParam templateParam) { + String type = templateParam.getParamType(); + logger.debug("Parameter: {} is of type ", templateParam.getParamName(), type); + + if (type.equalsIgnoreCase("number")) { + try { + return Integer.valueOf(inputValue.toString()); + } catch (Exception e) { + logger.debug("Unable to convert " + inputValue + " to an integer!", e); + return null; + } + } else if (type.equalsIgnoreCase("json")) { + try { + JsonNode jsonNode = JSON_MAPPER.readTree(JSON_MAPPER.writeValueAsString(inputValue)); + return jsonNode; + } catch (Exception e) { + logger.debug("Unable to convert " + inputValue + " to a JsonNode!", e); + return null; + } + } else if (type.equalsIgnoreCase("boolean")) { + return new Boolean(inputValue.toString()); + } + + // Nothing else matched. Return the original string + return inputValue; + } + + private Map<String, String> copyStringOutputs(Map<String, Object> stackOutputs) { + Map<String, String> stringOutputs = new HashMap<String, String>(); + for (String key : stackOutputs.keySet()) { + if (stackOutputs.get(key) instanceof String) { + stringOutputs.put(key, (String) stackOutputs.get(key)); + } else if (stackOutputs.get(key) instanceof Integer) { + try { + String str = "" + stackOutputs.get(key); + stringOutputs.put(key, str); + } catch (Exception e) { + logger.debug("Unable to add {} to outputs", key, e); + } + } else if (stackOutputs.get(key) instanceof JsonNode) { + try { + String str = this.convertNode((JsonNode) stackOutputs.get(key)); + stringOutputs.put(key, str); + } catch (Exception e) { + logger.debug("Unable to add {} to outputs - exception converting JsonNode", key, e); + } + } else if (stackOutputs.get(key) instanceof java.util.LinkedHashMap) { + try { + String str = JSON_MAPPER.writeValueAsString(stackOutputs.get(key)); + stringOutputs.put(key, str); + } catch (Exception e) { + logger.debug("Unable to add {} to outputs - exception converting LinkedHashMap", key, e); + } + } else { + try { + String str = stackOutputs.get(key).toString(); + stringOutputs.put(key, str); + } catch (Exception e) { + logger.debug("Unable to add " + key + " to outputs - unable to call .toString() " + e.getMessage(), + e); + } + } + } + return stringOutputs; + } + + + private void sendMapToDebug(Map<String, Object> inputs, String optionalName) { + int i = 0; + StringBuilder sb = new StringBuilder(optionalName == null ? "\ninputs" : "\n" + optionalName); + if (inputs == null) { + sb.append("\tNULL"); + } else if (inputs.size() < 1) { + sb.append("\tEMPTY"); + } else { + for (String str : inputs.keySet()) { + String outputString; + try { + outputString = inputs.get(str).toString(); + } catch (Exception e) { + outputString = "Unable to call toString() on the value for " + str; + } + sb.append("\t\nitem " + i++ + ": '" + str + "'='" + outputString + "'"); + } + } + logger.debug(sb.toString()); + return; + } + + private void sendMapToDebug(Map<String, Object> inputs) { + int i = 0; + StringBuilder sb = new StringBuilder("inputs:"); + if (inputs == null) { + sb.append("\tNULL"); + } else if (inputs.size() < 1) { + sb.append("\tEMPTY"); + } else { + for (String str : inputs.keySet()) { + sb.append("\titem " + i++ + ": " + str + "=" + inputs.get(str)); + } + } + logger.debug(sb.toString()); + return; + } + + private String convertNode(final JsonNode node) { + try { + final Object obj = JSON_MAPPER.treeToValue(node, Object.class); + final String json = JSON_MAPPER.writeValueAsString(obj); + return json; + } catch (JsonParseException jpe) { + logger.debug("Error converting json to string " + jpe.getMessage()); + } catch (Exception e) { + logger.debug("Error converting json to string " + e.getMessage()); + } + return "[Error converting json to string]"; + } + + private Map<String, String> convertMapStringObjectToStringString(Map<String, Object> objectMap) { + if (objectMap == null) { + return null; + } + Map<String, String> stringMap = new HashMap<String, String>(); + for (String key : objectMap.keySet()) { + if (!stringMap.containsKey(key)) { + Object obj = objectMap.get(key); + if (obj instanceof String) { + stringMap.put(key, (String) objectMap.get(key)); + } else if (obj instanceof JsonNode) { + // This is a bit of mess - but I think it's the least impacting + // let's convert it BACK to a string - then it will get converted back later + try { + String str = this.convertNode((JsonNode) obj); + stringMap.put(key, str); + } catch (Exception e) { + logger.debug("DANGER WILL ROBINSON: unable to convert value for JsonNode " + key, e); + // okay in this instance - only string values (fqdn) are expected to be needed + } + } else if (obj instanceof java.util.LinkedHashMap) { + logger.debug("LinkedHashMap - this is showing up as a LinkedHashMap instead of JsonNode"); + try { + String str = JSON_MAPPER.writeValueAsString(obj); + stringMap.put(key, str); + } catch (Exception e) { + logger.debug("DANGER WILL ROBINSON: unable to convert value for LinkedHashMap " + key, e); + } + } else if (obj instanceof Integer) { + try { + String str = "" + obj; + stringMap.put(key, str); + } catch (Exception e) { + logger.debug("DANGER WILL ROBINSON: unable to convert value for Integer " + key, e); + } + } else { + try { + String str = obj.toString(); + stringMap.put(key, str); + } catch (Exception e) { + logger.debug( + "DANGER WILL ROBINSON: unable to convert value " + key + " (" + e.getMessage() + ")", + e); + } + } + } + } + + return stringMap; + } + + /** + * This is the "Create VF Module" web service implementation. It will instantiate a new VF Module of the requested + * type in the specified cloud and tenant. The tenant must exist before this service is called. + * + * If a VF Module with the same name already exists, this can be considered a success or failure, depending on the + * value of the 'failIfExists' parameter. + * + * All VF Modules are defined in the MSO catalog. The caller must request one of the pre-defined module types or an + * error will be returned. Within the catalog, each VF Module references (among other things) a collection of + * artifacts that are used to deploy the required cloud resources (VMs, networks, etc.). + * + * Depending on the module templates, a variable set of input parameters will be defined, some of which are + * required. The caller is responsible to pass the necessary input data for the module or an error will be thrown. + * + * The method returns the vfModuleId, a Map of output attributes, and a VnfRollback object. This last object can be + * passed as-is to the rollbackVnf operation to undo everything that was created for the Module. This is useful if a + * VF module is successfully created but the orchestration fails on a subsequent step. + * + * @param cloudSiteId CLLI code of the cloud site in which to create the VNF + * @param cloudOwner cloud owner of the cloud site in which to create the VNF + * @param tenantId Openstack tenant identifier + * @param vfModuleType VF Module type key, should match a VNF definition in catalog DB. Deprecated - should use + * modelCustomizationUuid + * @param vnfVersion VNF version key, should match a VNF definition in catalog DB Deprecated - VF Module versions + * also captured by modelCustomizationUuid + * @param vnfId - VNF ID + * @param vfModuleName Name to be assigned to the new VF Module + * @param vfModuleId Id fo the new VF Module + * @param requestType Indicates if this is a Volume Group or Module request + * @param volumeGroupId Identifier (i.e. deployment ID) for a Volume Group to attach to a VF Module + * @param baseVfModuleId Identifier (i.e. deployment ID) of the Base Module if this is an Add-on module + * @param modelCustomizationUuid Unique ID for the VF Module's model. Replaces the use of vfModuleType. + * @param inputs Map of key=value inputs for VNF stack creation + * @param failIfExists Flag whether already existing VNF should be considered + * @param backout Flag whether to suppress automatic backout (for testing) + * @param msoRequest Request tracking information for logs + * @param vnfId Holder for output VF Module instance ID in the cloud + * @param outputs Holder for Map of VNF outputs from Deployment (assigned IPs, etc) + * @param rollback Holder for returning VnfRollback object + */ + + public void createVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vfModuleType, + String vnfVersion, String genericVnfId, String vfModuleName, String vfModuleId, String requestType, + String volumeGroupId, String baseVfModuleId, String modelCustomizationUuid, Map<String, Object> inputs, + Boolean failIfExists, Boolean backout, Boolean enableBridge, MsoRequest msoRequest, Holder<String> vnfId, + Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback) throws VnfException { + // Will capture execution time for metrics + long startTime = System.currentTimeMillis(); + + // Require a model customization ID. Every VF Module definition must have one. + if (modelCustomizationUuid == null || modelCustomizationUuid.isEmpty()) { + logger.debug("Missing required input: modelCustomizationUuid"); + String error = "Create vfModule error: Missing required input: modelCustomizationUuid"; + logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), + "VF Module ModelCustomizationUuid", "VDU", ErrorCode.DataError, + "Create VF Module: " + "Missing required input: modelCustomizationUuid"); + logger.debug(error); + throw new VnfException(error, MsoExceptionCategory.USERDATA); + } + + // Clean up some inputs to make comparisons easier + if (requestType == null) + requestType = ""; + + if ("".equals(volumeGroupId) || "null".equals(volumeGroupId)) + volumeGroupId = null; + + if ("".equals(baseVfModuleId) || "null".equals(baseVfModuleId)) + baseVfModuleId = null; + + if (inputs == null) { + // Create an empty set of inputs + inputs = new HashMap<>(); + logger.debug("inputs == null - setting to empty"); + } else { + this.sendMapToDebug(inputs); + } + + // Check if this is for a "Volume" module + boolean isVolumeRequest = false; + if (requestType.startsWith("VOLUME")) { + isVolumeRequest = true; + } + + logger.debug("requestType = " + requestType + ", volumeGroupStackId = " + volumeGroupId + ", baseStackId = " + + baseVfModuleId); + + // Build a default rollback object (no actions performed) + VnfRollback vfRollback = new VnfRollback(); + vfRollback.setCloudSiteId(cloudSiteId); + vfRollback.setCloudOwner(cloudOwner); + vfRollback.setTenantId(tenantId); + vfRollback.setMsoRequest(msoRequest); + vfRollback.setRequestType(requestType); + vfRollback.setIsBase(false); // Until we know better + vfRollback.setVolumeGroupHeatStackId(volumeGroupId); + vfRollback.setBaseGroupHeatStackId(baseVfModuleId); + vfRollback.setModelCustomizationUuid(modelCustomizationUuid); + vfRollback.setMode("CFY"); + + rollback.value = vfRollback; // Default rollback - no updates performed + + // Get the VNF/VF Module definition from the Catalog DB first. + // There are three relevant records: VfModule, VfModuleCustomization, VnfResource + + VfModule vfModule = null; + VnfResource vnfResource = null; + VfModuleCustomization vfModuleCust = null; + + try { + vfModuleCust = + vfModuleCustomRepo.findFirstByModelCustomizationUUIDOrderByCreatedDesc(modelCustomizationUuid); + + if (vfModuleCust == null) { + String error = "Create vfModule error: Unable to find vfModuleCust with modelCustomizationUuid=" + + modelCustomizationUuid; + logger.debug(error); + logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), + "VF Module ModelCustomizationUuid", modelCustomizationUuid, "CatalogDb", ErrorCode.DataError, + error); + throw new VnfException(error, MsoExceptionCategory.USERDATA); + } else { + logger.debug("Found vfModuleCust entry {}", vfModuleCust.toString()); + } + + // Get the vfModule and vnfResource records + vfModule = vfModuleCust.getVfModule(); + vnfResource = vfModuleCust.getVfModule().getVnfResources(); + } catch (Exception e) { + + logger.debug("unhandled exception in create VF - [Query]" + e.getMessage()); + throw new VnfException("Exception during create VF " + e.getMessage()); + } + + // Perform a version check against cloudSite + // Obtain the cloud site information where we will create the VF Module + Optional<CloudSite> cloudSiteOp = cloudConfig.getCloudSite(cloudSiteId); + if (!cloudSiteOp.isPresent()) { + // If cloudSiteId is not present in the catalog DB, then default to multicloud + logger.debug("{} is not present in cloud_site catalog DB, defaulting to Multicloud plugin adapter", + cloudSiteId); + } else { + CloudSite cloudSite = cloudSiteOp.get(); + MavenLikeVersioning aicV = new MavenLikeVersioning(); + aicV.setVersion(cloudSite.getCloudVersion()); + + String vnfMin = vnfResource.getAicVersionMin(); + String vnfMax = vnfResource.getAicVersionMax(); + + if ((vnfMin != null && !(aicV.isMoreRecentThan(vnfMin) || aicV.isTheSameVersion(vnfMin))) + || (vnfMax != null && aicV.isMoreRecentThan(vnfMax))) { + // ERROR + String error = + "VNF Resource type: " + vnfResource.getModelName() + ", ModelUuid=" + vnfResource.getModelUUID() + + " VersionMin=" + vnfMin + " VersionMax:" + vnfMax + " NOT supported on Cloud: " + + cloudSiteId + " with AIC_Version:" + cloudSite.getCloudVersion(); + logger.error("{} {} {} {} {}", MessageEnum.RA_CONFIG_EXC.toString(), error, "OpenStack", + ErrorCode.BusinessProcessError.getValue(), "Exception - setVersion"); + logger.debug(error); + throw new VnfException(error, MsoExceptionCategory.USERDATA); + } + } + // End Version check + + + VduInstance vduInstance = null; + CloudInfo cloudInfo = new CloudInfo(cloudSiteId, cloudOwner, tenantId, null); + + // Use the VduPlugin. + VduPlugin vduPlugin = getVduPlugin(cloudSiteId, cloudOwner); + + long subStartTime1 = System.currentTimeMillis(); + try { + vduInstance = vduPlugin.queryVdu(cloudInfo, vfModuleName); + } catch (VduException me) { + // Failed to query the VDU due to a plugin exception. + String error = "Create VF Module: Query " + vfModuleName + " in " + cloudOwner + "/" + cloudSiteId + "/" + + tenantId + ": " + me; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleName, + cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(), + "Exception - queryVdu", me); + logger.debug(error); + // Convert to a generic VnfException + me.addContext("CreateVFModule"); + throw new VnfException(me); + } + + // More precise handling/messaging if the Module already exists + if (vduInstance != null && !(vduInstance.getStatus().getState() == VduStateType.NOTFOUND)) { + VduStateType status = vduInstance.getStatus().getState(); + logger.debug("Found Existing VDU, status=" + status); + + if (status == VduStateType.INSTANTIATED) { + if (failIfExists != null && failIfExists) { + // fail - it exists + String error = "Create VF: Deployment " + vfModuleName + " already exists in " + cloudOwner + "/" + + cloudSiteId + "/" + tenantId; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), + vfModuleName, cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", + ErrorCode.DataError.getValue(), "VF Module " + vfModuleName + " already exists"); + logger.debug(error); + throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, + vduInstance.getVduInstanceId()); + } else { + // Found existing deployment and client has not requested "failIfExists". + // Populate the outputs from the existing deployment. + + vnfId.value = vduInstance.getVduInstanceId(); + outputs.value = copyStringOutputs(vduInstance.getOutputs()); + return; + } + } + // Check through various detailed error cases + else if (status == VduStateType.INSTANTIATING || status == VduStateType.DELETING + || status == VduStateType.UPDATING) { + // fail - it's in progress - return meaningful error + String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + + "; please wait for it to complete, or fix manually."; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, + cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(), + "VF Module " + vfModuleName + " already exists"); + logger.debug(error); + throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, + vduInstance.getVduInstanceId()); + } else if (status == VduStateType.FAILED) { + // fail - it exists and is in a FAILED state + String error = "Create VF: Deployment " + vfModuleName + " already exists and is in FAILED state in " + + cloudOwner + "/" + cloudSiteId + "/" + tenantId + "; requires manual intervention."; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, + cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(), + "VF Module " + vfModuleName + " already exists and is in FAILED state"); + logger.debug(error); + throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, + vduInstance.getVduInstanceId()); + } else if (status == VduStateType.UNKNOWN) { + // fail - it exists and is in a UNKNOWN state + String error = "Create VF: Deployment " + vfModuleName + " already exists and has status " + + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + + "; requires manual intervention."; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, + cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(), + "VF Module " + vfModuleName + " already exists and is in " + status.toString() + " state"); + logger.debug(error); + throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, + vduInstance.getVduInstanceId()); + } else { + // Unexpected, since all known status values have been tested for + String error = "Create VF: Deployment " + vfModuleName + " already exists with unexpected status " + + status.toString() + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + + "; requires manual intervention."; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_VNF_ALREADY_EXIST.toString(), vfModuleName, + cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu", ErrorCode.DataError.getValue(), + "VF Module " + vfModuleName + " already exists and is in an unknown state"); + logger.debug(error); + throw new VnfAlreadyExists(vfModuleName, cloudSiteId, cloudOwner, tenantId, + vduInstance.getVduInstanceId()); + } + } + + + // Collect outputs from Base Modules and Volume Modules + Map<String, Object> baseModuleOutputs = null; + Map<String, Object> volumeGroupOutputs = null; + + // If a Volume Group was provided, query its outputs for inclusion in Module input parameters + if (volumeGroupId != null) { + long subStartTime2 = System.currentTimeMillis(); + VduInstance volumeVdu = null; + try { + volumeVdu = vduPlugin.queryVdu(cloudInfo, volumeGroupId); + } catch (VduException me) { + // Failed to query the Volume Group VDU due to a plugin exception. + String error = "Create VF Module: Query Volume Group " + volumeGroupId + " in " + cloudOwner + "/" + + cloudSiteId + "/" + tenantId + ": " + me; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId, + cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu(volume)", ErrorCode.DataError.getValue(), + "Exception - queryVdu(volume)", me); + logger.debug(error); + // Convert to a generic VnfException + me.addContext("CreateVFModule(QueryVolume)"); + throw new VnfException(me); + } + + if (volumeVdu == null || volumeVdu.getStatus().getState() == VduStateType.NOTFOUND) { + String error = "Create VFModule: Attached Volume Group DOES NOT EXIST " + volumeGroupId + " in " + + cloudOwner + "/" + cloudSiteId + "/" + tenantId + " USER ERROR"; + logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), volumeGroupId, + cloudOwner, cloudSiteId, tenantId, error, "VDU", "queryVdu(volume)", + ErrorCode.BusinessProcessError.getValue(), + "Create VFModule: Attached Volume Group " + "DOES NOT EXIST"); + logger.debug(error); + throw new VnfException(error, MsoExceptionCategory.USERDATA); + } else { + logger.debug("Found nested volume group"); + volumeGroupOutputs = volumeVdu.getOutputs(); + this.sendMapToDebug(volumeGroupOutputs, "volumeGroupOutputs"); + } + } + + // If this is an Add-On Module, query the Base Module outputs + // Note: This will be performed whether or not the current request is for an + // Add-On Volume Group or Add-On VF Module + + if (vfModule.getIsBase()) { + logger.debug("This is a BASE Module request"); + vfRollback.setIsBase(true); + } else { + logger.debug("This is an Add-On Module request"); + + // Add-On Modules should always have a Base, but just treat as a warning if not provided. + // Add-on Volume requests may or may not specify a base. + if (!isVolumeRequest && baseVfModuleId == null) { + logger.debug("WARNING: Add-on Module request - no Base Module ID provided"); + } + + if (baseVfModuleId != null) { + long subStartTime2 = System.currentTimeMillis(); + VduInstance baseVdu = null; + try { + baseVdu = vduPlugin.queryVdu(cloudInfo, baseVfModuleId); + } catch (MsoException me) { + // Failed to query the Base VF Module due to a Vdu Plugin exception. + String error = "Create VF Module: Query Base " + baseVfModuleId + " in " + cloudOwner + "/" + + cloudSiteId + "/" + tenantId + ": " + me; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), baseVfModuleId, + cloudOwner, cloudSiteId, tenantId, "VDU", "queryVdu(Base)", ErrorCode.DataError.getValue(), + "Exception - queryVdu(Base)", me); + logger.debug(error); + // Convert to a generic VnfException + me.addContext("CreateVFModule(QueryBase)"); + throw new VnfException(me); + } + + if (baseVdu == null || baseVdu.getStatus().getState() == VduStateType.NOTFOUND) { + String error = "Create VFModule: Base Module DOES NOT EXIST " + baseVfModuleId + " in " + cloudOwner + + "/" + cloudSiteId + "/" + tenantId + " USER ERROR"; + logger.error("{} {} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), + baseVfModuleId, cloudOwner, cloudSiteId, tenantId, error, "VDU", "queryVdu(Base)", + ErrorCode.BusinessProcessError.getValue(), "Create VFModule: Base Module DOES NOT EXIST"); + logger.debug(error); + throw new VnfException(error, MsoExceptionCategory.USERDATA); + } else { + logger.debug("Found base module"); + baseModuleOutputs = baseVdu.getOutputs(); + this.sendMapToDebug(baseModuleOutputs, "baseModuleOutputs"); + } + } + } + + + // NOTE: For this section, heatTemplate is used for all template artifacts. + // In final implementation (post-POC), the template object would either be generic or there would + // be a separate DB Table/Object for different sub-orchestrators. + + // NOTE: The template is fixed for the VF Module. The environment is part of the customization. + + HeatTemplate heatTemplate = null; + HeatEnvironment heatEnvironment = null; + if (isVolumeRequest) { + heatTemplate = vfModule.getVolumeHeatTemplate(); + heatEnvironment = vfModuleCust.getVolumeHeatEnv(); + } else { + heatTemplate = vfModule.getModuleHeatTemplate(); + heatEnvironment = vfModuleCust.getHeatEnvironment(); + } + + if (heatTemplate == null) { + String error = "UpdateVF: No Heat Template ID defined in catalog database for " + vfModuleType + + ", modelCustomizationUuid=" + modelCustomizationUuid + ", vfModuleUuid=" + vfModule.getModelUUID() + + ", reqType=" + requestType; + logger.error("{} {} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Template ID", + vfModuleType, "VNF", ErrorCode.DataError.getValue(), error); + logger.debug(error); + throw new VnfException(error, MsoExceptionCategory.INTERNAL); + } else { + logger.debug("Got HEAT Template from DB: " + heatTemplate.getHeatTemplate()); + } + + if (heatEnvironment == null) { + String error = "Update VNF: undefined Heat Environment. VF=" + vfModuleType + ", modelCustomizationUuid=" + + modelCustomizationUuid + ", vfModuleUuid=" + vfModule.getModelUUID() + ", reqType=" + requestType; + logger.error("{} {} {} {} {}", MessageEnum.RA_VNF_UNKNOWN_PARAM.toString(), "Heat Environment ID", + "OpenStack", ErrorCode.DataError.getValue(), error); + throw new VnfException(error, MsoExceptionCategory.INTERNAL); + } else { + logger.debug("Got Heat Environment from DB: " + heatEnvironment.getEnvironment()); + } + + + // Create the combined set of parameters from the incoming request, base-module outputs, + // volume-module outputs. Also, convert all variables to their native object types. + + HashMap<String, Object> goldenInputs = new HashMap<String, Object>(); + List<String> extraInputs = new ArrayList<String>(); + + Boolean skipInputChecks = false; + + if (skipInputChecks) { + goldenInputs = new HashMap<String, Object>(); + for (String key : inputs.keySet()) { + goldenInputs.put(key, inputs.get(key)); + } + } else { + // Build maps for the parameters (including aliases) to simplify checks + HashMap<String, HeatTemplateParam> params = new HashMap<String, HeatTemplateParam>(); + + Set<HeatTemplateParam> paramSet = heatTemplate.getParameters(); + logger.debug("paramSet has " + paramSet.size() + " entries"); + + for (HeatTemplateParam htp : paramSet) { + params.put(htp.getParamName(), htp); + + // Include aliases. + String alias = htp.getParamAlias(); + if (alias != null && !alias.equals("") && !params.containsKey(alias)) { + params.put(alias, htp); + } + } + + // First, convert all inputs to their "template" type + for (String key : inputs.keySet()) { + if (params.containsKey(key)) { + Object value = convertInputValue(inputs.get(key), params.get(key)); + if (value != null) { + goldenInputs.put(key, value); + } else { + logger.debug("Failed to convert input " + key + "='" + inputs.get(key) + "' to " + + params.get(key).getParamType()); + } + } else { + extraInputs.add(key); + } + } + + if (!extraInputs.isEmpty()) { + // Add multicloud inputs + for (String key : MsoMulticloudUtils.MULTICLOUD_INPUTS) { + if (extraInputs.contains(key)) { + goldenInputs.put(key, inputs.get(key)); + extraInputs.remove(key); + if (extraInputs.isEmpty()) { + break; + } + } + } + logger.debug("Ignoring extra inputs: " + extraInputs); + } + + // Next add in Volume Group Outputs if there are any. Copy directly without conversions. + if (volumeGroupOutputs != null && !volumeGroupOutputs.isEmpty()) { + for (String key : volumeGroupOutputs.keySet()) { + if (params.containsKey(key) && !goldenInputs.containsKey(key)) { + goldenInputs.put(key, volumeGroupOutputs.get(key)); + } + } + } + + // Next add in Base Module Outputs if there are any. Copy directly without conversions. + if (baseModuleOutputs != null && !baseModuleOutputs.isEmpty()) { + for (String key : baseModuleOutputs.keySet()) { + if (params.containsKey(key) && !goldenInputs.containsKey(key)) { + goldenInputs.put(key, baseModuleOutputs.get(key)); + } + } + } + + // TODO: The model should support a mechanism to pre-assign default parameter values + // per "customization" (i.e. usage) of a given module. In HEAT, this is specified by + // an Environment file. There is not a general mechanism in the model to handle this. + // For the general case, any such parameter/values can be added dynamically to the + // inputs (only if not already specified). + + // Check that required parameters have been supplied from any of the sources + String missingParams = null; + boolean checkRequiredParameters = true; + try { + String propertyString = this.environment.getProperty(MsoVnfPluginAdapterImpl.CHECK_REQD_PARAMS); + if ("false".equalsIgnoreCase(propertyString) || "n".equalsIgnoreCase(propertyString)) { + checkRequiredParameters = false; + logger.debug("CheckRequiredParameters is FALSE. Will still check but then skip blocking..." + + MsoVnfPluginAdapterImpl.CHECK_REQD_PARAMS); + } + } catch (Exception e) { + // No problem - default is true + logger.debug("An exception occured trying to get property " + MsoVnfPluginAdapterImpl.CHECK_REQD_PARAMS, + e); + } + + // Do the actual parameter checking. + // Include looking at the ENV file as a valid definition of a parameter value. + // TODO: This handling of ENV applies only to Heat. A general mechanism to + // support pre-set parameter/values does not yet exist in the model. + // + StringBuilder sb = new StringBuilder(heatEnvironment.getEnvironment()); + MsoHeatEnvironmentEntry mhee = new MsoHeatEnvironmentEntry(sb); + for (HeatTemplateParam parm : heatTemplate.getParameters()) { + if (parm.isRequired() && (!goldenInputs.containsKey(parm.getParamName()))) { + if (mhee != null && mhee.containsParameter(parm.getParamName())) { + logger.debug("Required parameter " + parm.getParamName() + + " appears to be in environment - do not count as missing"); + } else { + logger.debug("adding to missing parameters list: " + parm.getParamName()); + if (missingParams == null) { + missingParams = parm.getParamName(); + } else { + missingParams += "," + parm.getParamName(); + } + } + } + } + + if (missingParams != null) { + if (checkRequiredParameters) { + // Problem - missing one or more required parameters + String error = "Create VFModule: Missing Required inputs: " + missingParams; + logger.error("{} {} {} {} {}", MessageEnum.RA_MISSING_PARAM.toString(), missingParams, "VDU", + ErrorCode.DataError.getValue(), "Create VFModule: Missing Required inputs"); + logger.debug(error); + throw new VnfException(error, MsoExceptionCategory.USERDATA); + } else { + logger.debug("found missing parameters [" + missingParams + + "] - but checkRequiredParameters is false - " + "will not block"); + } + } else { + logger.debug("No missing parameters found - ok to proceed"); + } + + } // NOTE: END PARAMETER CHECKING + + + // Here we go... ready to deploy the VF Module. + long instantiateVduStartTime = System.currentTimeMillis(); + if (backout == null) + backout = true; + + try { + // Construct the VDU Model structure to pass to the targeted VduPlugin + VduModelInfo vduModel = null; + if (!isVolumeRequest) { + vduModel = vduMapper.mapVfModuleCustomizationToVdu(vfModuleCust); + } else { + vduModel = vduMapper.mapVfModuleCustVolumeToVdu(vfModuleCust); + } + + // Invoke the VduPlugin to instantiate the VF Module + vduInstance = vduPlugin.instantiateVdu(cloudInfo, vfModuleName, goldenInputs, vduModel, backout); + + } catch (VduException me) { + // Failed to instantiate the VDU. + me.addContext("CreateVFModule"); + String error = "Create VF Module " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + + ": " + me; + logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner, + cloudSiteId, tenantId, "VDU", ErrorCode.DataError.getValue(), "MsoException - instantiateVdu", me); + logger.debug(error); + // Convert to a generic VnfException + throw new VnfException(me); + } catch (NullPointerException npe) { + String error = "Create VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + + ": " + npe; + logger.error("{} {} {} {} {} {} {} {}", MessageEnum.RA_CREATE_VNF_ERR.toString(), vfModuleType, cloudOwner, + cloudSiteId, tenantId, "VDU", ErrorCode.DataError.getValue(), + "NullPointerException - instantiateVdu", npe); + logger.debug(error); + logger.debug("NULL POINTER EXCEPTION at vduPlugin.instantiateVdu", npe); + throw new VnfException("NullPointerException during instantiateVdu"); + } catch (Exception e) { + String error = "Create VFModule " + vfModuleType + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + + ": " + e; + logger.debug("Unhandled exception at vduPlugin.instantiateVdu", e); + logger.debug(error); + throw new VnfException("Exception during instantiateVdu: " + e.getMessage()); + } + + + // Reach this point if create is successful. + // Populate remaining rollback info and response parameters. + vfRollback.setVnfCreated(true); + vfRollback.setVnfId(vduInstance.getVduInstanceId()); + vnfId.value = vduInstance.getVduInstanceId(); + outputs.value = copyStringOutputs(vduInstance.getOutputs()); + + rollback.value = vfRollback; + + logger.debug("VF Module " + vfModuleName + " successfully created"); + return; + } + + + public void deleteVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vfModuleId, + MsoRequest msoRequest, Holder<Map<String, String>> outputs) throws VnfException { + + logger.debug("Deleting VF Module " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId); + // Will capture execution time for metrics + long startTime = System.currentTimeMillis(); + + // Capture the output parameters on a delete, so need to query first + VduInstance vduInstance = null; + CloudInfo cloudInfo = new CloudInfo(cloudSiteId, cloudOwner, tenantId, null); + + // Use the VduPlugin. + VduPlugin vduPlugin = getVduPlugin(cloudSiteId, cloudOwner); + + try { + vduInstance = vduPlugin.queryVdu(cloudInfo, vfModuleId); + } catch (VduException e) { + // Failed to query the VDU due to a plugin exception. + // Convert to a generic VnfException + e.addContext("QueryVFModule"); + String error = "Query VfModule (VDU): " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + + tenantId + ": " + e; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_QUERY_VNF_ERR.toString(), vfModuleId, cloudOwner, + cloudSiteId, tenantId, "VDU", "QueryVFModule", ErrorCode.DataError.getValue(), + "Exception - queryVDU", e); + logger.debug(error); + throw new VnfException(e); + } + + // call method which handles the conversion from Map<String,Object> to Map<String,String> for our expected + // Object types + outputs.value = convertMapStringObjectToStringString(vduInstance.getOutputs()); + + // Use the VduPlugin to delete the VDU. + // The possible outcomes of deleteVdu are + // - a vnfInstance object with status of DELETED (success) + // - a vnfInstance object with status of NOTFOUND (VDU did not exist, treat as success) + // - a vnfInstance object with status of FAILED (error) + // Also, VduException could be thrown. + long subStartTime = System.currentTimeMillis(); + try { + // TODO: Get an appropriate timeout value - require access to the model + vduPlugin.deleteVdu(cloudInfo, vfModuleId, 5); + } catch (VduException me) { + me.addContext("DeleteVfModule"); + // Convert to a generic VnfException + String error = + "Delete VF: " + vfModuleId + " in " + cloudOwner + "/" + cloudSiteId + "/" + tenantId + ": " + me; + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.RA_DELETE_VNF_ERR.toString(), vfModuleId, cloudOwner, + cloudSiteId, tenantId, "VDU", "DeleteVdu", ErrorCode.DataError.getValue(), + "Exception - DeleteVdu: " + me.getMessage()); + logger.debug(error); + throw new VnfException(me); + } + + // On success, nothing is returned. + return; + } + + // Update VF Module not yet implemented for generic VDU plug-in model. + + public void updateVfModule(String cloudSiteId, String cloudOwner, String tenantId, String vnfType, + String vnfVersion, String vnfName, String requestType, String volumeGroupHeatStackId, + String baseVfHeatStackId, String vfModuleStackId, String modelCustomizationUuid, Map<String, Object> inputs, + MsoRequest msoRequest, Holder<Map<String, String>> outputs, Holder<VnfRollback> rollback) + throws VnfException { + // This operation is not currently supported for VduPlugin-orchestrated VF Modules. + logger.debug("Update VF Module command attempted but not supported"); + throw new VnfException("UpdateVfModule: Unsupported command", MsoExceptionCategory.USERDATA); + } + + /* + * Dynamic selection of a VduPlugin version. For initial tests, base on the "orchestrator" defined for the target + * cloud. Should really be looking at the VNF Model (ochestration_mode) but we don't currently have access to that + * in Query and Delete cases. + */ + private VduPlugin getVduPlugin(String cloudSiteId, String cloudOwner) { + Optional<CloudSite> cloudSiteOp = cloudConfig.getCloudSite(cloudSiteId); + if (cloudSiteOp.isPresent()) { + CloudSite cloudSite = cloudSiteOp.get(); + String orchestrator = cloudSite.getOrchestrator(); + + if (orchestrator.equalsIgnoreCase("HEAT")) { + return heatUtils; + } else if (orchestrator.equalsIgnoreCase("MULTICLOUD")) { + return multicloudUtils; + } else { + // Default if cloudSite record exists - return HEAT plugin - will fail later + return heatUtils; + } + } + // Default if no cloudSite record exists - return multicloud plugin + return multicloudUtils; + } +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/VnfAdapterUtils.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/VnfAdapterUtils.java new file mode 100644 index 0000000000..85f1a977da --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/VnfAdapterUtils.java @@ -0,0 +1,74 @@ +/*- + * ============LICENSE_START======================================================= + * OPENECOMP - SO + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Modifications Copyright (c) 2019 Samsung + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.so.adapters.vnf; + +import java.util.Optional; +import org.onap.so.cloud.CloudConfig; +import org.onap.so.db.catalog.beans.CloudSite; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class VnfAdapterUtils { + private static Logger logger = LoggerFactory.getLogger(VnfAdapterUtils.class); + + private static final String HEAT_MODE = "HEAT"; + private static final String MULTICLOUD_MODE = "MULTICLOUD"; + + @Autowired + private CloudConfig cloudConfig; + + /* + * Choose which implementation of VNF Adapter to use, based on the orchestration mode. Currently, the two supported + * orchestrators are HEAT and CLOUDIFY. + */ + public boolean isMulticloudMode(String mode, String cloudSiteId) { + // First, determine the orchestration mode to use. + // If was explicitly provided as a parameter, use that. Else if specified for + // the + // cloudsite, use that. Otherwise, the default is the (original) HEAT-based + // impl. + + logger.debug("Entered GetVnfAdapterImpl: mode=" + mode + ", cloudSite=" + cloudSiteId); + + if (mode == null) { + // Didn't get an explicit mode type requested. + // Use the CloudSite to determine which Impl to use, based on whether the target + // cloutSite + // has a CloudifyManager assigned to it + Optional<CloudSite> cloudSite = cloudConfig.getCloudSite(cloudSiteId); + if (cloudSite.isPresent()) { + logger.debug("Got CloudSite: " + cloudSite.toString()); + if (MULTICLOUD_MODE.equalsIgnoreCase(cloudSite.get().getOrchestrator())) { + logger.debug("GetVnfAdapterImpl: mode=" + MULTICLOUD_MODE); + return true; + } + } + } + + return false; + } + +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeImpl.java index 8ee87b50c3..eb0529c85f 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeImpl.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeImpl.java @@ -48,6 +48,7 @@ import javax.ws.rs.NotFoundException; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.UriBuilder; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.validator.routines.InetAddressValidator; import org.onap.aai.domain.yang.Flavor; import org.onap.aai.domain.yang.Image; @@ -342,8 +343,10 @@ public class HeatBridgeImpl implements HeatBridgeApi { } transaction.connect(vserverUri, foundVnfcURI.get()); } else { - throw new HeatBridgeException( - "VF Module contains no relationships to VNFCS, cannot build edge to VServer"); + /* + * throw new HeatBridgeException( + * "VF Module contains no relationships to VNFCS, cannot build edge to VServer"); + */ } } @@ -374,44 +377,47 @@ public class HeatBridgeImpl implements HeatBridgeApi { boolean isL2Multicast = false; Port port = osClient.getPortById(portId); Network network = osClient.getNetworkById(port.getNetworkId()); - LInterface lIf = new LInterface(); - lIf.setInterfaceId(port.getId()); - lIf.setInterfaceName(port.getName()); - lIf.setMacaddr(port.getMacAddress()); - lIf.setNetworkName(network.getName()); - lIf.setIsPortMirrored(false); - lIf.setIsIpUnnumbered(false); - lIf.setInMaint(false); - - if (port.getProfile() != null && port.getProfile().get("trusted") != null) { - String trusted = port.getProfile().get("trusted").toString(); - if (Boolean.parseBoolean(trusted)) { - isL2Multicast = true; + if (!StringUtils.isEmpty(port.getDeviceId())) { + LInterface lIf = new LInterface(); + lIf.setInterfaceId(port.getId()); + lIf.setInterfaceName(port.getName()); + lIf.setMacaddr(port.getMacAddress()); + lIf.setNetworkName(network.getName()); + lIf.setIsPortMirrored(false); + lIf.setIsIpUnnumbered(false); + lIf.setInMaint(false); + + if (port.getProfile() != null && port.getProfile().get("trusted") != null) { + String trusted = port.getProfile().get("trusted").toString(); + if (Boolean.parseBoolean(trusted)) { + isL2Multicast = true; + } } - } - lIf.setL2Multicasting(isL2Multicast); - lIf.setInterfaceType(getInterfaceType(nodeType, port.getvNicType())); - lIf.setRelationshipList(new RelationshipList()); + lIf.setL2Multicasting(isL2Multicast); + lIf.setInterfaceType(getInterfaceType(nodeType, port.getvNicType())); + lIf.setRelationshipList(new RelationshipList()); - if (oobMgtNetIds != null && oobMgtNetIds.contains(port.getNetworkId())) { - lIf.setInterfaceRole(OOB_MGT_NETWORK_IDENTIFIER); - } else { - lIf.setInterfaceRole(port.getvNicType()); - } + if (oobMgtNetIds != null && oobMgtNetIds.contains(port.getNetworkId())) { + lIf.setInterfaceRole(OOB_MGT_NETWORK_IDENTIFIER); + } else { + lIf.setInterfaceRole(port.getvNicType()); + } - // Update l-interface to the vserver - transaction.createIfNotExists( - AAIUriFactory.createResourceUri( - AAIFluentTypeBuilder.cloudInfrastructure().cloudRegion(cloudOwner, cloudRegionId) - .tenant(tenantId).vserver(port.getDeviceId()).lInterface(lIf.getInterfaceName())), - Optional.of(lIf)); + // Update l-interface to the vserver + transaction + .createIfNotExists( + AAIUriFactory.createResourceUri(AAIFluentTypeBuilder.cloudInfrastructure() + .cloudRegion(cloudOwner, cloudRegionId).tenant(tenantId) + .vserver(port.getDeviceId()).lInterface(lIf.getInterfaceName())), + Optional.of(lIf)); - updateLInterfaceIps(port, lIf); + updateLInterfaceIps(port, lIf); - if (cloudOwner.equals(env.getProperty("mso.cloudOwner.included", ""))) { - Server server = getOpenstackServerById(port.getDeviceId()); - createVlanAndSriovVF(port, lIf, server.getHypervisorHostname()); - updateSriovPfToSriovVF(port, lIf); + if (cloudOwner.equals(env.getProperty("mso.cloudOwner.included", ""))) { + Server server = getOpenstackServerById(port.getDeviceId()); + createVlanAndSriovVF(port, lIf, server.getHypervisorHostname()); + updateSriovPfToSriovVF(port, lIf); + } } } } diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/helpers/AaiHelper.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/helpers/AaiHelper.java index fef7b5e6cb..a8fa6535c3 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/helpers/AaiHelper.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/helpers/AaiHelper.java @@ -104,9 +104,10 @@ public class AaiHelper { } // vserver to vnfc relationship - if (!StringUtils.isEmpty(server.getName())) { - relationships.add(createVnfRelationshiptoVserver(server.getName(), genericVnfId, vfModuleId)); - } + /* + * if (!StringUtils.isEmpty(server.getName())) { + * relationships.add(createVnfRelationshiptoVserver(server.getName(), genericVnfId, vfModuleId)); } + */ // vserver to vf-module relationship Relationship vfModuleRelationship = buildRelationship(AAIUriFactory .createResourceUri(AAIFluentTypeBuilder.network().genericVnf(genericVnfId).vfModule(vfModuleId))); diff --git a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/PollServiceTest.java b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/PollServiceTest.java index 8d5ab8234e..60826705fd 100644 --- a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/PollServiceTest.java +++ b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/PollServiceTest.java @@ -14,6 +14,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import org.mockito.junit.MockitoJUnitRunner; import org.onap.so.adapters.tasks.orchestration.PollService; +import org.onap.so.adapters.vnf.VnfAdapterUtils; import org.onap.so.logging.tasks.AuditMDCSetup; import org.onap.so.openstack.exceptions.MsoException; import org.onap.so.openstack.utils.MsoHeatUtils; @@ -34,6 +35,9 @@ public class PollServiceTest { private MsoHeatUtils msoHeatUtils; @Mock + private VnfAdapterUtils vnfAdapterUtils; + + @Mock private AuditMDCSetup mdcSetup; @InjectMocks @@ -44,6 +48,7 @@ public class PollServiceTest { String xmlString = new String(Files.readAllBytes(Paths.get(RESOURCE_PATH + "/vnfAdapterTaskRequestCreate.xml"))); + Mockito.when(vnfAdapterUtils.isMulticloudMode(Mockito.any(), Mockito.any())).thenReturn(false); Mockito.when(mockExternalTask.getVariable("openstackAdapterTaskRequest")).thenReturn(xmlString); Mockito.when(mockExternalTask.getVariable("PollRollbackStatus")).thenReturn(false); Mockito.when(mockExternalTask.getVariable("stackId")).thenReturn("stackId/stack123"); diff --git a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/RollbackServiceTest.java b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/RollbackServiceTest.java index 1f68106f78..3a82dd5b72 100644 --- a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/RollbackServiceTest.java +++ b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/RollbackServiceTest.java @@ -18,6 +18,7 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.onap.so.adapters.tasks.orchestration.RollbackService; import org.onap.so.adapters.vnf.MsoVnfAdapterImpl; +import org.onap.so.adapters.vnf.VnfAdapterUtils; import org.onap.so.adapters.vnf.exceptions.VnfException; import org.onap.so.entity.MsoRequest; import org.onap.so.logging.tasks.AuditMDCSetup; @@ -41,6 +42,9 @@ public class RollbackServiceTest { private MsoVnfAdapterImpl vnfAdapterImpl; @Mock + private VnfAdapterUtils vnfAdapterUtils; + + @Mock private MsoHeatUtils msoHeatUtils; @Mock @@ -65,6 +69,8 @@ public class RollbackServiceTest { Stack stack = new Stack(); stack.setId("heatId"); + + Mockito.when(vnfAdapterUtils.isMulticloudMode(Mockito.any(), Mockito.any())).thenReturn(false); Mockito.when(mockExternalTask.getVariable("openstackAdapterTaskRequest")).thenReturn(payload); Mockito.doNothing().when(vnfAdapterImpl).deleteVfModule(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); diff --git a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/StackServiceTest.java b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/StackServiceTest.java index cc2dfa213e..afe7e17855 100644 --- a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/StackServiceTest.java +++ b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/tasks/orchestration/StackServiceTest.java @@ -7,7 +7,6 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import javax.xml.ws.Holder; import org.camunda.bpm.client.task.ExternalTask; import org.camunda.bpm.client.task.ExternalTaskService; import org.junit.Test; @@ -16,15 +15,13 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import org.onap.so.adapters.tasks.orchestration.StackService; import org.onap.so.adapters.vnf.MsoVnfAdapterImpl; -import org.onap.so.entity.MsoRequest; +import org.onap.so.adapters.vnf.VnfAdapterUtils; +import org.onap.so.adapters.vnf.exceptions.VnfException; import org.onap.so.logging.tasks.AuditMDCSetup; -import org.onap.so.openstack.beans.VnfRollback; import org.onap.so.openstack.exceptions.MsoException; import org.onap.so.openstack.utils.MsoHeatUtils; import com.woorea.openstack.heat.model.Stack; -import org.onap.so.adapters.vnf.exceptions.VnfException; @RunWith(MockitoJUnitRunner.class) public class StackServiceTest { @@ -41,6 +38,9 @@ public class StackServiceTest { private MsoVnfAdapterImpl vnfAdapterImpl; @Mock + private VnfAdapterUtils vnfAdapterUtils; + + @Mock private MsoHeatUtils msoHeatUtils; @Mock @@ -64,6 +64,7 @@ public class StackServiceTest { Stack stack = new Stack(); stack.setId("heatId"); + Mockito.when(vnfAdapterUtils.isMulticloudMode(Mockito.any(), Mockito.any())).thenReturn(false); Mockito.when(mockExternalTask.getVariable("openstackAdapterTaskRequest")).thenReturn(payload); Mockito.doNothing().when(vnfAdapterImpl).createVfModule(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), diff --git a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/heatbridge/HeatBridgeImplTest.java b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/heatbridge/HeatBridgeImplTest.java index 1026284470..a18904f8f4 100644 --- a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/heatbridge/HeatBridgeImplTest.java +++ b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/heatbridge/HeatBridgeImplTest.java @@ -371,15 +371,15 @@ public class HeatBridgeImplTest { org.onap.aai.domain.yang.RelationshipList relList = aaiHelper.getVserverRelationshipList(CLOUD_OWNER, REGION_ID, "test-genericVnf-id", "test-vfModule-id", server1); - assertEquals(3, relList.getRelationship().size()); + assertEquals(2, relList.getRelationship().size()); org.onap.aai.domain.yang.RelationshipList relList2 = aaiHelper.getVserverRelationshipList(CLOUD_OWNER, REGION_ID, "test-genericVnf-id", "test-vfModule-id", server2); - assertEquals(2, relList2.getRelationship().size()); + assertEquals(1, relList2.getRelationship().size()); org.onap.aai.domain.yang.RelationshipList relList3 = aaiHelper.getVserverRelationshipList(CLOUD_OWNER, REGION_ID, "test-genericVnf-id", "test-vfModule-id", server3); - assertEquals(2, relList3.getRelationship().size()); + assertEquals(1, relList3.getRelationship().size()); } @@ -752,6 +752,45 @@ public class HeatBridgeImplTest { } @Test + public void testBuildAddVserverLInterfacesToAaiAction_DeviceIdNull() + throws HeatBridgeException, JsonParseException, JsonMappingException, IOException { + // Arrange + List<Resource> stackResources = (List<Resource>) extractTestStackResources(); + Port port = mock(Port.class); + when(port.getId()).thenReturn("test-port-id"); + when(port.getName()).thenReturn("test-port-name"); + when(port.getvNicType()).thenReturn(HeatBridgeConstants.OS_SRIOV_PORT_TYPE); + when(port.getMacAddress()).thenReturn("78:4f:43:68:e2:78"); + when(port.getNetworkId()).thenReturn("890a203a-23gg-56jh-df67-731656a8f13a"); + when(port.getDeviceId()).thenReturn(null); + String pfPciId = "0000:08:00.0"; + + Network network = mock(Network.class); + when(network.getId()).thenReturn("test-network-id"); + when(network.getNetworkType()).thenReturn(NetworkType.VLAN); + when(network.getProviderSegID()).thenReturn("2345"); + + when(osClient.getPortById("212a203a-9764-4f42-84ea-731536a8f13a")).thenReturn(port); + when(osClient.getPortById("387e3904-8948-43d1-8635-b6c2042b54da")).thenReturn(port); + when(osClient.getPortById("70a09dfd-f1c5-4bc8-bd8f-dc539b8d662a")).thenReturn(port); + when(osClient.getPortById("12f88b4d-c8a4-4fbd-bcb4-7e36af02430b")).thenReturn(port); + when(osClient.getPortById("c54b9f45-b413-4937-bbe4-3c8a5689cfc9")).thenReturn(port); + when(osClient.getNetworkById(anyString())).thenReturn(network); + + PInterface pIf = mock(PInterface.class); + when(pIf.getInterfaceName()).thenReturn("test-port-id"); + doNothing().when(heatbridge).updateSriovPfToSriovVF(any(), any()); + + // Act + heatbridge.buildAddVserverLInterfacesToAaiAction(stackResources, Arrays.asList("1", "2"), "CloudOwner"); + + // Assert + verify(transaction, times(0)).createIfNotExists(any(AAIResourceUri.class), any(Optional.class)); + verify(osClient, times(5)).getPortById(anyString()); + verify(osClient, times(5)).getNetworkById(anyString()); + } + + @Test public void testExtractOpenstackImagesFromServers() throws HeatBridgeException { // Arrange List<Server> serverList = new ArrayList<>(); |