cloudInformation.getOwner());
logger.debug("Successfully queried neutron resources and built AAI actions to add l-interfaces to vservers.");
+ heatBridgeClient.buildAddVolumes(stackResources);
+
// Update AAI
logger.debug("Current Dry Run Value: {}", env.getProperty("heatBridgeDryrun", Boolean.class, false));
heatBridgeClient.submitToAai(env.getProperty("heatBridgeDryrun", Boolean.class, false));
void buildAddVserverLInterfacesToAaiAction(List<Resource> stackResources, List<String> oobMgtNetIds,
String cloudOwner) throws HeatBridgeException;
+ /**
+ * Query and build AAI actions for Openstack volumes
+ *
+ * @throws HeatBridgeException when failing to remove heatbridge data from AAI for a given vf-module
+ */
+ void buildAddVolumes(List<Resource> stackResources) throws HeatBridgeException;
+
/**
* Query and build AAI actions for Openstack Compute resources to AAI's pserver and pinterface objects
*
* @throws HeatBridgeException when failing to remove heatbridge data from AAI for a given vf-module
*/
void deleteVfModuleData(String vnfId, String vfModuleId) throws HeatBridgeException;
+
+
}
import org.onap.aai.domain.yang.SriovVf;
import org.onap.aai.domain.yang.Subnets;
import org.onap.aai.domain.yang.Vlan;
+import org.onap.aai.domain.yang.Volume;
import org.onap.aai.domain.yang.Vserver;
import org.onap.aaiclient.client.aai.AAIDSLQueryClient;
import org.onap.aaiclient.client.aai.AAIResourcesClient;
import org.openstack4j.model.network.NetworkType;
import org.openstack4j.model.network.Port;
import org.openstack4j.model.network.Subnet;
+import org.openstack4j.model.storage.block.VolumeAttachment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
}
}
+ @Override
+ public void buildAddVolumes(List<Resource> stackResources) throws HeatBridgeException {
+ try {
+ if (stackResources.stream().anyMatch(r -> r.getType().equals("OS::Cinder::Volume"))) {
+ stackResources.stream().filter(r -> r.getType().equalsIgnoreCase("OS::Cinder::Volume"))
+ .forEach(r -> createVolume(r));
+ } else {
+ logger.debug("Heat stack contains no volumes");
+ }
+ } catch (Exception e) {
+ logger.error("Failed to add volumes to AAI", e);
+ throw new HeatBridgeException("Failed to add volumes to AAI", e);
+ }
+
+ }
+
+ protected void createVolume(Resource r) {
+ org.openstack4j.model.storage.block.Volume osVolume = osClient.getVolumeById(r.getPhysicalResourceId());
+ List<? extends VolumeAttachment> attachments = osVolume.getAttachments();
+ if (attachments != null) {
+ Optional<? extends VolumeAttachment> vserver = attachments.stream().findFirst();
+ if (vserver.isPresent()) {
+ Volume volume = new Volume();
+ volume.setVolumeId(r.getPhysicalResourceId());
+ AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIFluentTypeBuilder.cloudInfrastructure()
+ .cloudRegion(cloudOwner, cloudRegionId).tenant(tenantId).vserver(vserver.get().getServerId())
+ .volume(r.getPhysicalResourceId()));
+ transaction.createIfNotExists(uri, Optional.of(volume));
+ } else {
+ logger.warn(
+ "Volume {} contains no attachments in openstack. Unable to determine which vserver volume belongs too.",
+ r.getPhysicalResourceId());
+ }
+ } else {
+ logger.warn(
+ "Volume {} contains no attachments in openstack. Unable to determine which vserver volume belongs too.",
+ r.getPhysicalResourceId());
+ }
+ }
+
protected String getInterfaceType(NodeType nodeType, String nicType) {
logger.debug("nicType: " + nicType + "nodeType: " + nodeType);
if (DIRECT.equalsIgnoreCase(nicType)) {
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Port;
import org.openstack4j.model.network.Subnet;
+import org.openstack4j.model.storage.block.Volume;
public interface OpenstackClient {
* @return Subnet object.
*/
Subnet getSubnetById(String subnetId);
+
+ /**
+ * Get a volume object by volume ID
+ *
+ * @return Volume object.
+ */
+ Volume getVolumeById(String volumeId);
}
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Port;
import org.openstack4j.model.network.Subnet;
+import org.openstack4j.model.storage.block.Volume;
+import org.openstack4j.model.storage.block.VolumeBackup;
abstract class OpenstackClientImpl implements OpenstackClient {
@Override
return getClient().networking().subnet().get(subnetId);
}
+ @Override
+ public Volume getVolumeById(String id) {
+ return getClient().blockStorage().volumes().get(id);
+ }
+
/**
* Retrieves the specific client to utilize.
*
import org.openstack4j.model.network.NetworkType;
import org.openstack4j.model.network.Port;
import org.openstack4j.model.network.Subnet;
+import org.openstack4j.model.storage.block.Volume;
+import org.openstack4j.model.storage.block.VolumeAttachment;
import org.openstack4j.openstack.heat.domain.HeatResource;
import org.openstack4j.openstack.heat.domain.HeatResource.Resources;
import org.slf4j.Logger;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
-
@RunWith(MockitoJUnitRunner.Silent.class)
public class HeatBridgeImplTest {
assertEquals(1, images.size());
}
+ @Test
+ public void testBuildAddVolumes() throws HeatBridgeException {
+ List<Resource> stackResources = (List<Resource>) extractTestStackResources();
+ Volume volume = mock(Volume.class);
+ List<VolumeAttachment> attachments = new ArrayList<>();
+ VolumeAttachment server = mock(VolumeAttachment.class);
+ attachments.add(server);
+ when(volume.getAttachments()).thenAnswer(x -> attachments);
+ when(server.getServerId()).thenReturn("vserverIdTest");
+
+ when(osClient.getVolumeById("5ad95036-8daf-4379-a59c-865f35976ca3")).thenReturn(volume);
+
+ heatbridge.buildAddVolumes(stackResources);
+
+ verify(transaction, times(1)).createIfNotExists(
+ eq(AAIUriFactory.createResourceUri(AAIFluentTypeBuilder.cloudInfrastructure()
+ .cloudRegion("CloudOwner", "RegionOne").tenant("7320ec4a5b9d4589ba7c4412ccfd290f")
+ .vserver("vserverIdTest").volume("5ad95036-8daf-4379-a59c-865f35976ca3"))),
+ any(Optional.class));
+ verify(osClient, times(1)).getVolumeById(eq("5ad95036-8daf-4379-a59c-865f35976ca3"));
+ }
+
private List<? extends Resource> extractTestStackResources() {
List<HeatResource> stackResources = null;
try {
}
+
}
"resource_status_reason": "state changed",
"physical_resource_id": "5ad95036-8daf-4379-a59c-865f35976cd4",
"resource_type": "OS::Neutron::Net"
+ },
+ {
+ "resource_name": "volume",
+ "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": "some_id",
+ "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-865f35976ca3",
+ "resource_type": "OS::Cinder::Volume"
}
]
}