package org.ovirt.engine.core.bll.storage.disk.cinder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import org.apache.commons.httpclient.HttpStatus;
import org.ovirt.engine.core.bll.provider.storage.OpenStackVolumeProviderProxy;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.businessentities.storage.CinderConnectionInfo;
import org.ovirt.engine.core.common.businessentities.storage.CinderDisk;
import org.ovirt.engine.core.common.businessentities.storage.CinderVolumeDriver;
import org.ovirt.engine.core.common.businessentities.storage.CinderVolumeStatus;
import org.ovirt.engine.core.common.businessentities.storage.ImageStatus;
import org.ovirt.engine.core.common.businessentities.storage.VolumeClassification;
import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat;
import org.ovirt.engine.core.common.errors.EngineError;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogable;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.woorea.openstack.base.client.OpenStackResponseException;
import com.woorea.openstack.cinder.model.ConnectionForInitialize;
import com.woorea.openstack.cinder.model.Snapshot;
import com.woorea.openstack.cinder.model.SnapshotForCreate;
import com.woorea.openstack.cinder.model.Volume;
import com.woorea.openstack.cinder.model.VolumeForCreate;
import com.woorea.openstack.cinder.model.VolumeForUpdate;
public class CinderBroker {
private static final Logger log = LoggerFactory.getLogger(CinderBroker.class);
public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS";
private OpenStackVolumeProviderProxy proxy;
private ArrayList<String> executeFailedMessages;
public CinderBroker(Guid storageDomainId, ArrayList<String> executeFailedMessages) {
this.proxy = getVolumeProviderProxy(storageDomainId);
this.executeFailedMessages = executeFailedMessages;
}
private <T> T execute(Callable<T> callable) {
try {
return callable.call();
} catch (OpenStackResponseException e) {
executeFailedMessages.add(EngineError.CINDER_ERROR.name());
executeFailedMessages.add(String.format("$cinderException %1$s", e.getMessage()));
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String createDisk(final CinderDisk cinderDisk) {
return execute(() -> {
VolumeForCreate cinderVolume = new VolumeForCreate();
cinderVolume.setName(cinderDisk.getDiskAlias());
cinderVolume.setDescription(cinderDisk.getDiskDescription());
cinderVolume.setSize((int) cinderDisk.getSizeInGigabytes());
cinderVolume.setVolumeType(cinderDisk.getCinderVolumeType());
return proxy.createVolume(cinderVolume);
});
}
public String cloneDisk(final CinderDisk cinderDisk) {
return execute(() -> {
VolumeForCreate cinderVolume = new VolumeForCreate();
cinderVolume.setName(cinderDisk.getDiskAlias());
cinderVolume.setDescription(cinderDisk.getDiskDescription());
cinderVolume.setSize((int) cinderDisk.getSizeInGigabytes());
cinderVolume.setSourceVolid(cinderDisk.getImageId().toString());
return proxy.createVolume(cinderVolume);
});
}
public VolumeClassification deleteVolumeByClassificationType(CinderDisk cinderDisk) {
VolumeClassification cinderVolumeType = cinderDisk.getVolumeClassification();
if (cinderVolumeType == VolumeClassification.Volume) {
deleteVolume(cinderDisk);
} else if (cinderVolumeType == VolumeClassification.Snapshot) {
deleteSnapshot(cinderDisk.getImageId());
} else {
log.error("Error, could not determine Cinder entity {} with id {} from Cinder provider.",
cinderDisk.getDiskAlias(),
cinderDisk.getImageId());
}
return cinderVolumeType;
}
public ImageStatus getImageStatusByClassificationType(CinderDisk cinderDisk) {
VolumeClassification cinderVolumeType = cinderDisk.getVolumeClassification();
if (cinderVolumeType == VolumeClassification.Volume) {
return getDiskStatus(cinderDisk.getImageId());
} else if (cinderVolumeType == VolumeClassification.Snapshot) {
return getSnapshotStatus(cinderDisk.getImageId());
}
log.error("Error, could not determine Cinder entity {} with id {} from Cinder provider.",
cinderDisk.getDiskAlias(),
cinderDisk.getImageId());
return ImageStatus.ILLEGAL;
}
public boolean isVolumeExistsByClassificationType(CinderDisk cinderDisk) {
VolumeClassification cinderVolumeType = cinderDisk.getVolumeClassification();
if (cinderVolumeType == VolumeClassification.Volume) {
return isDiskExist(cinderDisk.getImageId());
} else if (cinderVolumeType == VolumeClassification.Snapshot) {
return isSnapshotExist(cinderDisk.getImageId());
}
log.error("No valid cinder volume type enum has been initialized in the Cinder disk business entity.");
return true;
}
public boolean deleteVolume(final CinderDisk cinderDisk) {
return execute(() -> {
try {
proxy.deleteVolume(cinderDisk.getImageId().toString());
return true;
} catch (OpenStackResponseException ex) {
if (ex.getStatus() == HttpStatus.SC_NOT_FOUND) {
return false;
}
throw ex;
}
});
}
public Void updateDisk(final CinderDisk cinderDisk) {
return execute(() -> {
VolumeForUpdate volumeForUpdate = new VolumeForUpdate();
volumeForUpdate.setName(cinderDisk.getDiskAlias());
volumeForUpdate.setDescription(cinderDisk.getDiskDescription());
proxy.updateVolume(cinderDisk.getImageId().toString(), volumeForUpdate);
return null;
});
}
public Void extendDisk(final CinderDisk cinderDisk, final int newSize) {
return execute(() -> {
proxy.extendVolume(cinderDisk.getImageId().toString(), newSize);
return null;
});
}
public String createSnapshot(final CinderDisk cinderDisk, final String snapshotDescription) {
return execute(() -> {
SnapshotForCreate snapshotForCreate = new SnapshotForCreate();
snapshotForCreate.setVolumeId(cinderDisk.getImageId().toString());
snapshotForCreate.setDescription(snapshotDescription);
return proxy.createSnapshot(snapshotForCreate);
});
}
public boolean deleteSnapshot(final Guid snapshotId) {
return execute(() -> {
try {
proxy.deleteSnapshot(snapshotId.toString());
return true;
} catch (OpenStackResponseException ex) {
if (ex.getStatus() == HttpStatus.SC_NOT_FOUND) {
return false;
}
throw ex;
}
});
}
public String cloneVolumeFromSnapshot(final CinderDisk cinderDisk, final Guid snapshotId) {
return execute(() -> {
VolumeForCreate cinderVolume = new VolumeForCreate();
cinderVolume.setName(cinderDisk.getDiskAlias());
cinderVolume.setDescription(cinderDisk.getDiskDescription());
cinderVolume.setSize((int) cinderDisk.getSizeInGigabytes());
cinderVolume.setVolumeType(cinderDisk.getCinderVolumeType());
cinderVolume.setSnapshotId(snapshotId.toString());
return proxy.cloneVolumeFromSnapshot(cinderVolume);
});
}
public CinderConnectionInfo initializeConnectionForDisk(final CinderDisk cinderDisk) {
return execute(() -> {
ConnectionForInitialize connectionForInitialize = new ConnectionForInitialize();
return proxy.initializeConnectionForVolume(cinderDisk.getImageId().toString(), connectionForInitialize);
});
}
public void updateConnectionInfoForDisk(CinderDisk cinderDisk) {
try {
CinderConnectionInfo connectionInfo = initializeConnectionForDisk(cinderDisk);
CinderVolumeDriver cinderVolumeDriver = CinderVolumeDriver.forValue(connectionInfo.getDriverVolumeType());
if (cinderVolumeDriver == null) {
logDiskEvent(cinderDisk, AuditLogType.CINDER_DISK_CONNECTION_VOLUME_DRIVER_UNSUPPORTED);
}
cinderDisk.setCinderConnectionInfo(connectionInfo);
} catch (OpenStackResponseException ex) {
logDiskEvent(cinderDisk, AuditLogType.CINDER_DISK_CONNECTION_FAILURE);
throw ex;
}
}
private void logDiskEvent(CinderDisk cinderDisk, AuditLogType cinderDiskConnectionFailure) {
AuditLogable logable = new AuditLogableImpl();
logable.addCustomValue("DiskAlias", cinderDisk.getDiskAlias());
new AuditLogDirector().log(logable, cinderDiskConnectionFailure);
}
public boolean isDiskExist(final Guid id) {
return execute(() -> {
try {
Volume volume = proxy.getVolumeById(id.toString());
return volume != null;
} catch (OpenStackResponseException ex) {
if (ex.getStatus() == HttpStatus.SC_NOT_FOUND) {
return false;
}
throw ex;
}
});
}
public boolean isSnapshotExist(final Guid id) {
return execute(() -> {
try {
Snapshot snapshot = proxy.getSnapshotById(id.toString());
return snapshot != null;
} catch (OpenStackResponseException ex) {
if (ex.getStatus() == HttpStatus.SC_NOT_FOUND) {
log.info("Snapshot does not exists");
return false;
} else {
log.error("An error has occurred while looking for snapshot.");
throw ex;
}
}
});
}
public ImageStatus getSnapshotStatus(final Guid id) {
return execute(() -> {
Snapshot snapshot = proxy.getSnapshotById(id.toString());
CinderVolumeStatus cinderVolumeStatus = CinderVolumeStatus.forValue(snapshot.getStatus());
return mapCinderVolumeStatusToImageStatus(cinderVolumeStatus);
});
}
public ImageStatus getDiskStatus(final Guid id) {
return execute(() -> {
Volume volume = proxy.getVolumeById(id.toString());
CinderVolumeStatus cinderVolumeStatus = CinderVolumeStatus.forValue(volume.getStatus());
return mapCinderVolumeStatusToImageStatus(cinderVolumeStatus);
});
}
protected static ImageStatus mapCinderVolumeStatusToImageStatus(CinderVolumeStatus cinderVolumeStatus) {
switch (cinderVolumeStatus) {
case Available:
return ImageStatus.OK;
case Creating:
case Deleting:
case Extending:
return ImageStatus.LOCKED;
case Error:
case ErrorDeleting:
case ErrorExtending:
return ImageStatus.ILLEGAL;
default:
return null;
}
}
public static List<CinderDisk> volumesToCinderDisks(List<Volume> volumes, Guid storageDomainId) {
List<CinderDisk> cinderDisks = new ArrayList<>();
for (Volume volume : volumes) {
cinderDisks.add(volumeToCinderDisk(volume, storageDomainId));
}
return cinderDisks;
}
public static CinderDisk volumeToCinderDisk(Volume volume, Guid storageDomainId) {
CinderDisk cinderDisk = new CinderDisk();
cinderDisk.setId(Guid.createGuidFromString(volume.getId()));
cinderDisk.setImageId(Guid.createGuidFromString(volume.getId()));
cinderDisk.setDiskAlias(volume.getName());
cinderDisk.setDescription(volume.getDescription());
cinderDisk.setSizeInGigabytes(volume.getSize());
cinderDisk.setCinderVolumeType(volume.getVolumeType());
cinderDisk.setStorageIds(new ArrayList<>(Arrays.asList(storageDomainId)));
cinderDisk.setActive(true);
cinderDisk.setImageStatus(ImageStatus.OK);
cinderDisk.setVolumeFormat(VolumeFormat.RAW);
try {
cinderDisk.setCreationDate(new SimpleDateFormat(DATE_FORMAT).parse(volume.getCreatedAt()));
} catch (ParseException e) {
cinderDisk.setCreationDate(null);
log.error("Invalid disk creation date format, id: '{}' (info: {})", volume.getId(), e.getMessage());
}
return cinderDisk;
}
private OpenStackVolumeProviderProxy getVolumeProviderProxy(Guid storageDomainId) {
if (proxy == null) {
proxy = OpenStackVolumeProviderProxy.getFromStorageDomainId(storageDomainId);
}
return proxy;
}
}