package com.sequenceiq.cloudbreak.cloud.openstack.nativ.compute;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import javax.inject.Inject;
import org.openstack4j.api.Builders;
import org.openstack4j.api.OSClient;
import org.openstack4j.api.exceptions.OS4JException;
import org.openstack4j.model.compute.ActionResponse;
import org.openstack4j.model.storage.block.Volume;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Service;
import com.sequenceiq.cloudbreak.cloud.context.AuthenticatedContext;
import com.sequenceiq.cloudbreak.cloud.context.CloudContext;
import com.sequenceiq.cloudbreak.cloud.model.CloudResource;
import com.sequenceiq.cloudbreak.cloud.model.Group;
import com.sequenceiq.cloudbreak.cloud.model.Image;
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.nativ.service.OpenStackResourceNameService;
import com.sequenceiq.cloudbreak.cloud.openstack.view.CinderVolumeView;
import com.sequenceiq.cloudbreak.cloud.openstack.view.NovaInstanceView;
import com.sequenceiq.cloudbreak.common.type.ResourceType;
@Service
public class OpenStackAttachedDiskResourceBuilder extends AbstractOpenStackComputeResourceBuilder {
private static final String VOLUME_VIEW = "volumeView";
@Inject
private OpenStackResourceNameService resourceNameService;
@Inject
@Qualifier("intermediateBuilderExecutor")
private AsyncTaskExecutor intermediateBuilderExecutor;
@Override
public List<CloudResource> create(OpenStackContext context, long privateId, AuthenticatedContext auth, Group group, Image image) {
List<CloudResource> cloudResources = new ArrayList<>();
InstanceTemplate template = getInstanceTemplate(group, privateId);
NovaInstanceView instanceView = new NovaInstanceView(context.getName(), template, group.getType());
String groupName = group.getName();
final String stackName = getUtils().getStackName(auth);
for (int i = 0; i < instanceView.getVolumes().size(); i++) {
final String resourceName = resourceNameService.resourceName(resourceType(), stackName, groupName, privateId, i);
CloudResource resource = createNamedResource(resourceType(), groupName, resourceName);
resource.putParameter(VOLUME_VIEW, instanceView.getVolumes().get(i));
cloudResources.add(resource);
}
return cloudResources;
}
@Override
public List<CloudResource> build(OpenStackContext context, long privateId, final AuthenticatedContext auth, Group group, Image image,
List<CloudResource> buildableResource, Map<String, String> tags) throws Exception {
List<CloudResource> resources = new ArrayList<>();
final List<CloudResource> syncedResources = Collections.synchronizedList(resources);
List<Future<Void>> futures = new ArrayList<>();
for (final CloudResource cloudResource : buildableResource) {
Future<Void> submit = intermediateBuilderExecutor.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
CinderVolumeView volumeView = cloudResource.getParameter(VOLUME_VIEW, CinderVolumeView.class);
Volume osVolume = Builders.volume().name(cloudResource.getName())
.size(volumeView.getSize()).build();
try {
final OSClient osClient = createOSClient(auth);
osVolume = osClient.blockStorage().volumes().create(osVolume);
CloudResource newRes = createPersistedResource(cloudResource, group.getName(), osVolume.getId());
newRes.putParameter(OpenStackConstants.VOLUME_MOUNT_POINT, volumeView.getDevice());
syncedResources.add(newRes);
} catch (OS4JException ex) {
throw new OpenStackResourceException("Volume creation failed", resourceType(), cloudResource.getName(), ex);
}
return null;
}
});
futures.add(submit);
}
for (Future<Void> future : futures) {
future.get();
}
return resources;
}
@Override
public CloudResource delete(OpenStackContext context, AuthenticatedContext auth, CloudResource resource) throws Exception {
try {
OSClient osClient = createOSClient(auth);
ActionResponse response = osClient.blockStorage().volumes().delete(resource.getReference());
return checkDeleteResponse(response, resourceType(), auth, resource, "Volume deletion failed");
} catch (OS4JException ex) {
throw new OpenStackResourceException("Volume deletion failed", resourceType(), resource.getName(), ex);
}
}
@Override
public ResourceType resourceType() {
return ResourceType.OPENSTACK_ATTACHED_DISK;
}
protected boolean checkStatus(OpenStackContext context, AuthenticatedContext auth, CloudResource resource) {
CloudContext cloudContext = auth.getCloudContext();
OSClient osClient = createOSClient(auth);
Volume osVolume = osClient.blockStorage().volumes().get(resource.getReference());
if (osVolume != null && context.isBuild()) {
Volume.Status volumeStatus = osVolume.getStatus();
if (Volume.Status.ERROR == volumeStatus || Volume.Status.ERROR_DELETING == volumeStatus
|| Volume.Status.ERROR_RESTORING == osVolume.getStatus()) {
throw new OpenStackResourceException("Volume in failed state", resource.getType(), resource.getName(), cloudContext.getId(),
volumeStatus.name());
}
return volumeStatus == Volume.Status.AVAILABLE;
} else if (osVolume == null && !context.isBuild()) {
return true;
}
return false;
}
}