package com.sequenceiq.cloudbreak.cloud.openstack.nativ.compute; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.codec.binary.Base64; import org.openstack4j.api.Builders; import org.openstack4j.api.OSClient; import org.openstack4j.api.exceptions.OS4JException; import org.openstack4j.model.compute.Action; import org.openstack4j.model.compute.ActionResponse; import org.openstack4j.model.compute.BDMDestType; import org.openstack4j.model.compute.BDMSourceType; import org.openstack4j.model.compute.BlockDeviceMappingCreate; import org.openstack4j.model.compute.Flavor; import org.openstack4j.model.compute.Server; import org.openstack4j.model.compute.ServerCreate; import org.openstack4j.model.compute.builder.BlockDeviceMappingBuilder; import org.openstack4j.model.compute.builder.ServerCreateBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.google.common.collect.Lists; import com.sequenceiq.cloudbreak.cloud.context.AuthenticatedContext; import com.sequenceiq.cloudbreak.cloud.context.CloudContext; import com.sequenceiq.cloudbreak.cloud.model.CloudInstance; import com.sequenceiq.cloudbreak.cloud.model.CloudResource; import com.sequenceiq.cloudbreak.cloud.model.CloudVmInstanceStatus; import com.sequenceiq.cloudbreak.cloud.model.Group; import com.sequenceiq.cloudbreak.cloud.model.Image; import com.sequenceiq.cloudbreak.cloud.model.InstanceStatus; import com.sequenceiq.cloudbreak.cloud.model.InstanceTemplate; import com.sequenceiq.cloudbreak.cloud.openstack.common.OpenStackConstants; import com.sequenceiq.cloudbreak.cloud.openstack.nativ.OpenStackResourceException; import com.sequenceiq.cloudbreak.cloud.openstack.nativ.context.OpenStackContext; import com.sequenceiq.cloudbreak.cloud.openstack.status.NovaInstanceStatus; import com.sequenceiq.cloudbreak.cloud.openstack.view.KeystoneCredentialView; import com.sequenceiq.cloudbreak.cloud.openstack.view.NovaInstanceView; import com.sequenceiq.cloudbreak.common.type.ResourceType; @Service public class OpenStackInstanceBuilder extends AbstractOpenStackComputeResourceBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(OpenStackInstanceBuilder.class); @Override public List<CloudResource> build(OpenStackContext context, long privateId, AuthenticatedContext auth, Group group, Image image, List<CloudResource> buildableResource, Map<String, String> tags) throws Exception { CloudResource resource = buildableResource.get(0); try { OSClient osClient = createOSClient(auth); InstanceTemplate template = getInstanceTemplate(group, privateId); CloudResource port = getPort(context.getComputeResources(privateId)); KeystoneCredentialView osCredential = new KeystoneCredentialView(auth); NovaInstanceView novaInstanceView = new NovaInstanceView(context.getName(), template, group.getType()); String imageId = osClient.images().list(Collections.singletonMap("name", image.getImageName())).get(0).getId(); Map<String, String> metadata = mergeMetadata(novaInstanceView.getMetadataMap(), tags); ServerCreateBuilder serverCreateBuilder = Builders.server() .name(resource.getName()) .image(imageId) .flavor(getFlavorId(osClient, novaInstanceView.getFlavor())) .keypairName(osCredential.getKeyPairName()) .addMetadata(metadata) .addNetworkPort(port.getStringParameter(OpenStackConstants.PORT_ID)) .userData(new String(Base64.encodeBase64(image.getUserData(group.getType()).getBytes()))); BlockDeviceMappingBuilder blockDeviceMappingBuilder = Builders.blockDeviceMapping() .uuid(imageId) .sourceType(BDMSourceType.IMAGE) .deviceName("/dev/vda") .bootIndex(0) .deleteOnTermination(true) .destinationType(BDMDestType.LOCAL); serverCreateBuilder = serverCreateBuilder.blockDevice(blockDeviceMappingBuilder.build()); for (CloudResource computeResource : context.getComputeResources(privateId)) { if (computeResource.getType() == ResourceType.OPENSTACK_ATTACHED_DISK) { BlockDeviceMappingCreate blockDeviceMappingCreate = Builders.blockDeviceMapping() .uuid(computeResource.getReference()) .deviceName(computeResource.getStringParameter(OpenStackConstants.VOLUME_MOUNT_POINT)) .sourceType(BDMSourceType.VOLUME) .destinationType(BDMDestType.VOLUME) .build(); serverCreateBuilder.blockDevice(blockDeviceMappingCreate); } } ServerCreate serverCreate = serverCreateBuilder.build(); Server server = osClient.compute().servers().boot(serverCreate); return Collections.singletonList(createPersistedResource(resource, group.getName(), server.getId(), Collections.singletonMap(OpenStackConstants.SERVER, server))); } catch (OS4JException ex) { LOGGER.error("Failed to create OpenStack instance with privateId: {}", privateId, ex); throw new OpenStackResourceException("Instance creation failed", resourceType(), resource.getName(), ex); } } private Map<String, String> mergeMetadata(Map<String, String> metadataMap, Map<String, String> tags) { Map<String, String> result = new HashMap<>(); result.putAll(tags); result.putAll(metadataMap); return result; } @Override public List<CloudVmInstanceStatus> checkInstances(OpenStackContext context, AuthenticatedContext auth, List<CloudInstance> instances) { List<CloudVmInstanceStatus> statuses = Lists.newArrayList(); OSClient osClient = createOSClient(auth); for (CloudInstance instance : instances) { Server server = osClient.compute().servers().get(instance.getInstanceId()); if (server == null) { statuses.add(new CloudVmInstanceStatus(instance, InstanceStatus.TERMINATED)); } else { statuses.add(new CloudVmInstanceStatus(instance, NovaInstanceStatus.get(server))); } } return statuses; } @Override public CloudResource delete(OpenStackContext context, AuthenticatedContext auth, CloudResource resource) throws Exception { try { OSClient osClient = createOSClient(auth); ActionResponse response = osClient.compute().servers().delete(resource.getReference()); return checkDeleteResponse(response, resourceType(), auth, resource, "Instance deletion failed"); } catch (OS4JException ex) { throw new OpenStackResourceException("Instance deletion failed", resourceType(), resource.getName(), ex); } } @Override public CloudVmInstanceStatus stop(OpenStackContext context, AuthenticatedContext auth, CloudInstance instance) { return executeAction(auth, instance, Action.STOP); } @Override public CloudVmInstanceStatus start(OpenStackContext context, AuthenticatedContext auth, CloudInstance instance) { return executeAction(auth, instance, Action.START); } private CloudVmInstanceStatus executeAction(AuthenticatedContext auth, CloudInstance instance, Action action) { OSClient osClient = createOSClient(auth); ActionResponse actionResponse = osClient.compute().servers().action(instance.getInstanceId(), action); if (actionResponse.isSuccess()) { return new CloudVmInstanceStatus(instance, InstanceStatus.IN_PROGRESS); } return new CloudVmInstanceStatus(instance, InstanceStatus.FAILED, actionResponse.getFault()); } @Override public ResourceType resourceType() { return ResourceType.OPENSTACK_INSTANCE; } @Override protected boolean checkStatus(OpenStackContext context, AuthenticatedContext auth, CloudResource resource) { Server.Status status = getStatus(auth, resource.getReference()); if (status != null && context.isBuild()) { if (Server.Status.ERROR == status) { CloudContext cloudContext = auth.getCloudContext(); throw new OpenStackResourceException("Instance in failed state", resource.getType(), resource.getName(), cloudContext.getId(), status.name()); } return status == Server.Status.ACTIVE; } else if (status == null && !context.isBuild()) { return true; } return false; } private Server.Status getStatus(AuthenticatedContext auth, String serverId) { OSClient osClient = createOSClient(auth); Server server = osClient.compute().servers().get(serverId); Server.Status status = null; if (server != null) { status = server.getStatus(); } return status; } private CloudResource getPort(List<CloudResource> computeResources) { CloudResource instance = null; for (CloudResource computeResource : computeResources) { if (computeResource.getType() == ResourceType.OPENSTACK_PORT) { instance = computeResource; } } return instance; } private String getFlavorId(OSClient osClient, String flavorName) { List<? extends Flavor> flavors = osClient.compute().flavors().list(); for (Flavor flavor : flavors) { if (flavor.getName().equals(flavorName)) { return flavor.getId(); } } return null; } }