diff options
Diffstat (limited to 'adapters/mso-openstack-adapters/src')
30 files changed, 2956 insertions, 241 deletions
diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/audit/HeatStackAudit.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/audit/HeatStackAudit.java index 19e3ab71f5..72dee07379 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/audit/HeatStackAudit.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/audit/HeatStackAudit.java @@ -34,6 +34,7 @@ import org.onap.aai.domain.yang.LInterface; import org.onap.aai.domain.yang.LInterfaces; import org.onap.aai.domain.yang.Vserver; import org.onap.so.openstack.utils.MsoHeatUtils; +import org.onap.so.openstack.utils.MsoNeutronUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -43,6 +44,7 @@ import com.woorea.openstack.heat.model.Link; import com.woorea.openstack.heat.model.Resource; import com.woorea.openstack.heat.model.Resources; import com.woorea.openstack.heat.model.Stack; +import com.woorea.openstack.quantum.model.Port; @Component public class HeatStackAudit { @@ -55,6 +57,9 @@ public class HeatStackAudit { protected MsoHeatUtils heat; @Autowired + protected MsoNeutronUtils neutron; + + @Autowired protected AuditVServer auditVservers; public boolean auditHeatStackCreate(String cloudRegion, String cloudOwner, String tenantId, String heatStackName) { @@ -82,8 +87,9 @@ public class HeatStackAudit { if(novaResources.isEmpty()) return true; else{ + List<Optional<Port>> neutronPortDetails = retrieveNeutronPortDetails(resources,cloudRegion,tenantId); List<Resource> resourceGroups = extractResourceGroups(resources); - Set<Vserver> vserversToAudit = createVserverSet(resources, novaResources); + Set<Vserver> vserversToAudit = createVserverSet(resources, novaResources,neutronPortDetails); Set<Vserver> vserversWithSubInterfaces = processSubInterfaces(cloudRegion, tenantId, resourceGroups, vserversToAudit); if(isCreateAudit){ @@ -186,18 +192,16 @@ public class HeatStackAudit { lInterface.getInterfaceId(),subinterfaceStack.getId()); } - protected Set<Vserver> createVserverSet(Resources resources, List<Resource> novaResources) { + protected Set<Vserver> createVserverSet(Resources resources, List<Resource> novaResources, List<Optional<Port>> neutronPortDetails) { Set<Vserver> vserversToAudit = new HashSet<>(); for (Resource novaResource : novaResources) { Vserver auditVserver = new Vserver(); auditVserver.setLInterfaces(new LInterfaces()); auditVserver.setVserverId(novaResource.getPhysicalResourceId()); - Stream<Resource> filteredNeutronNetworks = resources.getList().stream() - .filter(resource -> resource.getRequiredBy().contains(novaResource.getLogicalResourceId())) - .filter(resource -> "OS::Neutron::Port".equals(resource.getType())); - filteredNeutronNetworks.forEach(network -> { + Stream<Port> filteredNeutronPorts = filterNeutronPorts(novaResource, neutronPortDetails); + filteredNeutronPorts.forEach(port -> { LInterface lInterface = new LInterface(); - lInterface.setInterfaceId(network.getPhysicalResourceId()); + lInterface.setInterfaceId(port.getId()); auditVserver.getLInterfaces().getLInterface().add(lInterface); }); vserversToAudit.add(auditVserver); @@ -205,6 +209,31 @@ public class HeatStackAudit { return vserversToAudit; } + /** + * @param novaResource Single openstack resource that is of type Nova + * @param neutronPorts List of Neutron ports created within the stack + * @return Filtered list of neutron ports taht relate to the nova server in openstack + */ + protected Stream<Port> filterNeutronPorts(Resource novaResource, List<Optional<Port>> neutronPorts) { + List<Port> filteredNeutronPorts = neutronPorts.stream().filter(Optional::isPresent).map(Optional::get) + .collect(Collectors.toList()); + return filteredNeutronPorts.stream() + .filter(port -> port.getDeviceId().equalsIgnoreCase(novaResource.getPhysicalResourceId())); + } + + /** + * @param resources Resource stream created by the stack in openstack + * @param cloudSiteId Unique site id to identify which openstack we talk to + * @param tenantId The tenant within the cloud we are talking to where resouces exist + * @return List of optional neutron ports found within the cloud site and tenant + */ + protected List<Optional<Port>> retrieveNeutronPortDetails(Resources resources,String cloudSiteId,String tenantId){ + return resources.getList().stream() + .filter(resource -> "OS::Neutron::Port".equals(resource.getType())) + .map(resource -> neutron.getNeutronPort(resource.getPhysicalResourceId(),cloudSiteId,tenantId)).collect(Collectors.toList()); + + } + protected Optional<String> extractResourcePathFromHref(String href) { try { Optional<String> stackPath = extractStackPathFromHref(href); @@ -234,3 +263,4 @@ public class HeatStackAudit { } } + diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/network/MsoNetworkAdapterImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/network/MsoNetworkAdapterImpl.java index 8053cd6f9e..2e8c7990db 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/network/MsoNetworkAdapterImpl.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/network/MsoNetworkAdapterImpl.java @@ -537,6 +537,7 @@ public class MsoNetworkAdapterImpl implements MsoNetworkAdapter { "CloudOwner", tenantId, networkName, + null, template, stackParams, true, diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vdu/mapper/VfModuleCustomizationToVduMapper.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vdu/mapper/VfModuleCustomizationToVduMapper.java index f6442b6252..b418368170 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vdu/mapper/VfModuleCustomizationToVduMapper.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vdu/mapper/VfModuleCustomizationToVduMapper.java @@ -33,25 +33,29 @@ import org.springframework.stereotype.Component; @Component public class VfModuleCustomizationToVduMapper { - + public VduModelInfo mapVfModuleCustomizationToVdu(VfModuleCustomization vfModuleCustom) - { + { VduModelInfo vduModel = new VduModelInfo(); vduModel.setModelCustomizationUUID(vfModuleCustom.getModelCustomizationUUID()); - + vduModel.setModelUUID(vfModuleCustom.getVfModule().getModelUUID()); + vduModel.setModelInvariantUUID(vfModuleCustom.getVfModule().getModelInvariantUUID()); + // Map the cloud templates, attached files, and environment file mapCloudTemplates(vfModuleCustom.getVfModule().getModuleHeatTemplate(), vduModel); mapCloudFiles(vfModuleCustom,vduModel); mapEnvironment(vfModuleCustom.getHeatEnvironment(), vduModel); - + return vduModel; } - + public VduModelInfo mapVfModuleCustVolumeToVdu(VfModuleCustomization vfModuleCustom) - { + { VduModelInfo vduModel = new VduModelInfo(); vduModel.setModelCustomizationUUID(vfModuleCustom.getModelCustomizationUUID()); - + vduModel.setModelUUID(vfModuleCustom.getVfModule().getModelUUID()); + vduModel.setModelInvariantUUID(vfModuleCustom.getVfModule().getModelInvariantUUID()); + // Map the cloud templates, attached files, and environment file mapCloudTemplates(vfModuleCustom.getVfModule().getVolumeHeatTemplate(), vduModel); mapCloudFiles(vfModuleCustom,vduModel); @@ -62,20 +66,20 @@ public class VfModuleCustomizationToVduMapper { private void mapCloudTemplates(HeatTemplate heatTemplate, VduModelInfo vduModel) { // TODO: These catalog objects will be refactored to be non-Heat-specific - + List<VduArtifact> vduArtifacts = vduModel.getArtifacts(); - + // Main template. Also set the VDU timeout based on the main template. vduArtifacts.add(mapHeatTemplateToVduArtifact(heatTemplate, ArtifactType.MAIN_TEMPLATE)); vduModel.setTimeoutMinutes(heatTemplate.getTimeoutMinutes()); - + // Nested templates List<HeatTemplate> childTemplates = heatTemplate.getChildTemplates(); if (childTemplates != null) { for(HeatTemplate childTemplate : childTemplates){ vduArtifacts.add(mapHeatTemplateToVduArtifact(childTemplate, ArtifactType.NESTED_TEMPLATE)); } - } + } } private VduArtifact mapHeatTemplateToVduArtifact(HeatTemplate heatTemplate, ArtifactType artifactType) { @@ -85,12 +89,12 @@ public class VfModuleCustomizationToVduMapper { vduArtifact.setType(artifactType); return vduArtifact; } - + private void mapCloudFiles(VfModuleCustomization vfModuleCustom, VduModelInfo vduModel) { // TODO: These catalog objects will be refactored to be non-Heat-specific - + List<VduArtifact> vduArtifacts = vduModel.getArtifacts(); - + // Attached Files List<HeatFiles> heatFiles = vfModuleCustom.getVfModule().getHeatFiles(); if (heatFiles != null) { @@ -115,7 +119,7 @@ public class VfModuleCustomizationToVduMapper { vduArtifacts.add(mapEnvironmentFileToVduArtifact(heatEnvironment)); } } - + private VduArtifact mapEnvironmentFileToVduArtifact(HeatEnvironment heatEnv) { VduArtifact vduArtifact = new VduArtifact(); vduArtifact.setName(heatEnv.getName()); diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfAdapterImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfAdapterImpl.java index 0563d6c242..949027f7c1 100644 --- a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfAdapterImpl.java +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/adapters/vnf/MsoVnfAdapterImpl.java @@ -37,10 +37,22 @@ import java.util.concurrent.TimeUnit; import javax.jws.WebService; import javax.xml.ws.Holder; +import org.apache.commons.collections.CollectionUtils; +import org.onap.so.adapters.valet.GenericValetResponse; +import org.onap.so.adapters.valet.ValetClient; +import org.onap.so.adapters.valet.beans.HeatRequest; +import org.onap.so.adapters.valet.beans.ValetConfirmResponse; +import org.onap.so.adapters.valet.beans.ValetCreateResponse; +import org.onap.so.adapters.valet.beans.ValetDeleteResponse; +import org.onap.so.adapters.valet.beans.ValetRollbackResponse; +import org.onap.so.adapters.valet.beans.ValetStatus; +import org.onap.so.adapters.valet.beans.ValetUpdateResponse; import org.onap.so.adapters.vnf.exceptions.VnfAlreadyExists; import org.onap.so.adapters.vnf.exceptions.VnfException; import org.onap.so.adapters.vnf.exceptions.VnfNotFound; +import org.onap.so.client.aai.AAIResourcesClient; import org.onap.so.cloud.CloudConfig; +import org.onap.so.db.catalog.beans.CloudIdentity; import org.onap.so.db.catalog.beans.CloudSite; import org.onap.so.db.catalog.beans.HeatEnvironment; import org.onap.so.db.catalog.beans.HeatFiles; @@ -54,27 +66,26 @@ import org.onap.so.db.catalog.data.repository.VnfResourceRepository; import org.onap.so.db.catalog.utils.MavenLikeVersioning; import org.onap.so.entity.MsoRequest; import org.onap.so.logger.ErrorCode; +import org.onap.so.heatbridge.HeatBridgeApi; +import org.onap.so.heatbridge.HeatBridgeImpl; +import org.onap.so.heatbridge.openstack.api.OpenstackClient; import org.onap.so.logger.MessageEnum; import org.onap.so.openstack.beans.HeatStatus; import org.onap.so.openstack.beans.StackInfo; 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.exceptions.MsoHeatNotFoundException; import org.onap.so.openstack.utils.MsoHeatEnvironmentEntry; import org.onap.so.openstack.utils.MsoHeatUtils; import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate; -import org.onap.so.adapters.valet.ValetClient; -import org.onap.so.adapters.valet.beans.HeatRequest; -import org.onap.so.adapters.valet.beans.ValetConfirmResponse; -import org.onap.so.adapters.valet.beans.ValetCreateResponse; -import org.onap.so.adapters.valet.beans.ValetDeleteResponse; -import org.onap.so.adapters.valet.beans.ValetRollbackResponse; -import org.onap.so.adapters.valet.beans.ValetStatus; -import org.onap.so.adapters.valet.beans.ValetUpdateResponse; -import org.onap.so.adapters.valet.GenericValetResponse; +import org.openstack4j.model.compute.Flavor; +import org.openstack4j.model.compute.Image; +import org.openstack4j.model.compute.Server; +import org.openstack4j.model.heat.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -509,6 +520,67 @@ public class MsoVnfAdapterImpl implements MsoVnfAdapter { } } + private void heatbridge(StackInfo heatStack, String cloudSiteId, String tenantId, String genericVnfName, + String vfModuleId) { + try { + CloudSite cloudSite = cloudConfig.getCloudSite(cloudSiteId).orElseThrow( + () -> new MsoCloudSiteNotFound(cloudSiteId)); + CloudIdentity cloudIdentity = cloudSite.getIdentityService(); + String heatStackId = heatStack.getCanonicalName().split("/")[1]; + + String cloudOwner = "CloudOwner";//cloud owner needs to come from bpmn-adapter + List<String> oobMgtNetNames = new ArrayList<>(); + + HeatBridgeApi heatBridgeClient = new HeatBridgeImpl(new AAIResourcesClient(), cloudIdentity, + cloudOwner, cloudSiteId, tenantId); + + OpenstackClient openstackClient = heatBridgeClient.authenticate(); + List<Resource> stackResources = heatBridgeClient.queryNestedHeatStackResources(heatStackId); + + List<Server> osServers = heatBridgeClient.getAllOpenstackServers(stackResources); + + List<Image> osImages = heatBridgeClient.extractOpenstackImagesFromServers(osServers); + + List<Flavor> osFlavors = heatBridgeClient.extractOpenstackFlavorsFromServers(osServers); + + logger.debug("Successfully queried heat stack{} for resources.", heatStackId); + //os images + if (osImages != null && !osImages.isEmpty()) { + heatBridgeClient.buildAddImagesToAaiAction(osImages); + logger.debug("Successfully built AAI actions to add images."); + } else { + logger.debug("No images to update to AAI."); + } + //flavors + if (osFlavors != null && !osFlavors.isEmpty()) { + heatBridgeClient.buildAddFlavorsToAaiAction(osFlavors); + logger.debug("Successfully built AAI actions to add flavors."); + } else { + logger.debug("No flavors to update to AAI."); + } + + //compute resources + heatBridgeClient.buildAddVserversToAaiAction(genericVnfName, vfModuleId, osServers); + logger.debug("Successfully queried compute resources and built AAI vserver actions."); + + //neutron resources + List<String> oobMgtNetIds = new ArrayList<>(); + + //if no network-id list is provided, however network-name list is + if (!CollectionUtils.isEmpty(oobMgtNetNames)) { + oobMgtNetIds = heatBridgeClient.extractNetworkIds(oobMgtNetNames); + } + heatBridgeClient.buildAddVserverLInterfacesToAaiAction(stackResources, oobMgtNetIds); + logger.debug( + "Successfully queried neutron resources and built AAI actions to add l-interfaces to vservers."); + + //Update AAI + heatBridgeClient.submitToAai(); + } catch (Exception ex) { + logger.debug("Heatbrige failed for stackId: " + heatStack.getCanonicalName(), ex); + } + } + private String convertNode(final JsonNode node) { try { final Object obj = JSON_MAPPER.treeToValue(node, Object.class); @@ -1206,6 +1278,7 @@ public class MsoVnfAdapterImpl implements MsoVnfAdapter { cloudOwner, tenantId, vfModuleName, + null, template, goldenInputs, true, @@ -1270,7 +1343,9 @@ public class MsoVnfAdapterImpl implements MsoVnfAdapter { logger.error("Exception encountered while sending Confirm to Valet ", e); } } - logger.debug("VF Module {} successfully created", vfModuleName); + logger.debug ("VF Module {} successfully created", vfModuleName); + //call heatbridge + heatbridge(heatStack, cloudSiteId, tenantId, genericVnfName, vfModuleId); return; } catch (Exception e) { logger.debug("unhandled exception in create VF",e); diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeApi.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeApi.java new file mode 100644 index 0000000000..6b06761474 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeApi.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ +package org.onap.so.heatbridge; + +import java.util.List; +import org.onap.so.heatbridge.openstack.api.OpenstackClient; +import org.openstack4j.model.compute.Flavor; +import org.openstack4j.model.compute.Image; +import org.openstack4j.model.compute.Server; +import org.openstack4j.model.heat.Resource; + +/** + * Defines the contract to extract Heat Stack Resources from Openstack and inventory it to AAI. + * This API is used only to "create" objects in AAI. + */ +public interface HeatBridgeApi { + + /** + * Authenticate with Openstack Keystone. The auth information is read from SO cloud configuration file. + * + * @return Openstack client object with keystone token + * @throws HeatBridgeException upon failure to authenticate with keystone + */ + OpenstackClient authenticate() throws HeatBridgeException; + + /** + * Query all the stack based resources from Openstack Heat service + * + * @param heatStackId Heat stack UUID + * @return A list of stack based resources + */ + List<Resource> queryNestedHeatStackResources(String heatStackId); + + /** + * Get a filtered list of resource IDs by resource type + * + * @param stackResources A list of stack based resources + * @param resourceType Resource type to filter by + * @return A list of stack resources matching the specified resource-type + */ + List<String> extractStackResourceIdsByResourceType(List<Resource> stackResources, String resourceType); + + /** + * Get network IDs for a given list of network names. + * It is assumed that there is a one to one mapping between the name and ID. + * @param networkNameList List of network names + * @return List of matching network IDs + */ + List<String> extractNetworkIds(List<String> networkNameList); + + /** + * Query the Openstack server objects from the list of stack resources + * + * @param stackResources A list of stack based resources + * @return A list of Openstack Server objects + */ + List<Server> getAllOpenstackServers(List<Resource> stackResources); + + /** + * Extract Openstack Image objects from a a list of Server objects + * + * @param servers A list of Openstack Server objects + * @return A list of Openstack Image objects + */ + List<Image> extractOpenstackImagesFromServers(List<Server> servers); + + /** + * Extract Openstack Flavor objects from a a list of Server objects + * + * @param servers A list of Openstack Server objects + * @return A list of Openstack Flavor objects + */ + List<Flavor> extractOpenstackFlavorsFromServers(List<Server> servers); + + /** + * Query and build AAI actions for Openstack Image resources to AAI's image objects + * + * @param images List of Openstack Image objects + * @throws HeatBridgeException when failing to add images to AAI + */ + void buildAddImagesToAaiAction(List<Image> images) throws HeatBridgeException; + + /** + * Query and build AAI actions for Openstack Flavor resources to AAI's flavor objects + * + * @param flavors List of Openstack Flavor objects + * @throws HeatBridgeException when failing to add flavors to AAI + */ + void buildAddFlavorsToAaiAction(List<Flavor> flavors) throws HeatBridgeException; + + /** + * Query and build AAI actions for Openstack Compute resources to AAI's vserver objects + * + * @param genericVnfId AAI generic-vnf-id + * @param vfModuleId AAI vf-module-id + * @param servers Openstack Server list + */ + void buildAddVserversToAaiAction(String genericVnfId, String vfModuleId, List<Server> servers); + + /** + * Query and build AAI actions for Openstack Neutron resources associated with a Compute resource to AAI's + * l-interface objects + * + * @param stackResources Openstack Heat stack resource list + * @param oobMgtNetIds List of OOB network IDs list + */ + void buildAddVserverLInterfacesToAaiAction(List<Resource> stackResources, List<String> oobMgtNetIds); + + /** + * Execute AAI restful API to update the Openstack resources + * + * @throws HeatBridgeException when failing to add openstack resource PoJos to AAI + */ + void submitToAai() throws HeatBridgeException; +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeException.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeException.java new file mode 100644 index 0000000000..f993d71e4c --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeException.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ +package org.onap.so.heatbridge; + +public class HeatBridgeException extends Exception { + + private static final long serialVersionUID = -1472047930391718894L; + + public HeatBridgeException(final String message) { + super(message); + } + + public HeatBridgeException(final String message, final Throwable cause) { + super(message, cause); + } +} 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 new file mode 100644 index 0000000000..90ceeb7d1c --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/HeatBridgeImpl.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ +package org.onap.so.heatbridge; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.ws.rs.WebApplicationException; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.validator.routines.InetAddressValidator; +import org.onap.aai.domain.yang.Flavor; +import org.onap.aai.domain.yang.Image; +import org.onap.aai.domain.yang.L3InterfaceIpv4AddressList; +import org.onap.aai.domain.yang.LInterface; +import org.onap.aai.domain.yang.PInterface; +import org.onap.aai.domain.yang.SriovPf; +import org.onap.aai.domain.yang.SriovPfs; +import org.onap.aai.domain.yang.SriovVf; +import org.onap.aai.domain.yang.SriovVfs; +import org.onap.aai.domain.yang.Vlan; +import org.onap.aai.domain.yang.Vlans; +import org.onap.aai.domain.yang.Vserver; +import org.onap.so.client.aai.AAIObjectType; +import org.onap.so.client.aai.AAIResourcesClient; +import org.onap.so.client.aai.AAISingleTransactionClient; +import org.onap.so.client.aai.entities.uri.AAIResourceUri; +import org.onap.so.client.aai.entities.uri.AAIUriFactory; +import org.onap.so.client.graphinventory.entities.uri.Depth; +import org.onap.so.client.graphinventory.exceptions.BulkProcessFailed; +import org.onap.so.db.catalog.beans.CloudIdentity; +import org.onap.so.heatbridge.constants.HeatBridgeConstants; +import org.onap.so.heatbridge.factory.MsoCloudClientFactoryImpl; +import org.onap.so.heatbridge.helpers.AaiHelper; +import org.onap.so.heatbridge.openstack.api.OpenstackClient; +import org.onap.so.heatbridge.openstack.factory.OpenstackClientFactoryImpl; +import org.onap.so.heatbridge.utils.HeatBridgeUtils; +import org.onap.so.logger.MessageEnum; +import org.onap.so.logger.ErrorCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.openstack4j.model.compute.Server; +import org.openstack4j.model.heat.Resource; +import org.openstack4j.model.network.IP; +import org.openstack4j.model.network.Network; +import org.openstack4j.model.network.NetworkType; +import org.openstack4j.model.network.Port; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; + +/** + * This class provides an implementation of {@link HeatBridgeApi} + */ +public class HeatBridgeImpl implements HeatBridgeApi { + + private static final Logger logger = LoggerFactory.getLogger(HeatBridgeImpl.class); + private static final String ERR_MSG_NULL_OS_CLIENT = "Initialization error: Null openstack client. Authenticate with Keystone first."; + private static final String OOB_MGT_NETWORK_IDENTIFIER = "Management"; + private OpenstackClient osClient; + private AAIResourcesClient resourcesClient; + private AAISingleTransactionClient transaction; + private String cloudOwner; + private String cloudRegionId; + private String tenantId; + private AaiHelper aaiHelper = new AaiHelper(); + private CloudIdentity cloudIdentity; + + + public HeatBridgeImpl(AAIResourcesClient resourcesClient, final CloudIdentity cloudIdentity, + @Nonnull final String cloudOwner, @Nonnull final String cloudRegionId, @Nonnull final String tenantId) { + Objects.requireNonNull(cloudOwner, "Null cloud-owner value!"); + Objects.requireNonNull(cloudRegionId, "Null cloud-region identifier!"); + Objects.requireNonNull(tenantId, "Null tenant identifier!"); + Objects.requireNonNull(tenantId, "Null AAI actions list!"); + + this.cloudIdentity = cloudIdentity; + this.cloudOwner = cloudOwner; + this.cloudRegionId = cloudRegionId; + this.tenantId = tenantId; + this.resourcesClient = resourcesClient; + this.transaction = resourcesClient.beginSingleTransaction(); + } + + @Override + public OpenstackClient authenticate() throws HeatBridgeException { + this.osClient = new MsoCloudClientFactoryImpl(new OpenstackClientFactoryImpl()) + .getOpenstackClient(cloudIdentity.getIdentityUrl(), cloudIdentity.getMsoId(), cloudIdentity.getMsoPass(), cloudRegionId, tenantId); + logger.debug("Successfully authenticated with keystone for tenant: " + tenantId + " and cloud " + + "region: " + cloudRegionId); + return osClient; + } + + @Override + public List<Resource> queryNestedHeatStackResources(final String heatStackId) { + Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT); + Preconditions.checkState(!Strings.isNullOrEmpty(heatStackId), "Invalid heatStackId!"); + List<Resource> stackBasedResources = osClient + .getStackBasedResources(heatStackId, HeatBridgeConstants.OS_DEFAULT_HEAT_NESTING); + logger.debug(stackBasedResources.size() + " heat stack resources are extracted for stack: " + heatStackId); + return stackBasedResources; + } + + @Override + public List<String> extractStackResourceIdsByResourceType(final List<Resource> stackResources, + final String resourceType) { + return stackResources.stream() + .filter(stackResource -> stackResource.getType().equals(resourceType)) + .map(Resource::getPhysicalResourceId) + .collect(Collectors.toList()); + } + + @Override + public List<String> extractNetworkIds(final List<String> networkNameList) { + Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT); + return networkNameList.stream() + .map(netName -> osClient.listNetworksByFilter(ImmutableMap.of(HeatBridgeConstants.OS_NAME_KEY, netName))) + .filter(nets -> nets != null && nets.size() == 1) //extract network-id only if network-name is unique + .map(nets -> nets.get(0).getId()) + .collect(Collectors.toList()); + } + + @Override + public List<Server> getAllOpenstackServers(final List<Resource> stackResources) { + Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT); + + // Filter Openstack Compute resources + List<String> serverIds = extractStackResourceIdsByResourceType(stackResources, + HeatBridgeConstants.OS_SERVER_RESOURCE_TYPE); + return serverIds.stream().map(serverId -> osClient.getServerById(serverId)).collect(Collectors.toList()); + } + + @Override + public List<org.openstack4j.model.compute.Image> extractOpenstackImagesFromServers(final List<Server> servers) { + Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT); + return servers.stream().map(Server::getImage) + .filter(distinctByProperty(org.openstack4j.model.compute.Image::getId)).collect(Collectors.toList()); + } + + @Override + public List<org.openstack4j.model.compute.Flavor> extractOpenstackFlavorsFromServers(final List<Server> servers) { + Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT); + return servers.stream().map(Server::getFlavor) + .filter(distinctByProperty(org.openstack4j.model.compute.Flavor::getId)).collect(Collectors.toList()); + } + + @Override + public void buildAddImagesToAaiAction(final List<org.openstack4j.model.compute.Image> images) + throws HeatBridgeException { + for (org.openstack4j.model.compute.Image image : images) { + Image aaiImage = aaiHelper.buildImage(image); + try { + AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.IMAGE, cloudOwner, cloudRegionId, aaiImage.getImageId()); + if (!resourcesClient.exists(uri)) { + transaction.create(uri, aaiImage); + logger.debug("Queuing AAI command to add image: " + aaiImage.getImageId()); + } else { + logger.debug("Nothing to add since image: " + aaiImage.getImageId() + "already exists in AAI."); + } + } catch (WebApplicationException e) { + throw new HeatBridgeException("Failed to update image to AAI: " + aaiImage.getImageId() + ". Error" + + " cause: " + e, e); + } + } + } + + @Override + public void buildAddFlavorsToAaiAction(final List<org.openstack4j.model.compute.Flavor> flavors) + throws HeatBridgeException { + for (org.openstack4j.model.compute.Flavor flavor : flavors) { + Flavor aaiFlavor = aaiHelper.buildFlavor(flavor); + try { + AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.FLAVOR, cloudOwner, cloudRegionId, aaiFlavor.getFlavorId()); + if (!resourcesClient.exists(uri)) { + transaction.create(uri, aaiFlavor); + logger.debug("Queuing AAI command to add flavor: " + aaiFlavor.getFlavorId()); + } else { + logger.debug("Nothing to add since flavor: " + aaiFlavor.getFlavorId() + "already exists in AAI."); + } + } catch (WebApplicationException e) { + throw new HeatBridgeException("Failed to update flavor to AAI: " + aaiFlavor.getFlavorId() + ". Error" + + " cause: " + e, e); + } + } + } + + @Override + public void buildAddVserversToAaiAction(final String genericVnfId, final String vfModuleId, + final List<Server> servers) { + servers.forEach(server -> { + Vserver vserver = aaiHelper.buildVserver(server.getId(), server); + + // Build vserver relationships to: image, flavor, pserver, vf-module + vserver.setRelationshipList(aaiHelper.getVserverRelationshipList(cloudOwner, cloudRegionId, genericVnfId, + vfModuleId, server)); + transaction.create(AAIUriFactory.createResourceUri(AAIObjectType.VSERVER, cloudOwner, cloudRegionId, tenantId, vserver.getVserverId()), vserver); + }); + } + + @Override + public void buildAddVserverLInterfacesToAaiAction(final List<Resource> stackResources, + final List<String> oobMgtNetIds) { + Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT); + List<String> portIds = extractStackResourceIdsByResourceType(stackResources, + HeatBridgeConstants.OS_PORT_RESOURCE_TYPE); + for (String portId : portIds) { + Port port = osClient.getPortById(portId); + LInterface lIf = new LInterface(); + lIf.setInterfaceId(port.getId()); + lIf.setInterfaceName(port.getName()); + lIf.setMacaddr(port.getMacAddress()); + if (oobMgtNetIds != null && oobMgtNetIds.contains(port.getNetworkId())) { + lIf.setInterfaceRole(OOB_MGT_NETWORK_IDENTIFIER); + } else { + lIf.setInterfaceRole(port.getvNicType()); + } + + updateLInterfaceIps(port, lIf); + updateLInterfaceVlan(port, lIf); + + // Update l-interface to the vserver + transaction.create(AAIUriFactory.createResourceUri( + AAIObjectType.L_INTERFACE, cloudOwner, cloudRegionId, tenantId, port.getDeviceId(), lIf.getInterfaceName()), lIf); + } + } + + private void updateLInterfaceVlan(final Port port, final LInterface lIf) { + Vlan vlan = new Vlan(); + Network network = osClient.getNetworkById(port.getNetworkId()); + lIf.setNetworkName(network.getName()); + if (network.getNetworkType().equals(NetworkType.VLAN)) { + vlan.setVlanInterface(network.getProviderSegID()); + Vlans vlans = new Vlans(); + List<Vlan> vlanList = vlans.getVlan(); + vlanList.add(vlan); + lIf.setVlans(vlans); + } + // Build sriov-vf to the l-interface + if (port.getvNicType().equalsIgnoreCase(HeatBridgeConstants.OS_SRIOV_PORT_TYPE)) { + SriovVfs sriovVfs = new SriovVfs(); + // JAXB does not generate setters for list, however getter ensures its creation. + // Thus, all list manipulations must be made on live list. + List<SriovVf> sriovVfList = sriovVfs.getSriovVf(); + SriovVf sriovVf = new SriovVf(); + sriovVf.setPciId(port.getProfile().get(HeatBridgeConstants.OS_PCI_SLOT_KEY).toString()); + sriovVf.setNeutronNetworkId(port.getNetworkId()); + if (port.getVifDetails() != null) { + sriovVf.setVfVlanFilter((String) port.getVifDetails().get(HeatBridgeConstants.OS_VLAN_NETWORK_KEY)); + } + sriovVfList.add(sriovVf); + + lIf.setSriovVfs(sriovVfs); + + // For the given port create sriov-pf for host pserver/p-interface if absent + updateSriovPfToPserver(port, lIf); + } + } + + /** + * Needs to be corrected according to the specification that is in draft + * If pserver/p-interface does not have a SRIOV-PF object matching the PCI-ID of the Openstack port object, then + * create it in AAI. + * Openstack SRIOV Port object has pci-id (to match sriov-pf on pserver/p-interface), physical-network ID (that + * matches the p-interface name). + * + * @param port Openstack port object + * @param lIf AAI l-interface object + */ + private void updateSriovPfToPserver(final Port port, final LInterface lIf) { + if (port.getProfile() == null || Strings + .isNullOrEmpty(port.getProfile().get(HeatBridgeConstants.OS_PHYSICAL_NETWORK_KEY).toString())) { + logger.debug("The SRIOV port:" + port.getName() + " is missing physical-network-id, cannot update " + + "sriov-pf object for host pserver: " + port.getHostId()); + return; + } + Optional<String> matchingPifName = HeatBridgeUtils + .getMatchingPserverPifName(port.getProfile().get(HeatBridgeConstants.OS_PHYSICAL_NETWORK_KEY).toString()); + if (matchingPifName.isPresent()) { + // Update l-interface description + String pserverHostName = port.getHostId(); + lIf.setInterfaceDescription( + "Attached to SR-IOV port: " + pserverHostName + "::" + matchingPifName.get()); + try { + Optional<PInterface> matchingPIf = resourcesClient.get(PInterface.class, + AAIUriFactory.createResourceUri(AAIObjectType.P_INTERFACE, pserverHostName, matchingPifName.get()).depth(Depth.ONE)); + if (matchingPIf.isPresent()) { + SriovPfs pIfSriovPfs = matchingPIf.get().getSriovPfs(); + if (pIfSriovPfs == null) { + pIfSriovPfs = new SriovPfs(); + } + // Extract PCI-ID from OS port object + String pfPciId = port.getProfile().get(HeatBridgeConstants.OS_PCI_SLOT_KEY).toString(); + + List<SriovPf> existingSriovPfs = pIfSriovPfs.getSriovPf(); + if (CollectionUtils.isEmpty(existingSriovPfs) || existingSriovPfs.stream() + .noneMatch(existingSriovPf -> existingSriovPf.getPfPciId().equals(pfPciId))) { + // Add sriov-pf object with PCI-ID to AAI + SriovPf sriovPf = new SriovPf(); + sriovPf.setPfPciId(pfPciId); + logger.debug("Queuing AAI command to update sriov-pf object to pserver: " + pserverHostName + "/" + + matchingPifName.get()); + transaction.create(AAIUriFactory.createResourceUri( + AAIObjectType.SRIOV_PF, pserverHostName, matchingPifName.get(), sriovPf.getPfPciId()), sriovPf); + } + } + } catch (WebApplicationException e) { + // Silently log that we failed to update the Pserver p-interface with PCI-ID + logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.GENERAL_EXCEPTION, pserverHostName, matchingPifName.get(), cloudOwner, + tenantId, "OpenStack", "Heatbridge", ErrorCode.DataError.getValue(), "Exception - Failed to add sriov-pf object to pserver", e); + } + } + } + + private void updateLInterfaceIps(final Port port, final LInterface lIf) { + List<L3InterfaceIpv4AddressList> lInterfaceIps = lIf.getL3InterfaceIpv4AddressList(); + for (IP ip : port.getFixedIps()) { + String ipAddress = ip.getIpAddress(); + if (InetAddressValidator.getInstance().isValidInet4Address(ipAddress)) { + L3InterfaceIpv4AddressList lInterfaceIp = new L3InterfaceIpv4AddressList(); + lInterfaceIp.setL3InterfaceIpv4Address(ipAddress); + lInterfaceIp.setNeutronNetworkId(port.getNetworkId()); + lInterfaceIp.setNeutronSubnetId(ip.getSubnetId()); + lInterfaceIp.setL3InterfaceIpv4PrefixLength(32L); + lInterfaceIps.add(lInterfaceIp); + } + } + } + + @Override + public void submitToAai() throws HeatBridgeException { + try { + transaction.execute(); + } catch (BulkProcessFailed e) { + String msg = "Failed to commit transaction"; + logger.debug(msg + " with error: " + e); + throw new HeatBridgeException(msg, e); + } + } + + private <T> Predicate<T> distinctByProperty(Function<? super T, Object> keyExtractor) { + Map<Object, Boolean> map = new ConcurrentHashMap<>(); + return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; + } +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/constants/HeatBridgeConstants.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/constants/HeatBridgeConstants.java new file mode 100644 index 0000000000..1f302341ad --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/constants/HeatBridgeConstants.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ +package org.onap.so.heatbridge.constants; + +public class HeatBridgeConstants { + + private HeatBridgeConstants() { + throw new IllegalStateException("Trying to instantiate a constants class."); + } + + /** + * Openstack related constants + */ + public static final Integer OS_DEFAULT_HEAT_NESTING = 5; + public static final String OS_SERVER_RESOURCE_TYPE = "OS::Nova::Server"; + public static final String OS_PORT_RESOURCE_TYPE = "OS::Neutron::Port"; + public static final String OS_SRIOV_PORT_TYPE = "direct"; + public static final String OS_PCI_SLOT_KEY = "pci_slot"; + public static final String OS_PHYSICAL_NETWORK_KEY = "physical_network"; + public static final String OS_VLAN_NETWORK_KEY = "vlan"; + public static final String OS_UNKNOWN_KEY = "unknown"; + public static final String OS_RESOURCES_SELF_LINK_KEY = "self"; + public static final String OS_DEFAULT_DOMAIN_NAME = "default"; + public static final String OS_KEYSTONE_V2_KEY = "v2.0"; + public static final String OS_KEYSTONE_V3_KEY = "v3"; + public static final String OS_NAME_KEY = "name"; + + /** + * AAI related constants + */ + public static final String AAI_GENERIC_VNF = "generic-vnf"; + public static final String AAI_GENERIC_VNF_ID = "generic-vnf.vnf-id"; + public static final String AAI_PSERVER = "pserver"; + public static final String AAI_VSERVER = "vserver"; + public static final String AAI_PSERVER_HOSTNAME = "pserver.hostname"; + public static final String AAI_VF_MODULE = "vf-module"; + public static final String AAI_VF_MODULE_ID = "vf-module.vf-module-id"; + public static final String AAI_IMAGE = "image"; + public static final String AAI_IMAGE_ID = "image.image-id"; + public static final String AAI_CLOUD_OWNER = "cloud-region.cloud-owner"; + public static final String AAI_CLOUD_REGION_ID = "cloud-region.cloud-region-id"; + public static final String AAI_FLAVOR = "flavor"; + public static final String AAI_FLAVOR_ID = "flavor.flavor-id"; + public static final String AAI_RESOURCE_DEPTH_ALL = "all"; + public static final String AAI_SRIOV_PF = "sriov-pf"; + public static final String AAI_P_INTERFACE_NAME = "p-interface.interface-name"; + public static final String AAI_SRIOV_PF_PCI_ID = "sriov-pf.pf-pci-id"; + + /** + * Keys for internal usage + */ + public static final String KEY_FLAVORS = "flavors"; + public static final String KEY_IMAGES = "images"; + public static final String KEY_VSERVERS = "vservers"; + public static final String KEY_SRIOV_PFS = "pserverSriovPfs"; + public static final String KEY_GLOBAL_SUBSCRIBER_ID = "globalSubscriberId"; + public static final String KEY_SERVICE_TYPE = "subscriptionServiceType"; + public static final String KEY_SERVICE_INSTANCE_ID = "serviceInstanceId"; + public static final String KEY_VNF_INSTANCE_ID = "genericVnfId"; + public static final String KEY_MSO_REQUEST_ID = "msoRequestId"; + public static final String KEY_SO_WORKFLOW_EXCEPTION = "WorkflowException"; + public static final String KEY_PROCESS_STATUS_MSG = "processStatusMsg"; +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/factory/MsoCloudClientFactory.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/factory/MsoCloudClientFactory.java new file mode 100644 index 0000000000..100b50e502 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/factory/MsoCloudClientFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ +package org.onap.so.heatbridge.factory; + +import org.onap.so.heatbridge.HeatBridgeException; +import org.onap.so.heatbridge.openstack.api.OpenstackClient; + +/** + * Defines contract to load the cloud configuration from SO, authenticate with keystone for a given cloud-region and + * tenant. + */ +public interface MsoCloudClientFactory { + + /** + * Get the Openstack Client for keystone version specified in cloud configuration. + * + * @param url openstack url + * @param msoId openstack user for mso + * @param msoPass openstack password for mso user + * @param cloudRegionId cloud-region identifier + * @param tenantId tenant identifier + * @return Openstack Client for the keystone version requested + * @throws HeatBridgeException if any errors when reading cloud configuration or getting openstack client + */ + + + OpenstackClient getOpenstackClient(String url, String msoId, String msoPass, String cloudRegionId, String tenantId) throws HeatBridgeException; +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/factory/MsoCloudClientFactoryImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/factory/MsoCloudClientFactoryImpl.java new file mode 100644 index 0000000000..b70b32a4d6 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/factory/MsoCloudClientFactoryImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ +package org.onap.so.heatbridge.factory; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.onap.so.heatbridge.HeatBridgeException; +import org.onap.so.heatbridge.constants.HeatBridgeConstants; +import org.onap.so.heatbridge.openstack.api.OpenstackAccess; +import org.onap.so.heatbridge.openstack.api.OpenstackAccess.OpenstackAccessBuilder; +import org.onap.so.heatbridge.openstack.api.OpenstackClient; +import org.onap.so.heatbridge.openstack.api.OpenstackClientException; +import org.onap.so.heatbridge.openstack.factory.OpenstackClientFactory; +import org.onap.so.utils.CryptoUtils; + +/** + * This class implements {@link MsoCloudClientFactory} + * It loads the cloud configuration from SO and uses it to authenticate with keystone. + * As a result of authentication with keystone, it returns the Openstack client with the auth token so that + * subsequent API calls to Openstack can be made. + */ +public class MsoCloudClientFactoryImpl implements MsoCloudClientFactory { + + private OpenstackClientFactory openstackClientFactory; + + public MsoCloudClientFactoryImpl(@Nonnull OpenstackClientFactory openstackClientFactory) { + Objects.requireNonNull(openstackClientFactory, "Null OpenstackClientFactory object"); + this.openstackClientFactory = openstackClientFactory; + } + @Override + public OpenstackClient getOpenstackClient(@Nonnull String url, @Nonnull String msoId, @Nonnull String msoPass, @Nonnull String cloudRegionId, @Nonnull String tenantId) throws + HeatBridgeException { + Objects.requireNonNull(url, "Null openstack url!"); + Objects.requireNonNull(msoId, "Null openstack user id!"); + Objects.requireNonNull(msoPass, "Null openstack password!"); + Objects.requireNonNull(cloudRegionId, "Null cloud-region ID!"); + Objects.requireNonNull(tenantId, "Null tenant ID!"); + try { + final OpenstackAccess osAccess = new OpenstackAccessBuilder() + .setBaseUrl(url) // keystone URL + .setUser(msoId) // keystone username + .setPassword(CryptoUtils.decryptCloudConfigPassword(msoPass)) // keystone decrypted password + .setRegion(cloudRegionId) // openstack region + .setDomainName(HeatBridgeConstants.OS_DEFAULT_DOMAIN_NAME) // hardcode to "default" + .setTenantId(tenantId) // tenantId + .build(); + + // Identify the Keystone version + String version = new URL(url).getPath().replace("/", ""); + if (version.equals(HeatBridgeConstants.OS_KEYSTONE_V2_KEY)) { + return openstackClientFactory.createOpenstackV2Client(osAccess); + } else if (version.equals(HeatBridgeConstants.OS_KEYSTONE_V3_KEY)) { + return openstackClientFactory.createOpenstackV3Client(osAccess); + } + throw new OpenstackClientException("Unsupported keystone version!"); + } catch (MalformedURLException e) { + throw new HeatBridgeException("Malformed Keystone Endpoint in SO configuration.", e); + } catch (OpenstackClientException osClientEx) { + throw new HeatBridgeException("Client error when authenticating with the Openstack V3.", osClientEx); + } + } +} 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 new file mode 100644 index 0000000000..a0f1f0798f --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/helpers/AaiHelper.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2018 Bell Canada. + * + * 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. + */ +package org.onap.so.heatbridge.helpers; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.commons.collections.CollectionUtils; +import org.onap.aai.domain.yang.Flavor; +import org.onap.aai.domain.yang.Image; +import org.onap.aai.domain.yang.Relationship; +import org.onap.aai.domain.yang.RelationshipData; +import org.onap.aai.domain.yang.RelationshipList; +import org.onap.aai.domain.yang.SriovVf; +import org.onap.aai.domain.yang.Vserver; +import org.onap.so.heatbridge.constants.HeatBridgeConstants; +import org.openstack4j.model.compute.Server; + +/** + * This class provides wrapper methods to manage creation of AAI objects and extracting objects from AAI and + * transforming into required objects. + */ +public class AaiHelper { + + /** + * Build vserver relationship object to entities: pserver, vf-module, image, flavor + * + * @param cloudOwner AAI cloudOwner value + * @param cloudRegionId AAI cloud-region identifier + * @param genericVnfId AAI generic-vnf identifier + * @param vfModuleId AAI vf-module identifier + * @param server Openstack Server object + */ + public RelationshipList getVserverRelationshipList(final String cloudOwner, final String cloudRegionId, final String + genericVnfId, final String vfModuleId, final Server server) { + RelationshipList relationshipList = new RelationshipList(); + List<Relationship> relationships = relationshipList.getRelationship(); + + // vserver to pserver relationship + Relationship pserverRelationship = buildRelationship(HeatBridgeConstants.AAI_PSERVER, + ImmutableMap.<String, String>builder() + .put(HeatBridgeConstants.AAI_PSERVER_HOSTNAME, server.getHypervisorHostname()) + .build()); + relationships.add(pserverRelationship); + + // vserver to vf-module relationship + Relationship vfModuleRelationship = buildRelationship(HeatBridgeConstants.AAI_VF_MODULE, + ImmutableMap.<String, String>builder() + .put(HeatBridgeConstants.AAI_GENERIC_VNF_ID, genericVnfId) + .put(HeatBridgeConstants.AAI_VF_MODULE_ID, vfModuleId) + .build()); + relationships.add(vfModuleRelationship); + + // vserver to image relationship + Relationship imageRel = buildRelationship(HeatBridgeConstants.AAI_IMAGE, + ImmutableMap.<String, String>builder() + .put(HeatBridgeConstants.AAI_CLOUD_OWNER, cloudOwner) + .put(HeatBridgeConstants.AAI_CLOUD_REGION_ID, cloudRegionId) + .put(HeatBridgeConstants.AAI_IMAGE_ID, server.getImage().getId()) + .build()); + relationships.add(imageRel); + + // vserver to flavor relationship + Relationship flavorRel = buildRelationship(HeatBridgeConstants.AAI_FLAVOR, + ImmutableMap.<String, String>builder() + .put(HeatBridgeConstants.AAI_CLOUD_OWNER, cloudOwner) + .put(HeatBridgeConstants.AAI_CLOUD_REGION_ID, cloudRegionId) + .put(HeatBridgeConstants.AAI_FLAVOR_ID, server.getFlavor().getId()) + .build()); + relationships.add(flavorRel); + return relationshipList; + } + + public RelationshipList getLInterfaceRelationshipList(final String pserverName, final String pIfName, + final String pfPciId) { + RelationshipList relationshipList = new RelationshipList(); + List<Relationship> relationships = relationshipList.getRelationship(); + + // sriov-vf to sriov-pf relationship + Relationship sriovPfRelationship = buildRelationship(HeatBridgeConstants.AAI_SRIOV_PF, + ImmutableMap.<String, String>builder() + .put(HeatBridgeConstants.AAI_PSERVER_HOSTNAME, pserverName) + .put(HeatBridgeConstants.AAI_P_INTERFACE_NAME, pIfName) + .put(HeatBridgeConstants.AAI_SRIOV_PF_PCI_ID, pfPciId) + .build()); + relationships.add(sriovPfRelationship); + + return relationshipList; + } + + /** + * Transform Openstack Server object to AAI Vserver object + * + * @param serverId Openstack server identifier + * @param server Openstack server object + * @return AAI Vserver object + */ + public Vserver buildVserver(final String serverId, final Server server) { + Vserver vserver = new Vserver(); + vserver.setInMaint(false); + vserver.setIsClosedLoopDisabled(false); + vserver.setVserverId(serverId); + vserver.setVserverName(server.getName()); + vserver.setVserverName2(server.getName()); + vserver.setProvStatus(server.getStatus().value()); + server.getLinks().stream().filter(link -> link.getRel().equals(HeatBridgeConstants.OS_RESOURCES_SELF_LINK_KEY)) + .findFirst().ifPresent(link -> vserver.setVserverSelflink(link.getHref())); + return vserver; + } + + /** + * Transform Openstack Image object to AAI Image object + * + * @param image Openstack Image object + * @return AAI Image object + */ + public Image buildImage(final org.openstack4j.model.compute.Image image) { + Image aaiImage = new Image(); + aaiImage.setImageId(image.getId()); + aaiImage.setImageName(image.getName()); + aaiImage.setImageOsDistro(HeatBridgeConstants.OS_UNKNOWN_KEY); + aaiImage.setImageOsVersion(HeatBridgeConstants.OS_UNKNOWN_KEY); + image.getLinks().stream().filter(link -> link.getRel().equals(HeatBridgeConstants.OS_RESOURCES_SELF_LINK_KEY)) + .findFirst().ifPresent(link -> aaiImage.setImageSelflink(link.getHref())); + return aaiImage; + } + + /** + * Transform Openstack Flavor object to AAI Flavor object + * + * @param flavor Openstack Flavor object + * @return AAI Flavor object + */ + public Flavor buildFlavor(final org.openstack4j.model.compute.Flavor flavor) { + Flavor aaiFlavor = new Flavor(); + aaiFlavor.setFlavorId(flavor.getId()); + aaiFlavor.setFlavorName(flavor.getName()); + flavor.getLinks().stream().filter(link -> link.getRel().equals(HeatBridgeConstants.OS_RESOURCES_SELF_LINK_KEY)) + .findFirst().ifPresent(link -> aaiFlavor.setFlavorSelflink(link.getHref())); + return aaiFlavor; + } + + /** + * Extract a list of flavors URI associated with the list of vservers + * + * @param vservers List of vserver AAI objects + * @return a list of related flavor related-links + */ + public List<String> getFlavorsUriFromVserver(final List<Vserver> vservers) { + List<String> flavorUris = new ArrayList<>(); + vservers.forEach(vserver -> flavorUris.addAll( + filterRelatedLinksByRelatedToProperty(vserver.getRelationshipList(), HeatBridgeConstants.AAI_FLAVOR))); + return flavorUris; + } + + /** + * Extract a list of images URI associated with the list of vservers + * + * @param vservers List of vserver AAI objects + * @return a list of related image related-links + */ + public List<String> getImagesUriFromVserver(final List<Vserver> vservers) { + List<String> imageUris = new ArrayList<>(); + vservers.forEach(vserver -> imageUris.addAll( + filterRelatedLinksByRelatedToProperty(vserver.getRelationshipList(), HeatBridgeConstants.AAI_IMAGE))); + return imageUris; + } + + /** + * From the list vserver objects build a map of compute hosts's name and the PCI IDs linked to it. + * + * @param vservers List of vserver AAI objects + * @return a map of compute names to the PCI ids associated with the compute + */ + public Map<String, List<String>> getPserverToPciIdMap(final List<Vserver> vservers) { + Map<String, List<String>> pserverToPciIdMap = new HashMap<>(); + for(Vserver vserver : vservers) { + if(vserver.getLInterfaces() != null) { + List<String> pciIds = vserver.getLInterfaces().getLInterface() + .stream() + .filter(lInterface -> lInterface.getSriovVfs() != null + && CollectionUtils.isNotEmpty(lInterface.getSriovVfs().getSriovVf())) + .flatMap(lInterface -> lInterface.getSriovVfs().getSriovVf().stream()) + .map(SriovVf::getPciId) + .collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(pciIds)) { + List<String> matchingPservers = extractRelationshipDataValue(vserver.getRelationshipList(), + HeatBridgeConstants.AAI_PSERVER, HeatBridgeConstants.AAI_PSERVER_HOSTNAME); + Preconditions.checkState(matchingPservers != null && matchingPservers.size() == 1, + "Invalid pserver relationships for vserver: " + vserver.getVserverName()); + pserverToPciIdMap.put(matchingPservers.get(0), pciIds); + } + } + } + return pserverToPciIdMap; + } + + /** + * Extract from relationship-list object all the relationship-value that match the related-to and + * relationship-key fields. + * + * @param relationshipListObj AAI relationship-list object + * @param relatedToProperty related-to value + * @param relationshipKey relationship-key value + * @return relationship-value matching the key requested for the relationship object of type related-to property + */ + private List<String> extractRelationshipDataValue(final RelationshipList relationshipListObj, + final String relatedToProperty, final String relationshipKey) { + if (relationshipListObj != null && relationshipListObj.getRelationship() != null) { + return relationshipListObj.getRelationship().stream() + .filter(relationship -> relationship.getRelatedTo().equals(relatedToProperty)) + .map(Relationship::getRelationshipData) + .flatMap(Collection::stream) + .filter(data -> data.getRelationshipKey() != null && relationshipKey.equals(data.getRelationshipKey())) + .map(RelationshipData::getRelationshipValue) + .collect(Collectors.toList()); + } + return new ArrayList<>(); + } + + /** + * Extract and filter the related-links to all objects that match the type specified by the filter property + * + * @param relationshipListObj AAI object representing relationship object + * @param relatedToProperty Value identifying the type of AAI object for related-to field + * @return a list of related-links filtered by the specified related-to property + */ + private List<String> filterRelatedLinksByRelatedToProperty(final RelationshipList relationshipListObj, + final String relatedToProperty) { + if (relationshipListObj != null && relationshipListObj.getRelationship() != null) { + return relationshipListObj.getRelationship().stream() + .filter(relationship -> relationship.getRelatedTo().equals(relatedToProperty)) + .map(Relationship::getRelatedLink) + .collect(Collectors.toList()); + } + return new ArrayList<>(); + } + + /** + * Build the relationship object + * + * @param relatedTo Related to entity value + * @param relationshipKeyValues Key value pairs of relationship data + * @return AAI Relationship object + */ + private Relationship buildRelationship(final String relatedTo, final Map<String, String> relationshipKeyValues) { + Relationship relationship = new Relationship(); + relationship.setRelatedTo(relatedTo); + relationshipKeyValues.keySet().forEach(k -> { + RelationshipData relationshipData = new RelationshipData(); + relationshipData.setRelationshipKey(k); + relationshipData.setRelationshipValue(relationshipKeyValues.get(k)); + relationship.getRelationshipData().add(relationshipData); + }); + return relationship; + } +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackAccess.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackAccess.java new file mode 100644 index 0000000000..fd5dabc784 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackAccess.java @@ -0,0 +1,120 @@ +/*- + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ + +package org.onap.so.heatbridge.openstack.api; + +import org.openstack4j.model.common.Identifier; + +/** + * Object handling OpenStack API access information. + */ +public class OpenstackAccess { + private final String baseUrl; + private final String tenantId; + private final String user; + private final String password; + private final String region; + private String domainName; + private String projectName; + + public OpenstackAccess(OpenstackAccessBuilder builder) { + this.baseUrl = builder.baseUrl; + this.tenantId = builder.tenantId; + this.user = builder.user; + this.password = builder.password; + this.region = builder.region; + this.domainName = builder.domainName; + this.projectName = builder.projectName; + } + + public String getUrl() { + return baseUrl; + } + + public String getTenantId() { + return tenantId; + } + + public String getUser() { + return user; + } + + public String getPassword() { + return password; + } + + public String getRegion() { + return region; + } + + public Identifier getDomainNameIdentifier() { + return Identifier.byName(domainName); + } + + public String getProjectName() { + return projectName; + } + + public static class OpenstackAccessBuilder { + + private String baseUrl; + private String tenantId; + private String user; + private String password; + private String region; + private String domainName; + private String projectName; + + public OpenstackAccessBuilder setBaseUrl(final String baseUrl) { + this.baseUrl = baseUrl; + return this; + } + + public OpenstackAccessBuilder setTenantId(final String tenantId) { + this.tenantId = tenantId; + return this; + } + + public OpenstackAccessBuilder setUser(final String user) { + this.user = user; + return this; + } + + public OpenstackAccessBuilder setPassword(final String password) { + this.password = password; + return this; + } + + public OpenstackAccessBuilder setRegion(final String region) { + this.region = region; + return this; + } + + public OpenstackAccessBuilder setDomainName(final String domainName) { + this.domainName = domainName; + return this; + } + + public OpenstackAccessBuilder setProjectName(final String projectName) { + this.projectName = projectName; + return this; + } + + public OpenstackAccess build() { + return new OpenstackAccess(this); + } + } +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackClient.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackClient.java new file mode 100644 index 0000000000..143e33581d --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackClient.java @@ -0,0 +1,69 @@ +/*- + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ + +package org.onap.so.heatbridge.openstack.api; + +import java.util.List; +import java.util.Map; +import org.openstack4j.model.compute.Server; +import org.openstack4j.model.heat.Resource; +import org.openstack4j.model.network.Network; +import org.openstack4j.model.network.Port; + +public interface OpenstackClient { + + /** + * Get a server object by server ID + * @param serverId Unique server-name (simple name) or server-id (UUID) + * @return Server object + */ + Server getServerById(String serverId); + + /** + * Get a port object by port ID + * @param portId Unique UUID of the port. + * @return Port object. + */ + Port getPortById(String portId); + + /** + * Returns a list of all ports we have the right to see + * @return List of all Openstack ports + */ + List<Port> getAllPorts(); + + /** + * Returns a list of all the resources for the stack + * @param stackId Stack name or unique UUID + * @param nestingDepth The recursion level for which resources will be listed. + * @return List of Openstack Stack resources + */ + List<Resource> getStackBasedResources(String stackId, int nestingDepth); + + /** + * Get a network instance by network ID + * @param networkId Unique UUID of the network. + * @return Network object. + */ + Network getNetworkById(String networkId); + + /** + * List networks by filtering parameters + * @param filterParams key-value pairs for filtering params + * @return List of filtered Network objects + */ + List<Network> listNetworksByFilter(Map<String, String> filterParams); +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackClientException.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackClientException.java new file mode 100644 index 0000000000..a062ca826d --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackClientException.java @@ -0,0 +1,29 @@ +/*- + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ + +package org.onap.so.heatbridge.openstack.api; + +public class OpenstackClientException extends Exception { + private static final long serialVersionUID = -5514207977226960180L; + + public OpenstackClientException(final String message) { + super(message); + } + + public OpenstackClientException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackClientImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackClientImpl.java new file mode 100644 index 0000000000..ebd4753323 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackClientImpl.java @@ -0,0 +1,69 @@ +/*- + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ + +package org.onap.so.heatbridge.openstack.api; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import org.openstack4j.api.OSClient; +import org.openstack4j.model.compute.Server; +import org.openstack4j.model.heat.Resource; +import org.openstack4j.model.network.Network; +import org.openstack4j.model.network.Port; + +abstract class OpenstackClientImpl implements OpenstackClient { + @Override + public Server getServerById(String serverId) { + return getClient().compute().servers().get(serverId); + } + + @Override + public Port getPortById(String portId) { + return getClient().networking().port().get(portId); + } + + @Override + public List<Port> getAllPorts() { return (List<Port>)getClient().networking().port().list(); } + + @Override + public List<Resource> getStackBasedResources(String stackId, int nestingDepth) { + return getClient().heat() + .resources() + .list(stackId, nestingDepth) + .stream() + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + @Override + public Network getNetworkById(String networkId) { + return getClient().networking().network().get(networkId); + } + + @Override + public List<Network> listNetworksByFilter(Map<String, String> filterParams) { + return (List<Network>) getClient().networking().network().list(filterParams); + } + + /** + * Retrieves the specific client to utilize. + * @return The specific client to utilize + */ + protected abstract OSClient getClient(); + +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackV2ClientImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackV2ClientImpl.java new file mode 100644 index 0000000000..760be72b3f --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackV2ClientImpl.java @@ -0,0 +1,37 @@ +/*- + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ + +package org.onap.so.heatbridge.openstack.api; + +import org.openstack4j.api.OSClient; +import org.openstack4j.api.OSClient.OSClientV2; + +public class OpenstackV2ClientImpl extends OpenstackClientImpl { + + private OSClientV2 client; + + public OpenstackV2ClientImpl(OSClientV2 client) { + this.client = client; + } + + /** + * {@inheritDoc} + */ + protected OSClient getClient() { + return client; + } + +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackV3ClientImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackV3ClientImpl.java new file mode 100644 index 0000000000..dddd82ce6a --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/api/OpenstackV3ClientImpl.java @@ -0,0 +1,37 @@ +/*- + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ + +package org.onap.so.heatbridge.openstack.api; + +import org.openstack4j.api.OSClient; +import org.openstack4j.api.OSClient.OSClientV3; + +public class OpenstackV3ClientImpl extends OpenstackClientImpl { + + private OSClientV3 client; + + public OpenstackV3ClientImpl(OSClientV3 client) { + this.client = client; + } + + /** + * {@inheritDoc} + */ + protected OSClient getClient() { + return client; + } + +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/factory/OpenstackClientFactory.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/factory/OpenstackClientFactory.java new file mode 100644 index 0000000000..5019eec09b --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/factory/OpenstackClientFactory.java @@ -0,0 +1,27 @@ +/*- + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ +package org.onap.so.heatbridge.openstack.factory; + +import org.onap.so.heatbridge.openstack.api.OpenstackAccess; +import org.onap.so.heatbridge.openstack.api.OpenstackClient; +import org.onap.so.heatbridge.openstack.api.OpenstackClientException; + +public interface OpenstackClientFactory { + + OpenstackClient createOpenstackV3Client(OpenstackAccess osAccess) throws OpenstackClientException; + + OpenstackClient createOpenstackV2Client(OpenstackAccess osAccess) throws OpenstackClientException; +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/factory/OpenstackClientFactoryImpl.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/factory/OpenstackClientFactoryImpl.java new file mode 100644 index 0000000000..72b3795053 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/openstack/factory/OpenstackClientFactoryImpl.java @@ -0,0 +1,74 @@ +/*- + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ +package org.onap.so.heatbridge.openstack.factory; + +import com.google.common.base.Preconditions; +import org.onap.so.heatbridge.openstack.api.OpenstackAccess; +import org.onap.so.heatbridge.openstack.api.OpenstackClient; +import org.onap.so.heatbridge.openstack.api.OpenstackClientException; +import org.onap.so.heatbridge.openstack.api.OpenstackV2ClientImpl; +import org.onap.so.heatbridge.openstack.api.OpenstackV3ClientImpl; +import org.openstack4j.api.OSClient.OSClientV2; +import org.openstack4j.api.OSClient.OSClientV3; +import org.openstack4j.api.exceptions.AuthenticationException; +import org.openstack4j.openstack.OSFactory; + +public class OpenstackClientFactoryImpl implements OpenstackClientFactory { + + @Override + public OpenstackClient createOpenstackV3Client(OpenstackAccess osAccess) throws OpenstackClientException { + Preconditions.checkNotNull(osAccess.getUrl(), "Keystone-v3 Auth: endpoint not set."); + Preconditions.checkNotNull(osAccess.getUser(), "Keystone-v3 Auth: username not set."); + Preconditions.checkNotNull(osAccess.getPassword(), "Keystone-v3 Auth: password not set."); + Preconditions.checkNotNull(osAccess.getDomainNameIdentifier(), "Keystone-v3 Auth: domain not set."); + Preconditions.checkNotNull(osAccess.getRegion(), "Keystone-v3 Auth: region not set."); + + OSClientV3 client; + try { + client = OSFactory.builderV3() + .endpoint(osAccess.getUrl()) + .credentials(osAccess.getUser(), osAccess.getPassword(), osAccess.getDomainNameIdentifier()) + .authenticate() + .useRegion(osAccess.getRegion()); + return new OpenstackV3ClientImpl(client); + } catch (AuthenticationException exception) { + throw new OpenstackClientException("Failed to authenticate with Keystone-v3: " + osAccess.getUrl(), exception); + } + } + + @Override + public OpenstackClient createOpenstackV2Client(OpenstackAccess osAccess) throws OpenstackClientException { + Preconditions.checkNotNull(osAccess.getUrl(), "Keystone-v2 Auth: endpoint not set."); + Preconditions.checkNotNull(osAccess.getUser(), "Keystone-v2 Auth: username not set."); + Preconditions.checkNotNull(osAccess.getPassword(), "Keystone-v2 Auth: password not set."); + Preconditions.checkNotNull(osAccess.getTenantId(), "Keystone-v2 Auth: domain not set."); + Preconditions.checkNotNull(osAccess.getRegion(), "Keystone-v2 Auth: region not set."); + + OSClientV2 client; + try { + client = OSFactory.builderV2() + .endpoint(osAccess.getUrl()) + .credentials(osAccess.getUser(), osAccess.getPassword()) + .tenantId(osAccess.getTenantId()) + .authenticate() + .useRegion(osAccess.getRegion()); + return new OpenstackV2ClientImpl(client); + } catch (AuthenticationException exception) { + throw new OpenstackClientException("Failed to authenticate with Keystone-v2.0: " + osAccess.getUrl(), + exception); + } + } +} diff --git a/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/utils/HeatBridgeUtils.java b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/utils/HeatBridgeUtils.java new file mode 100644 index 0000000000..7daa8c2c71 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/main/java/org/onap/so/heatbridge/utils/HeatBridgeUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ + +package org.onap.so.heatbridge.utils; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import java.util.Optional; +import javax.annotation.Nonnull; + +public class HeatBridgeUtils { + + private HeatBridgeUtils() { + throw new IllegalStateException("Trying to instantiate a utility class."); + } + + /** + * IaaS naming convention for compute/p-interface to openstack/physical-network name mapping + */ + private static final String OS_SIDE_SHARED_SRIOV_PREFIX = "shared-"; + private static final String OS_SIDE_DEDICATED_SRIOV_PREFIX = "dedicated-"; + private static final String COMPUTE_SIDE_SHARED_SRIOV_PREFIX = "sriov-s-"; + private static final String COMPUTE_SIDE_DEDICATED_SRIOV_PREFIX = "sriov-d-"; + + public static Optional<String> getMatchingPserverPifName(@Nonnull final String physicalNetworkName) { + Preconditions.checkState(!Strings.isNullOrEmpty(physicalNetworkName), "Physical network name is null or " + + "empty!"); + if (physicalNetworkName.contains(OS_SIDE_DEDICATED_SRIOV_PREFIX)) { + return Optional + .of(physicalNetworkName.replace(OS_SIDE_DEDICATED_SRIOV_PREFIX, COMPUTE_SIDE_DEDICATED_SRIOV_PREFIX)); + } else if (physicalNetworkName.contains(OS_SIDE_SHARED_SRIOV_PREFIX)) { + return Optional + .of(physicalNetworkName.replace(OS_SIDE_SHARED_SRIOV_PREFIX, COMPUTE_SIDE_SHARED_SRIOV_PREFIX)); + } + return Optional.empty(); + } + + public static Optional<String> getMatchingPhysicalNetworkName(final String pserverPinterfaceName) { + if (pserverPinterfaceName.contains(COMPUTE_SIDE_DEDICATED_SRIOV_PREFIX)) { + return Optional + .of(pserverPinterfaceName.replace(COMPUTE_SIDE_DEDICATED_SRIOV_PREFIX, OS_SIDE_DEDICATED_SRIOV_PREFIX)); + } else if (pserverPinterfaceName.contains(COMPUTE_SIDE_SHARED_SRIOV_PREFIX)) { + return Optional + .of(pserverPinterfaceName.replace(COMPUTE_SIDE_SHARED_SRIOV_PREFIX, OS_SIDE_SHARED_SRIOV_PREFIX)); + } + return Optional.empty(); + } +} diff --git a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/audit/HeatStackAuditTest.java b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/audit/HeatStackAuditTest.java index 5eea46d09f..987e4cf76d 100644 --- a/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/audit/HeatStackAuditTest.java +++ b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/adapters/audit/HeatStackAuditTest.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.doReturn; import java.io.File; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -42,6 +43,7 @@ import org.onap.aai.domain.yang.LInterface; import org.onap.aai.domain.yang.LInterfaces; import org.onap.aai.domain.yang.Vserver; import org.onap.so.openstack.utils.MsoHeatUtils; +import org.onap.so.openstack.utils.MsoNeutronUtils; import org.skyscreamer.jsonassert.JSONAssert; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -49,6 +51,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.woorea.openstack.heat.model.Resource; import com.woorea.openstack.heat.model.Resources; import com.woorea.openstack.heat.model.Stack; +import com.woorea.openstack.quantum.model.Port; @RunWith(MockitoJUnitRunner.Silent.class) @@ -61,6 +64,9 @@ public class HeatStackAuditTest extends HeatStackAudit { private MsoHeatUtils msoHeatUtilsMock; @Mock + private MsoNeutronUtils neutronUtilsMock; + + @Mock private AuditVServer auditVserver; private static final String cloudRegion = "cloudRegion"; @@ -72,10 +78,32 @@ public class HeatStackAuditTest extends HeatStackAudit { private ObjectMapper stackObjectMapper = new ObjectMapper().configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); + private List<Optional<Port>> portList = new ArrayList<>(); + @Before public void setup() throws Exception{ resources= objectMapper.readValue(new File("src/test/resources/GetResources.json"), Resources.class); + Port neutronPort1 =stackObjectMapper.readValue(new File("src/test/resources/NeutronPort1.json"), Port.class); + doReturn(Optional.of(neutronPort1)).when(neutronUtilsMock).getNeutronPort("7ee06d9d-3d18-411c-9d3e-aec930f70413", cloudRegion,tenantId); + Port neutronPort2 = stackObjectMapper.readValue(new File("src/test/resources/NeutronPort2.json"), Port.class); + doReturn(Optional.of(neutronPort2)).when(neutronUtilsMock).getNeutronPort("27391d94-33af-474a-927d-d409249e8fd3", cloudRegion,tenantId); + Port neutronPort3 = stackObjectMapper.readValue(new File("src/test/resources/NeutronPort3.json"), Port.class); + doReturn(Optional.of(neutronPort3)).when(neutronUtilsMock).getNeutronPort("fdeedf37-c01e-4ab0-bdd6-8d5fc4913943", cloudRegion,tenantId); + Port neutronPort4 = stackObjectMapper.readValue(new File("src/test/resources/NeutronPort4.json"), Port.class); + doReturn(Optional.of(neutronPort4)).when(neutronUtilsMock).getNeutronPort("8d93f63e-e972-48c7-ad98-b2122da47315", cloudRegion,tenantId); + Port neutronPort5 = stackObjectMapper.readValue(new File("src/test/resources/NeutronPort5.json"), Port.class); + doReturn(Optional.of(neutronPort5)).when(neutronUtilsMock).getNeutronPort("0594a2f2-7ea4-42eb-abc2-48ea49677fca", cloudRegion,tenantId); + Port neutronPort6 = stackObjectMapper.readValue(new File("src/test/resources/NeutronPort6.json"), Port.class); + doReturn(Optional.of(neutronPort6)).when(neutronUtilsMock).getNeutronPort("00bb8407-650e-48b5-b919-33b88d6f8fe3", cloudRegion,tenantId); + + portList.add(Optional.empty()); + portList.add(Optional.of(neutronPort1)); + portList.add(Optional.of(neutronPort2)); + portList.add(Optional.of(neutronPort3)); + portList.add(Optional.of(neutronPort4)); + portList.add(Optional.of(neutronPort5)); + portList.add(Optional.of(neutronPort6)); } @Test @@ -111,13 +139,13 @@ public class HeatStackAuditTest extends HeatStackAudit { vServer1.setLInterfaces(vServer1Linterfaces); LInterface ssc_1_trusted_port_0 = new LInterface(); - ssc_1_trusted_port_0.setInterfaceId("d2f51f82-0ec2-4581-bd1a-d2a82073e52b"); + ssc_1_trusted_port_0.setInterfaceId("7ee06d9d-3d18-411c-9d3e-aec930f70413"); vServer1.getLInterfaces().getLInterface().add(ssc_1_trusted_port_0); LInterface ssc_1_mgmt_port_1 = new LInterface(); - ssc_1_mgmt_port_1.setInterfaceId("07f5b14c-147a-4d14-8c94-a9e94dbc097b"); + ssc_1_mgmt_port_1.setInterfaceId("fdeedf37-c01e-4ab0-bdd6-8d5fc4913943"); vServer1.getLInterfaces().getLInterface().add(ssc_1_mgmt_port_1); LInterface ssc_1_mgmt_port_0 = new LInterface(); @@ -187,7 +215,7 @@ public class HeatStackAuditTest extends HeatStackAudit { Resources service1ResourceQuerySubInt3 = objectMapper.readValue(new File("src/test/resources/Service1SubInterface2Resources.json"), Resources.class); doReturn(service1ResourceQuerySubInt3).when(msoHeatUtilsMock).executeHeatClientRequest("/stacks/tsbc0005vm002ssc001-ssc_1_subint_service1_port_0_subinterfaces-dtmxjmny7yjz-2-y3ndsavmsymv/bd0fc728-cbde-4301-a581-db56f494675c/resources", cloudRegion,tenantId, Resources.class); - Set<Vserver> vServersToAudit = heatStackAudit.createVserverSet(resources, novaResources); + Set<Vserver> vServersToAudit = heatStackAudit.createVserverSet(resources, novaResources,portList); Set<Vserver> vserversWithSubInterfaces = heatStackAudit.processSubInterfaces(cloudRegion,tenantId,resourceGroups, vServersToAudit); String actualValue = objectMapper.writeValueAsString(vserversWithSubInterfaces); @@ -219,7 +247,7 @@ public class HeatStackAuditTest extends HeatStackAudit { vServer1.setLInterfaces(vServer1Linterfaces); LInterface ssc_1_trusted_port_0 = new LInterface(); - ssc_1_trusted_port_0.setInterfaceId("d2f51f82-0ec2-4581-bd1a-d2a82073e52b"); + ssc_1_trusted_port_0.setInterfaceId("7ee06d9d-3d18-411c-9d3e-aec930f70413"); vServer1.getLInterfaces().getLInterface().add(ssc_1_trusted_port_0); LInterface ssc_1_service1_port_0 = new LInterface(); @@ -227,7 +255,7 @@ public class HeatStackAuditTest extends HeatStackAudit { vServer1.getLInterfaces().getLInterface().add(ssc_1_service1_port_0); LInterface ssc_1_mgmt_port_1 = new LInterface(); - ssc_1_mgmt_port_1.setInterfaceId("07f5b14c-147a-4d14-8c94-a9e94dbc097b"); + ssc_1_mgmt_port_1.setInterfaceId("fdeedf37-c01e-4ab0-bdd6-8d5fc4913943"); vServer1.getLInterfaces().getLInterface().add(ssc_1_mgmt_port_1); LInterface ssc_1_mgmt_port_0 = new LInterface(); @@ -244,7 +272,7 @@ public class HeatStackAuditTest extends HeatStackAudit { expectedVservers.add(vServer1); - Set<Vserver> actualVservers = heatStackAudit.createVserverSet(resources, novaResources); + Set<Vserver> actualVservers = heatStackAudit.createVserverSet(resources, novaResources,portList); assertThat(actualVservers, sameBeanAs(expectedVservers)); } 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 new file mode 100644 index 0000000000..3c777e17cb --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/java/org/onap/so/heatbridge/HeatBridgeImplTest.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2018 Bell Canada. All rights reserved. + * + * 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. + */ +package org.onap.so.heatbridge; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.apache.commons.io.FileUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.onap.aai.domain.yang.LInterface; +import org.onap.aai.domain.yang.PInterface; +import org.onap.aai.domain.yang.SriovPf; +import org.onap.aai.domain.yang.Vserver; +import org.onap.so.client.aai.AAIObjectType; +import org.onap.so.client.aai.AAIResourcesClient; +import org.onap.so.client.aai.AAISingleTransactionClient; +import org.onap.so.client.aai.entities.uri.AAIResourceUri; +import org.onap.so.client.aai.entities.uri.AAIUriFactory; +import org.onap.so.client.graphinventory.exceptions.BulkProcessFailed; +import org.onap.so.db.catalog.beans.CloudIdentity; +import org.onap.so.heatbridge.constants.HeatBridgeConstants; +import org.onap.so.heatbridge.openstack.api.OpenstackClient; +import org.onap.so.heatbridge.openstack.api.OpenstackClientException; +import org.openstack4j.model.compute.Flavor; +import org.openstack4j.model.compute.Image; +import org.openstack4j.model.compute.Server; +import org.openstack4j.model.compute.Server.Status; +import org.openstack4j.model.heat.Resource; +import org.openstack4j.model.network.Network; +import org.openstack4j.model.network.NetworkType; +import org.openstack4j.model.network.Port; +import org.openstack4j.openstack.heat.domain.HeatResource; +import org.openstack4j.openstack.heat.domain.HeatResource.Resources; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; + + +@RunWith(MockitoJUnitRunner.class) +public class HeatBridgeImplTest { + + private static final String CLOUD_OWNER = "CloudOwner"; + private static final String REGION_ID = "RegionOne"; + private static final String TENANT_ID = "7320ec4a5b9d4589ba7c4412ccfd290f"; + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Mock + private OpenstackClient osClient; + + private CloudIdentity cloudIdentity = new CloudIdentity(); + + @Mock + private AAIResourcesClient resourcesClient; + @Mock + private AAISingleTransactionClient transaction; + + private HeatBridgeImpl heatbridge; + + @Before + public void setUp() throws HeatBridgeException, OpenstackClientException, BulkProcessFailed { + + when(resourcesClient.beginSingleTransaction()).thenReturn(transaction); + heatbridge = new HeatBridgeImpl(resourcesClient, cloudIdentity, CLOUD_OWNER, REGION_ID, TENANT_ID); + } + + @Ignore + @Test + public void testQueryNestedHeatStackResources() throws HeatBridgeException { + // Arrange + String heatStackId = "1234567"; + List<Resource> expectedResourceList = (List<Resource>) extractTestStackResources(); + when(osClient.getStackBasedResources(heatStackId, HeatBridgeConstants.OS_DEFAULT_HEAT_NESTING)) + .thenReturn(expectedResourceList); + + // Act + List<Resource> resourceList = heatbridge.queryNestedHeatStackResources(heatStackId); + + // Assert + verify(osClient).getStackBasedResources(heatStackId, HeatBridgeConstants.OS_DEFAULT_HEAT_NESTING); + assertEquals(resourceList, expectedResourceList); + } + + @Test + public void testExtractStackResourceIdsByResourceType() throws HeatBridgeException { + // Arrange + List<Resource> expectedResourceList = (List<Resource>) extractTestStackResources(); + List<String> expectedServerIds = Arrays.asList("43c2159b-2c04-46ac-bda5-594110cae2d3", + "7cff109a-b2b7-4933-97b4-ec44a8365568"); + + // Act + List<String> serverIds = heatbridge + .extractStackResourceIdsByResourceType(expectedResourceList, HeatBridgeConstants.OS_SERVER_RESOURCE_TYPE); + + // Assert + assertEquals(expectedServerIds, serverIds); + } + + @Ignore + @Test + public void testGetAllOpenstackServers() { + // Arrange + List<Resource> stackResources = (List<Resource>) extractTestStackResources(); + + Server server1 = mock(Server.class); + Server server2 = mock(Server.class); + List<Server> expectedServers = Arrays.asList(server1, server2); + + when(osClient.getServerById("43c2159b-2c04-46ac-bda5-594110cae2d3")).thenReturn(server1); + when(osClient.getServerById("7cff109a-b2b7-4933-97b4-ec44a8365568")).thenReturn(server2); + + // Act + List<Server> servers = heatbridge.getAllOpenstackServers(stackResources); + + // Assert + assertEquals(expectedServers, servers); + } + + @Ignore + @Test + public void testExtractOpenstackImagesFromServers() { + // Arrange + Server server1 = mock(Server.class); + Server server2 = mock(Server.class); + List<Server> servers = Arrays.asList(server1, server2); + + Image image1 = mock(Image.class); + Image image2 = mock(Image.class); + when(image1.getId()).thenReturn("1"); + when(image2.getId()).thenReturn("1"); + List<Image> expectedDistinctImages = Collections.singletonList(image1); + + when(server1.getImage()).thenReturn(image1); + when(server2.getImage()).thenReturn(image2); + + // Act + List<Image> images = heatbridge.extractOpenstackImagesFromServers(servers); + + // Assert + assertEquals(expectedDistinctImages, images); + } + + @Ignore + @Test + public void testExtractOpenstackFlavorsFromServers() { + // Arrange + Server server1 = mock(Server.class); + Server server2 = mock(Server.class); + List<Server> servers = Arrays.asList(server1, server2); + + Flavor flavor1 = mock(Flavor.class); + Flavor flavor2 = mock(Flavor.class); + when(flavor1.getId()).thenReturn("1"); + when(flavor2.getId()).thenReturn("2"); + List<Flavor> expectedFlavors = Arrays.asList(flavor1, flavor2); + + when(server1.getFlavor()).thenReturn(flavor1); + when(server2.getFlavor()).thenReturn(flavor2); + + // Act + List<Flavor> flavors = heatbridge.extractOpenstackFlavorsFromServers(servers); + + // Assert + assertEquals(expectedFlavors, flavors); + } + + @Test + public void testUpdateVserversToAai() throws HeatBridgeException { + // Arrange + Server server1 = mock(Server.class); + + when(server1.getId()).thenReturn("test-server1-id"); + when(server1.getHypervisorHostname()).thenReturn("test-hypervisor"); + when(server1.getName()).thenReturn("test-server1-name"); + when(server1.getStatus()).thenReturn(Status.ACTIVE); + when(server1.getLinks()).thenReturn(new ArrayList<>()); + + Server server2 = mock(Server.class); + when(server2.getId()).thenReturn("test-server2-id"); + when(server2.getHypervisorHostname()).thenReturn("test-hypervisor"); + when(server2.getName()).thenReturn("test-server2-name"); + when(server2.getStatus()).thenReturn(Status.ACTIVE); + when(server2.getLinks()).thenReturn(new ArrayList<>()); + + List<Server> servers = Arrays.asList(server1, server2); + + Image image = mock(Image.class); + when(server1.getImage()).thenReturn(image); + when(server2.getImage()).thenReturn(image); + when(image.getId()).thenReturn("test-image-id"); + + Flavor flavor = mock(Flavor.class); + when(server1.getFlavor()).thenReturn(flavor); + when(server2.getFlavor()).thenReturn(flavor); + when(flavor.getId()).thenReturn("test-flavor-id"); + + + // Act + heatbridge.buildAddVserversToAaiAction("test-genericVnf-id", "test-vfModule-id", servers); + + // Assert + ArgumentCaptor<AAIResourceUri> captor = ArgumentCaptor.forClass(AAIResourceUri.class); + verify(transaction, times(2)).create(captor.capture(), any(Vserver.class)); + + List<AAIResourceUri> uris = captor.getAllValues(); + assertEquals(AAIUriFactory.createResourceUri( + AAIObjectType.VSERVER, CLOUD_OWNER, REGION_ID, TENANT_ID, server1.getId()), uris.get(0)); + assertEquals(AAIUriFactory.createResourceUri( + AAIObjectType.VSERVER, CLOUD_OWNER, REGION_ID, TENANT_ID, server2.getId()), uris.get(1)); + + } + + @Test + public void testUpdateImagesToAai() throws HeatBridgeException { + // Arrange + Image image1 = mock(Image.class); + when(image1.getId()).thenReturn("test-image1-id"); + when(image1.getName()).thenReturn("test-image1-name"); + when(image1.getLinks()).thenReturn(new ArrayList<>()); + + Image image2 = mock(Image.class); + when(image2.getId()).thenReturn("test-image2-id"); + when(image2.getName()).thenReturn("test-image2-name"); + when(image2.getLinks()).thenReturn(new ArrayList<>()); + + List<Image> images = Arrays.asList(image1, image2); + + // Act #1 + heatbridge.buildAddImagesToAaiAction(images); + + // Assert #1 + verify(transaction, times(2)).create(any(AAIResourceUri.class), any(org.onap.aai.domain.yang.Image.class)); + + // Act #2 + heatbridge.buildAddImagesToAaiAction(images); + + // Assert #2 + verify(transaction, times(4)).create(any(AAIResourceUri.class), any(org.onap.aai.domain.yang.Image.class)); + } + + @Test + public void testUpdateFlavorsToAai() throws HeatBridgeException { + // Arrange + Flavor flavor1 = mock(Flavor.class); + when(flavor1.getId()).thenReturn("test-flavor1-id"); + when(flavor1.getName()).thenReturn("test-flavor1-name"); + when(flavor1.getLinks()).thenReturn(new ArrayList<>()); + + Flavor flavor2 = mock(Flavor.class); + when(flavor2.getId()).thenReturn("test-flavor2-id"); + when(flavor2.getName()).thenReturn("test-flavor2-name"); + when(flavor2.getLinks()).thenReturn(new ArrayList<>()); + + List<Flavor> flavors = Arrays.asList(flavor1, flavor2); + + // Act #1 + heatbridge.buildAddFlavorsToAaiAction(flavors); + + // Assert #1 + verify(transaction, times(2)).create(any(AAIResourceUri.class), any(org.onap.aai.domain.yang.Flavor.class)); + + // Act #2 + heatbridge.buildAddFlavorsToAaiAction(flavors); + + // Assert #2 + verify(transaction, times(4)).create(any(AAIResourceUri.class), any(org.onap.aai.domain.yang.Flavor.class)); + } + + @Ignore + @Test + public void testUpdateVserverLInterfacesToAai() throws HeatBridgeException { + // 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("test-device-id"); + when(port.getVifDetails()) + .thenReturn(ImmutableMap.of(HeatBridgeConstants.OS_VLAN_NETWORK_KEY, "2345")); + String pfPciId = "0000:08:00.0"; + when(port.getProfile()).thenReturn(ImmutableMap.of(HeatBridgeConstants.OS_PCI_SLOT_KEY, pfPciId, + HeatBridgeConstants.OS_PHYSICAL_NETWORK_KEY, "physical_network_id")); + + 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); + + SriovPf sriovPf = new SriovPf(); + sriovPf.setPfPciId(pfPciId); + PInterface pIf = mock(PInterface.class); + when(pIf.getInterfaceName()).thenReturn("test-port-id"); + when(resourcesClient.get(eq(PInterface.class), any(AAIResourceUri.class))).thenReturn(Optional.of(pIf)); + + // Act + heatbridge.buildAddVserverLInterfacesToAaiAction(stackResources, Arrays.asList("1", "2")); + + // Assert + verify(transaction, times(5)).create(any(AAIResourceUri.class), any(LInterface.class)); + verify(osClient, times(5)).getPortById(anyString()); + verify(osClient, times(5)).getNetworkById(anyString()); + } + + private List<? extends Resource> extractTestStackResources() { + List<HeatResource> stackResources = null; + try { + stackResources = MAPPER.readValue(readTestResourceFile("stack-resources.json"), Resources.class) + .getList(); + assertNotNull(stackResources); + assertFalse(stackResources.isEmpty()); + } catch (IOException e) { + Assert.fail("Failed to extract test stack resources."); + } + return stackResources; + } + + private String readTestResourceFile(String filePath) { + String content = null; + String pathname = Objects.requireNonNull(getClass().getClassLoader().getResource(filePath)).getFile(); + File file = new File(pathname); + try { + content = Objects.requireNonNull(FileUtils.readFileToString(file, Charset.defaultCharset())); + } catch (IOException e) { + Assert.fail(String.format("Failed to read test resource file (%s)", filePath)); + } + return content; + } +} diff --git a/adapters/mso-openstack-adapters/src/test/resources/GetResources.json b/adapters/mso-openstack-adapters/src/test/resources/GetResources.json index 22e66d41bb..3366ce4a05 100644 --- a/adapters/mso-openstack-adapters/src/test/resources/GetResources.json +++ b/adapters/mso-openstack-adapters/src/test/resources/GetResources.json @@ -1,6 +1,6 @@ { - "resources": [ - { + "resources": [ + { "links": [ { "href": "https://orchestration.com:8004/v1/99cecb7b19dc4690960761abd0fe2413/stacks/zdyh3brlba05_addon/03840be2-7ce6-4e38-a748-dbd59a798732/resources/vlbagent_eph_aff_id", @@ -22,207 +22,208 @@ "resource_type": "OS::Heat::RandomString", "updated_time": "2019-02-07T22:56:12Z" }, + { + "links": [ + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_trusted_port_0", + "rel": "self" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", + "rel": "stack" + } + ], + "logical_resource_id": "ssc_1_trusted_port_0", + "physical_resource_id": "7ee06d9d-3d18-411c-9d3e-aec930f70413", + "required_by": [ + "ssc_server_1" + ], + "resource_name": "ssc_1_trusted_port_0", + "resource_status": "CREATE_COMPLETE", + "resource_status_reason": "state changed", + "resource_type": "OS::Neutron::Port", + "updated_time": "2019-01-23T19:34:15Z" + }, + { + "links": [ + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_service1_port_0", + "rel": "self" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", + "rel": "stack" + } + ], + "logical_resource_id": "ssc_1_service1_port_0", + "physical_resource_id": "36551a08-592c-4329-ab75-6c594420754c", + "required_by": [ + "ssc_1_subint_service1_port_0_subinterfaces", + "ssc_server_1" + ], + "resource_name": "ssc_1_service1_port_0", + "resource_status": "CREATE_COMPLETE", + "resource_status_reason": "state changed", + "resource_type": "OS::Neutron::Port", + "updated_time": "2019-01-23T19:34:15Z" + }, + { + "links": [ + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_subint_service2_port_0_subinterfaces", + "rel": "self" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", + "rel": "stack" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001-ssc_1_subint_service2_port_0_subinterfaces-hlzdigtimzst/447a9b41-714e-434b-b1d0-6cce8d9f0f0c", + "rel": "nested" + } + ], + "logical_resource_id": "ssc_1_subint_service2_port_0_subinterfaces", + "physical_resource_id": "447a9b41-714e-434b-b1d0-6cce8d9f0f0c", + "required_by": [], + "resource_name": "ssc_1_subint_service2_port_0_subinterfaces", + "resource_status": "CREATE_COMPLETE", + "resource_status_reason": "state changed", + "resource_type": "OS::Heat::ResourceGroup", + "updated_time": "2019-01-23T19:34:15Z" + }, + { + "links": [ + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_mgmt_port_1", + "rel": "self" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", + "rel": "stack" + } + ], + "logical_resource_id": "ssc_1_mgmt_port_1", + "physical_resource_id": "fdeedf37-c01e-4ab0-bdd6-8d5fc4913943", + "required_by": [ + "ssc_server_1" + ], + "resource_name": "ssc_1_mgmt_port_1", + "resource_status": "CREATE_COMPLETE", + "resource_status_reason": "state changed", + "resource_type": "OS::Neutron::Port", + "updated_time": "2019-01-23T19:34:15Z" + }, + { + "links": [ + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_mgmt_port_0", + "rel": "self" + }, { - "resource_name": "ssc_1_trusted_port_0", - "links": [ - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_trusted_port_0", - "rel": "self" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", - "rel": "stack" - } - ], - "logical_resource_id": "ssc_1_trusted_port_0", - "resource_status": "CREATE_COMPLETE", - "updated_time": "2019-01-23T19:34:15Z", - "required_by": [ - "ssc_server_1" - ], - "resource_status_reason": "state changed", - "physical_resource_id": "d2f51f82-0ec2-4581-bd1a-d2a82073e52b", - "resource_type": "OS::Neutron::Port" - }, - { - "resource_name": "ssc_1_service1_port_0", - "links": [ - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_service1_port_0", - "rel": "self" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", - "rel": "stack" - } - ], - "logical_resource_id": "ssc_1_service1_port_0", - "resource_status": "CREATE_COMPLETE", - "updated_time": "2019-01-23T19:34:15Z", - "required_by": [ - "ssc_1_subint_service1_port_0_subinterfaces", - "ssc_server_1" - ], - "resource_status_reason": "state changed", - "physical_resource_id": "27391d94-33af-474a-927d-d409249e8fd3", - "resource_type": "OS::Neutron::Port" - }, - { - "resource_name": "ssc_1_subint_service2_port_0_subinterfaces", - "links": [ - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_subint_service2_port_0_subinterfaces", - "rel": "self" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", - "rel": "stack" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001-ssc_1_subint_service2_port_0_subinterfaces-hlzdigtimzst/447a9b41-714e-434b-b1d0-6cce8d9f0f0c", - "rel": "nested" - } - ], - "logical_resource_id": "ssc_1_subint_service2_port_0_subinterfaces", - "resource_status": "CREATE_COMPLETE", - "updated_time": "2019-01-23T19:34:15Z", - "required_by": [], - "resource_status_reason": "state changed", - "physical_resource_id": "447a9b41-714e-434b-b1d0-6cce8d9f0f0c", - "resource_type": "OS::Heat::ResourceGroup" - }, - { - "resource_name": "ssc_1_mgmt_port_1", - "links": [ - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_mgmt_port_1", - "rel": "self" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", - "rel": "stack" - } - ], - "logical_resource_id": "ssc_1_mgmt_port_1", - "resource_status": "CREATE_COMPLETE", - "updated_time": "2019-01-23T19:34:15Z", - "required_by": [ - "ssc_server_1" - ], - "resource_status_reason": "state changed", - "physical_resource_id": "07f5b14c-147a-4d14-8c94-a9e94dbc097b", - "resource_type": "OS::Neutron::Port" - }, - { - "resource_name": "ssc_1_mgmt_port_0", - "links": [ - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_mgmt_port_0", - "rel": "self" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", - "rel": "stack" - } - ], - "logical_resource_id": "ssc_1_mgmt_port_0", - "resource_status": "CREATE_COMPLETE", - "updated_time": "2019-01-23T19:34:15Z", - "required_by": [ - "ssc_server_1" - ], - "resource_status_reason": "state changed", - "physical_resource_id": "8d93f63e-e972-48c7-ad98-b2122da47315", - "resource_type": "OS::Neutron::Port" - }, - { - "resource_name": "ssc_1_service2_port_0", - "links": [ - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_service2_port_0", - "rel": "self" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", - "rel": "stack" - } - ], - "logical_resource_id": "ssc_1_service2_port_0", - "resource_status": "CREATE_COMPLETE", - "updated_time": "2019-01-23T19:34:15Z", - "required_by": [ - "ssc_1_subint_service2_port_0_subinterfaces", - "ssc_server_1" - ], - "resource_status_reason": "state changed", - "physical_resource_id": "0594a2f2-7ea4-42eb-abc2-48ea49677fca", - "resource_type": "OS::Neutron::Port" - }, - { - "resource_name": "ssc_1_int_ha_port_0", - "links": [ - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_int_ha_port_0", - "rel": "self" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", - "rel": "stack" - } - ], - "logical_resource_id": "ssc_1_int_ha_port_0", - "resource_status": "CREATE_COMPLETE", - "updated_time": "2019-01-23T19:34:15Z", - "required_by": [ - "ssc_server_1" - ], - "resource_status_reason": "state changed", - "physical_resource_id": "00bb8407-650e-48b5-b919-33b88d6f8fe3", - "resource_type": "OS::Neutron::Port" - }, - { - "resource_name": "ssc_server_1", - "links": [ - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_server_1", - "rel": "self" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", - "rel": "stack" - } - ], - "logical_resource_id": "ssc_server_1", - "resource_status": "CREATE_COMPLETE", - "updated_time": "2019-01-23T19:34:15Z", - "required_by": [], - "resource_status_reason": "state changed", - "physical_resource_id": "92272b67-d23f-42ca-87fa-7b06a9ec81f3", - "resource_type": "OS::Nova::Server" - }, - { - "resource_name": "ssc_1_subint_service1_port_0_subinterfaces", - "links": [ - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_subint_service1_port_0_subinterfaces", - "rel": "self" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", - "rel": "stack" - }, - { - "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001-ssc_1_subint_service1_port_0_subinterfaces-dtmxjmny7yjz/31d0647a-6043-49a4-81b6-ccab29380672", - "rel": "nested" - } - ], - "logical_resource_id": "ssc_1_subint_service1_port_0_subinterfaces", - "resource_status": "CREATE_COMPLETE", - "updated_time": "2019-01-23T19:34:15Z", - "required_by": [], - "resource_status_reason": "state changed", - "physical_resource_id": "31d0647a-6043-49a4-81b6-ccab29380672", - "resource_type": "OS::Heat::ResourceGroup" + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", + "rel": "stack" + } + ], + "logical_resource_id": "ssc_1_mgmt_port_0", + "physical_resource_id": "8d93f63e-e972-48c7-ad98-b2122da47315", + "required_by": [ + "ssc_server_1" + ], + "resource_name": "ssc_1_mgmt_port_0", + "resource_status": "CREATE_COMPLETE", + "resource_status_reason": "state changed", + "resource_type": "OS::Neutron::Port", + "updated_time": "2019-01-23T19:34:15Z" + }, + { + "links": [ + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_service2_port_0", + "rel": "self" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", + "rel": "stack" } - ] + ], + "logical_resource_id": "ssc_1_service2_port_0", + "physical_resource_id": "0594a2f2-7ea4-42eb-abc2-48ea49677fca", + "required_by": [ + "ssc_1_subint_service2_port_0_subinterfaces", + "ssc_server_1" + ], + "resource_name": "ssc_1_service2_port_0", + "resource_status": "CREATE_COMPLETE", + "resource_status_reason": "state changed", + "resource_type": "OS::Neutron::Port", + "updated_time": "2019-01-23T19:34:15Z" + }, + { + "links": [ + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_int_ha_port_0", + "rel": "self" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", + "rel": "stack" + } + ], + "logical_resource_id": "ssc_1_int_ha_port_0", + "physical_resource_id": "00bb8407-650e-48b5-b919-33b88d6f8fe3", + "required_by": [ + "ssc_server_1" + ], + "resource_name": "ssc_1_int_ha_port_0", + "resource_status": "CREATE_COMPLETE", + "resource_status_reason": "state changed", + "resource_type": "OS::Neutron::Port", + "updated_time": "2019-01-23T19:34:15Z" + }, + { + "links": [ + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_server_1", + "rel": "self" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", + "rel": "stack" + } + ], + "logical_resource_id": "ssc_server_1", + "physical_resource_id": "92272b67-d23f-42ca-87fa-7b06a9ec81f3", + "required_by": [], + "resource_name": "ssc_server_1", + "resource_status": "CREATE_COMPLETE", + "resource_status_reason": "state changed", + "resource_type": "OS::Nova::Server", + "updated_time": "2019-01-23T19:34:15Z" + }, + { + "links": [ + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34/resources/ssc_1_subint_service1_port_0_subinterfaces", + "rel": "self" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001/75e046b1-cf7d-4590-91e7-a6079f83fd34", + "rel": "stack" + }, + { + "href": "https://orchestration.com:8004/v1/ea2d13cc98b44d60a6f94bdcb2738f9e/stacks/tsbc0005vm002ssc001-ssc_1_subint_service1_port_0_subinterfaces-dtmxjmny7yjz/31d0647a-6043-49a4-81b6-ccab29380672", + "rel": "nested" + } + ], + "logical_resource_id": "ssc_1_subint_service1_port_0_subinterfaces", + "physical_resource_id": "31d0647a-6043-49a4-81b6-ccab29380672", + "required_by": [], + "resource_name": "ssc_1_subint_service1_port_0_subinterfaces", + "resource_status": "CREATE_COMPLETE", + "resource_status_reason": "state changed", + "resource_type": "OS::Heat::ResourceGroup", + "updated_time": "2019-01-23T19:34:15Z" + } + ] } + diff --git a/adapters/mso-openstack-adapters/src/test/resources/NeutronPort1.json b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort1.json new file mode 100644 index 0000000000..e4bd83c21d --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort1.json @@ -0,0 +1,41 @@ +{ + "port": { + "admin_state_up": true, + "allowed_address_pairs": [ + { + "ip_address": "192.168.1.1", + "mac_address": "" + } + ], + "binding:host_id": "cool.host.com", + "binding:vif_details": { + "vhostuser_mode": "client", + "vhostuser_socket": "/var/run/asdfasdf/asdfasfd-3d", + "vhostuser_vrouter_plug": true + }, + "binding:vif_type": "vhostuser", + "binding:vnic_type": "normal", + "device_id": "92272b67-d23f-42ca-87fa-7b06a9ec81f3", + "device_owner": "compute:ddd-daa-ddd", + "fixed_ips": [ + { + "ip_address": "2001:1890:1001:264d::2d:2b", + "subnet_id": "1327b4d5-e0f7-4e95-b019-60caaac751d1" + }, + { + "ip_address": "192.168.1.1", + "subnet_id": "05f0a2e9-e9d9-4cc0-8635-b82d3de2d700" + } + ], + "id": "7ee06d9d-3d18-411c-9d3e-aec930f70413", + "mac_address": "02:7e:e0:6d:9d:3d", + "name": "ibcx0026v_ibcx0026vm003_untrusted_port", + "network_id": "cee81fae-28b9-40a0-985f-181796ae0df6", + "port_security_enabled": true, + "security_groups": [ + "ee45e4fd-b00c-4396-85ee-18d82bd03ef6" + ], + "status": "ACTIVE", + "tenant_id": "a9442388264e4a198e68484e676404e9" + } +} diff --git a/adapters/mso-openstack-adapters/src/test/resources/NeutronPort2.json b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort2.json new file mode 100644 index 0000000000..376a526133 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort2.json @@ -0,0 +1,41 @@ +{ + "port": { + "admin_state_up": true, + "allowed_address_pairs": [ + { + "ip_address": "192.168.1.1", + "mac_address": "" + } + ], + "binding:host_id": "cool.host.com", + "binding:vif_details": { + "vhostuser_mode": "client", + "vhostuser_socket": "/var/run/asdfasdf/asdfasfd-3d", + "vhostuser_vrouter_plug": true + }, + "binding:vif_type": "vhostuser", + "binding:vnic_type": "normal", + "device_id": "92272b67-d23f-42ca-87fa-7b06a9ec81f3", + "device_owner": "compute:ddd-daa-ddd", + "fixed_ips": [ + { + "ip_address": "2001:1890:1001:264d::2d:2b", + "subnet_id": "1327b4d5-e0f7-4e95-b019-60caaac751d1" + }, + { + "ip_address": "192.168.1.1", + "subnet_id": "05f0a2e9-e9d9-4cc0-8635-b82d3de2d700" + } + ], + "id": "27391d94-33af-474a-927d-d409249e8fd3", + "mac_address": "02:7e:e0:6d:9d:3d", + "name": "ibcx0026v_ibcx0026vm003_untrusted_port", + "network_id": "cee81fae-28b9-40a0-985f-181796ae0df6", + "port_security_enabled": true, + "security_groups": [ + "ee45e4fd-b00c-4396-85ee-18d82bd03ef6" + ], + "status": "ACTIVE", + "tenant_id": "a9442388264e4a198e68484e676404e9" + } +} diff --git a/adapters/mso-openstack-adapters/src/test/resources/NeutronPort3.json b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort3.json new file mode 100644 index 0000000000..f0549216be --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort3.json @@ -0,0 +1,41 @@ +{ + "port": { + "admin_state_up": true, + "allowed_address_pairs": [ + { + "ip_address": "192.168.1.1", + "mac_address": "" + } + ], + "binding:host_id": "cool.host.com", + "binding:vif_details": { + "vhostuser_mode": "client", + "vhostuser_socket": "/var/run/asdfasdf/asdfasfd-3d", + "vhostuser_vrouter_plug": true + }, + "binding:vif_type": "vhostuser", + "binding:vnic_type": "normal", + "device_id": "92272b67-d23f-42ca-87fa-7b06a9ec81f3", + "device_owner": "compute:ddd-daa-ddd", + "fixed_ips": [ + { + "ip_address": "2001:1890:1001:264d::2d:2b", + "subnet_id": "1327b4d5-e0f7-4e95-b019-60caaac751d1" + }, + { + "ip_address": "192.168.1.1", + "subnet_id": "05f0a2e9-e9d9-4cc0-8635-b82d3de2d700" + } + ], + "id": "fdeedf37-c01e-4ab0-bdd6-8d5fc4913943", + "mac_address": "02:7e:e0:6d:9d:3d", + "name": "ibcx0026v_ibcx0026vm003_untrusted_port", + "network_id": "cee81fae-28b9-40a0-985f-181796ae0df6", + "port_security_enabled": true, + "security_groups": [ + "ee45e4fd-b00c-4396-85ee-18d82bd03ef6" + ], + "status": "ACTIVE", + "tenant_id": "a9442388264e4a198e68484e676404e9" + } +} diff --git a/adapters/mso-openstack-adapters/src/test/resources/NeutronPort4.json b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort4.json new file mode 100644 index 0000000000..fa10b0c864 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort4.json @@ -0,0 +1,41 @@ +{ + "port": { + "admin_state_up": true, + "allowed_address_pairs": [ + { + "ip_address": "192.168.1.1", + "mac_address": "" + } + ], + "binding:host_id": "cool.host.com", + "binding:vif_details": { + "vhostuser_mode": "client", + "vhostuser_socket": "/var/run/asdfasdf/asdfasfd-3d", + "vhostuser_vrouter_plug": true + }, + "binding:vif_type": "vhostuser", + "binding:vnic_type": "normal", + "device_id": "92272b67-d23f-42ca-87fa-7b06a9ec81f3", + "device_owner": "compute:ddd-daa-ddd", + "fixed_ips": [ + { + "ip_address": "2001:1890:1001:264d::2d:2b", + "subnet_id": "1327b4d5-e0f7-4e95-b019-60caaac751d1" + }, + { + "ip_address": "192.168.1.1", + "subnet_id": "05f0a2e9-e9d9-4cc0-8635-b82d3de2d700" + } + ], + "id": "8d93f63e-e972-48c7-ad98-b2122da47315", + "mac_address": "02:7e:e0:6d:9d:3d", + "name": "ibcx0026v_ibcx0026vm003_untrusted_port", + "network_id": "cee81fae-28b9-40a0-985f-181796ae0df6", + "port_security_enabled": true, + "security_groups": [ + "ee45e4fd-b00c-4396-85ee-18d82bd03ef6" + ], + "status": "ACTIVE", + "tenant_id": "a9442388264e4a198e68484e676404e9" + } +} diff --git a/adapters/mso-openstack-adapters/src/test/resources/NeutronPort5.json b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort5.json new file mode 100644 index 0000000000..54a9ee8404 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort5.json @@ -0,0 +1,41 @@ +{ + "port": { + "admin_state_up": true, + "allowed_address_pairs": [ + { + "ip_address": "192.168.1.1", + "mac_address": "" + } + ], + "binding:host_id": "cool.host.com", + "binding:vif_details": { + "vhostuser_mode": "client", + "vhostuser_socket": "/var/run/asdfasdf/asdfasfd-3d", + "vhostuser_vrouter_plug": true + }, + "binding:vif_type": "vhostuser", + "binding:vnic_type": "normal", + "device_id": "92272b67-d23f-42ca-87fa-7b06a9ec81f3", + "device_owner": "compute:ddd-daa-ddd", + "fixed_ips": [ + { + "ip_address": "2001:1890:1001:264d::2d:2b", + "subnet_id": "1327b4d5-e0f7-4e95-b019-60caaac751d1" + }, + { + "ip_address": "192.168.1.1", + "subnet_id": "05f0a2e9-e9d9-4cc0-8635-b82d3de2d700" + } + ], + "id": "0594a2f2-7ea4-42eb-abc2-48ea49677fca", + "mac_address": "02:7e:e0:6d:9d:3d", + "name": "ibcx0026v_ibcx0026vm003_untrusted_port", + "network_id": "cee81fae-28b9-40a0-985f-181796ae0df6", + "port_security_enabled": true, + "security_groups": [ + "ee45e4fd-b00c-4396-85ee-18d82bd03ef6" + ], + "status": "ACTIVE", + "tenant_id": "a9442388264e4a198e68484e676404e9" + } +} diff --git a/adapters/mso-openstack-adapters/src/test/resources/NeutronPort6.json b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort6.json new file mode 100644 index 0000000000..c47dfd755b --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/resources/NeutronPort6.json @@ -0,0 +1,41 @@ +{ + "port": { + "admin_state_up": true, + "allowed_address_pairs": [ + { + "ip_address": "192.168.1.1", + "mac_address": "" + } + ], + "binding:host_id": "cool.host.com", + "binding:vif_details": { + "vhostuser_mode": "client", + "vhostuser_socket": "/var/run/asdfasdf/asdfasfd-3d", + "vhostuser_vrouter_plug": true + }, + "binding:vif_type": "vhostuser", + "binding:vnic_type": "normal", + "device_id": "92272b67-d23f-42ca-87fa-7b06a9ec81f3", + "device_owner": "compute:ddd-daa-ddd", + "fixed_ips": [ + { + "ip_address": "2001:1890:1001:264d::2d:2b", + "subnet_id": "1327b4d5-e0f7-4e95-b019-60caaac751d1" + }, + { + "ip_address": "192.168.1.1", + "subnet_id": "05f0a2e9-e9d9-4cc0-8635-b82d3de2d700" + } + ], + "id": "00bb8407-650e-48b5-b919-33b88d6f8fe3", + "mac_address": "02:7e:e0:6d:9d:3d", + "name": "ibcx0026v_ibcx0026vm003_untrusted_port", + "network_id": "cee81fae-28b9-40a0-985f-181796ae0df6", + "port_security_enabled": true, + "security_groups": [ + "ee45e4fd-b00c-4396-85ee-18d82bd03ef6" + ], + "status": "ACTIVE", + "tenant_id": "a9442388264e4a198e68484e676404e9" + } +} diff --git a/adapters/mso-openstack-adapters/src/test/resources/stack-resources.json b/adapters/mso-openstack-adapters/src/test/resources/stack-resources.json new file mode 100644 index 0000000000..6b63895a33 --- /dev/null +++ b/adapters/mso-openstack-adapters/src/test/resources/stack-resources.json @@ -0,0 +1,441 @@ +{ + "resources": [ + { + "resource_name": "ge_000", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule/d1431cdc-9b29-44fc-98d0-9b3dc1ac246d/resources/ge_000", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule/d1431cdc-9b29-44fc-98d0-9b3dc1ac246d", + "rel": "stack" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-ge_000-t66dxpwq6nb5/deee54a3-08ac-477b-9c09-c798edb40be1", + "rel": "nested" + } + ], + "logical_resource_id": "ge_000", + "resource_status_reason": "state changed", + "updated_time": "2018-04-09T21:09:52Z", + "required_by": [ + "vfw_instance" + ], + "resource_status": "CREATE_COMPLETE", + "physical_resource_id": "deee54a3-08ac-477b-9c09-c798edb40be1", + "resource_type": "port.yaml" + }, + { + "resource_name": "vfw_instance", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule/d1431cdc-9b29-44fc-98d0-9b3dc1ac246d/resources/vfw_instance", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule/d1431cdc-9b29-44fc-98d0-9b3dc1ac246d", + "rel": "stack" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "rel": "nested" + } + ], + "logical_resource_id": "vfw_instance", + "resource_status_reason": "state changed", + "updated_time": "2018-04-09T21:09:52Z", + "required_by": [], + "resource_status": "CREATE_COMPLETE", + "physical_resource_id": "54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "resource_type": "vfw.yaml" + }, + { + "resource_name": "port", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-ge_000-t66dxpwq6nb5/deee54a3-08ac-477b-9c09-c798edb40be1/resources/port", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-ge_000-t66dxpwq6nb5/deee54a3-08ac-477b-9c09-c798edb40be1", + "rel": "stack" + } + ], + "logical_resource_id": "port", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:09:52Z", + "required_by": [], + "resource_status_reason": "state changed", + "physical_resource_id": "212a203a-9764-4f42-84ea-731536a8f13a", + "resource_type": "OS::Neutron::Port" + }, + { + "resource_name": "pfe0", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b/resources/pfe0", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "rel": "stack" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-pfe0-kvqmgn7jmiti/1325e04b-e836-4a13-bb2e-f34923d97ad7", + "rel": "nested" + } + ], + "logical_resource_id": "pfe0", + "resource_status_reason": "state changed", + "updated_time": "2018-04-09T21:09:54Z", + "required_by": [ + "re0" + ], + "resource_status": "CREATE_COMPLETE", + "physical_resource_id": "1325e04b-e836-4a13-bb2e-f34923d97ad7", + "resource_type": "fpc.yaml" + }, + { + "resource_name": "fpc_internal_port", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b/resources/fpc_internal_port", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "rel": "stack" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-fpc_internal_port-gbnyc4w7mb5b/4e920f39-9784-417e-9331-d75e2e37cc51", + "rel": "nested" + } + ], + "logical_resource_id": "fpc_internal_port", + "resource_status_reason": "state changed", + "updated_time": "2018-04-09T21:09:54Z", + "required_by": [ + "pfe0" + ], + "resource_status": "CREATE_COMPLETE", + "physical_resource_id": "4e920f39-9784-417e-9331-d75e2e37cc51", + "resource_type": "re_pfe_port.yaml" + }, + { + "resource_name": "re-fpc-affinity-grp", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b/resources/re-fpc-affinity-grp", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "rel": "stack" + } + ], + "logical_resource_id": "re-fpc-affinity-grp", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:09:54Z", + "required_by": [ + "pfe0", + "re0" + ], + "resource_status_reason": "state changed", + "physical_resource_id": "3aa37238-f8ff-4c96-b56a-8903bae28a60", + "resource_type": "OS::Nova::ServerGroup" + }, + { + "resource_name": "re0", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b/resources/re0", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "rel": "stack" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re0-73oifso3xntc/0915e27e-428d-4d2c-a67b-abbce18081b2", + "rel": "nested" + } + ], + "logical_resource_id": "re0", + "resource_status_reason": "state changed", + "updated_time": "2018-04-09T21:09:54Z", + "required_by": [], + "resource_status": "CREATE_COMPLETE", + "physical_resource_id": "0915e27e-428d-4d2c-a67b-abbce18081b2", + "resource_type": "re.yaml" + }, + { + "resource_name": "re_external_port", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b/resources/re_external_port", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "rel": "stack" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_external_port-3okiee3zocr7/f58c65e3-a72e-4b2d-a295-cb40324d6b4c", + "rel": "nested" + } + ], + "logical_resource_id": "re_external_port", + "resource_status_reason": "state changed", + "updated_time": "2018-04-09T21:09:54Z", + "required_by": [ + "re0" + ], + "resource_status": "CREATE_COMPLETE", + "physical_resource_id": "f58c65e3-a72e-4b2d-a295-cb40324d6b4c", + "resource_type": "port.yaml" + }, + { + "resource_name": "fpc_external_port", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b/resources/fpc_external_port", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "rel": "stack" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-fpc_external_port-5vumcqp7hkbn/979e47c9-c15a-428e-ad73-af922029ee37", + "rel": "nested" + } + ], + "logical_resource_id": "fpc_external_port", + "resource_status_reason": "state changed", + "updated_time": "2018-04-09T21:09:54Z", + "required_by": [ + "pfe0", + "re0" + ], + "resource_status": "CREATE_COMPLETE", + "physical_resource_id": "979e47c9-c15a-428e-ad73-af922029ee37", + "resource_type": "port.yaml" + }, + { + "resource_name": "re_internal_port", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b/resources/re_internal_port", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "rel": "stack" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_internal_port-u4txbvemndci/0aebfd9d-ad97-43b1-a67b-b2b5340738d2", + "rel": "nested" + } + ], + "logical_resource_id": "re_internal_port", + "resource_status_reason": "state changed", + "updated_time": "2018-04-09T21:09:54Z", + "required_by": [ + "re0" + ], + "resource_status": "CREATE_COMPLETE", + "physical_resource_id": "0aebfd9d-ad97-43b1-a67b-b2b5340738d2", + "resource_type": "re_pfe_port.yaml" + }, + { + "resource_name": "re_pfe_network", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b/resources/re_pfe_network", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam/54f93b9e-5138-4f3f-bfe0-ee06e1f0877b", + "rel": "stack" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_pfe_network-2wmjvgzrhtvs/290fc2fd-cd1d-47d0-90eb-2ece7c009b29", + "rel": "nested" + } + ], + "logical_resource_id": "re_pfe_network", + "resource_status_reason": "state changed", + "updated_time": "2018-04-09T21:09:54Z", + "required_by": [ + "fpc_internal_port", + "re_internal_port" + ], + "resource_status": "CREATE_COMPLETE", + "physical_resource_id": "290fc2fd-cd1d-47d0-90eb-2ece7c009b29", + "resource_type": "bridge_int.yaml" + }, + { + "resource_name": "fpc", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-pfe0-kvqmgn7jmiti/1325e04b-e836-4a13-bb2e-f34923d97ad7/resources/fpc", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-pfe0-kvqmgn7jmiti/1325e04b-e836-4a13-bb2e-f34923d97ad7", + "rel": "stack" + } + ], + "logical_resource_id": "fpc", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:09:58Z", + "required_by": [], + "resource_status_reason": "state changed", + "physical_resource_id": "43c2159b-2c04-46ac-bda5-594110cae2d3", + "resource_type": "OS::Nova::Server" + }, + { + "resource_name": "port", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-fpc_internal_port-gbnyc4w7mb5b/4e920f39-9784-417e-9331-d75e2e37cc51/resources/port", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-fpc_internal_port-gbnyc4w7mb5b/4e920f39-9784-417e-9331-d75e2e37cc51", + "rel": "stack" + } + ], + "logical_resource_id": "port", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:09:56Z", + "required_by": [], + "resource_status_reason": "state changed", + "physical_resource_id": "387e3904-8948-43d1-8635-b6c2042b54da", + "resource_type": "OS::Neutron::Port" + }, + { + "resource_name": "re", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re0-73oifso3xntc/0915e27e-428d-4d2c-a67b-abbce18081b2/resources/re", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re0-73oifso3xntc/0915e27e-428d-4d2c-a67b-abbce18081b2", + "rel": "stack" + } + ], + "logical_resource_id": "re", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:10:36Z", + "required_by": [], + "resource_status_reason": "state changed", + "physical_resource_id": "7cff109a-b2b7-4933-97b4-ec44a8365568", + "resource_type": "OS::Nova::Server" + }, + { + "resource_name": "port", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_external_port-3okiee3zocr7/f58c65e3-a72e-4b2d-a295-cb40324d6b4c/resources/port", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_external_port-3okiee3zocr7/f58c65e3-a72e-4b2d-a295-cb40324d6b4c", + "rel": "stack" + } + ], + "logical_resource_id": "port", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:09:55Z", + "required_by": [], + "resource_status_reason": "state changed", + "physical_resource_id": "70a09dfd-f1c5-4bc8-bd8f-dc539b8d662a", + "resource_type": "OS::Neutron::Port" + }, + { + "resource_name": "port", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-fpc_external_port-5vumcqp7hkbn/979e47c9-c15a-428e-ad73-af922029ee37/resources/port", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-fpc_external_port-5vumcqp7hkbn/979e47c9-c15a-428e-ad73-af922029ee37", + "rel": "stack" + } + ], + "logical_resource_id": "port", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:09:55Z", + "required_by": [], + "resource_status_reason": "state changed", + "physical_resource_id": "12f88b4d-c8a4-4fbd-bcb4-7e36af02430b", + "resource_type": "OS::Neutron::Port" + }, + { + "resource_name": "port", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_internal_port-u4txbvemndci/0aebfd9d-ad97-43b1-a67b-b2b5340738d2/resources/port", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_internal_port-u4txbvemndci/0aebfd9d-ad97-43b1-a67b-b2b5340738d2", + "rel": "stack" + } + ], + "logical_resource_id": "port", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:09:56Z", + "required_by": [], + "resource_status_reason": "state changed", + "physical_resource_id": "c54b9f45-b413-4937-bbe4-3c8a5689cfc9", + "resource_type": "OS::Neutron::Port" + }, + { + "resource_name": "bridge_network_subnet", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_pfe_network-2wmjvgzrhtvs/290fc2fd-cd1d-47d0-90eb-2ece7c009b29/resources/bridge_network_subnet", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_pfe_network-2wmjvgzrhtvs/290fc2fd-cd1d-47d0-90eb-2ece7c009b29", + "rel": "stack" + } + ], + "logical_resource_id": "bridge_network_subnet", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:09:55Z", + "required_by": [], + "resource_status_reason": "state changed", + "physical_resource_id": "5ffd8c02-6913-4b67-adba-74e78c2bbe40", + "resource_type": "OS::Neutron::Subnet" + }, + { + "resource_name": "bridge_network", + "links": [ + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_pfe_network-2wmjvgzrhtvs/290fc2fd-cd1d-47d0-90eb-2ece7c009b29/resources/bridge_network", + "rel": "self" + }, + { + "href": "http://10.10.10.10:8004/v1/7320ec4a5b9d4589ba7c4412ccfd290f/stacks/ClosedLoop_vFW_VfModule-vfw_instance-tw3i5ile2nam-re_pfe_network-2wmjvgzrhtvs/290fc2fd-cd1d-47d0-90eb-2ece7c009b29", + "rel": "stack" + } + ], + "logical_resource_id": "bridge_network", + "resource_status": "CREATE_COMPLETE", + "updated_time": "2018-04-09T21:09:55Z", + "required_by": [ + "bridge_network_subnet" + ], + "resource_status_reason": "state changed", + "physical_resource_id": "5ad95036-8daf-4379-a59c-865f35976cd4", + "resource_type": "OS::Neutron::Net" + } + ] +} |