package org.zstack.storage.ceph;
import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.Q;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.apimediator.ApiMessageInterceptor;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.message.APIMessage;
import org.zstack.storage.ceph.backup.*;
import org.zstack.storage.ceph.primary.*;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.function.Function;
import org.zstack.utils.logging.CLogger;
import org.zstack.utils.network.NetworkUtils;
import static org.zstack.core.Platform.argerr;
import java.util.ArrayList;
import java.util.List;
/**
* Created by frank on 7/29/2015.
*/
public class CephApiInterceptor implements ApiMessageInterceptor {
private static final CLogger logger = Utils.getLogger(CephApiInterceptor.class);
@Autowired
private ErrorFacade errf;
@Autowired
private DatabaseFacade dbf;
private static final String MON_URL_FORMAT = "sshUsername:sshPassword@hostname:[sshPort]/?[monPort=]";
@Override
public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException {
if (msg instanceof APIAddCephBackupStorageMsg) {
validate((APIAddCephBackupStorageMsg) msg);
} else if (msg instanceof APIAddCephPrimaryStorageMsg) {
validate((APIAddCephPrimaryStorageMsg) msg);
} else if (msg instanceof APIAddMonToCephBackupStorageMsg) {
validate((APIAddMonToCephBackupStorageMsg) msg);
} else if (msg instanceof APIUpdateCephBackupStorageMonMsg) {
validate((APIUpdateCephBackupStorageMonMsg) msg);
} else if (msg instanceof APIAddMonToCephPrimaryStorageMsg) {
validate((APIAddMonToCephPrimaryStorageMsg) msg);
} else if (msg instanceof APIUpdateCephPrimaryStorageMonMsg) {
validate((APIUpdateCephPrimaryStorageMonMsg) msg);
} else if (msg instanceof APIDeleteCephPrimaryStoragePoolMsg) {
validate((APIDeleteCephPrimaryStoragePoolMsg) msg);
} else if (msg instanceof APIAddCephPrimaryStoragePoolMsg) {
validate((APIAddCephPrimaryStoragePoolMsg) msg);
}
return msg;
}
private void validate(APIAddCephPrimaryStoragePoolMsg msg) {
if (Q.New(CephPrimaryStoragePoolVO.class)
.eq(CephPrimaryStoragePoolVO_.primaryStorageUuid, msg.getPrimaryStorageUuid())
.eq(CephPrimaryStoragePoolVO_.poolName, msg.getPoolName()).isExists()) {
throw new ApiMessageInterceptionException(argerr("duplicate poolName[%s]. There has been a pool with the same name existing", msg.getPoolName()));
}
}
private void validate(APIDeleteCephPrimaryStoragePoolMsg msg) {
msg.setPrimaryStorageUuid(
Q.New(CephPrimaryStoragePoolVO.class).select(CephPrimaryStoragePoolVO_.primaryStorageUuid)
.eq(CephPrimaryStoragePoolVO_.uuid, msg.getUuid()).findValue()
);
}
private void checkExistingPrimaryStorage(List<String> monUrls) {
List<String> hostnames = CollectionUtils.transformToList(monUrls, new Function<String, String>() {
@Override
public String call(String url) {
MonUri uri = new MonUri(url);
return uri.getHostname();
}
});
SimpleQuery<CephPrimaryStorageMonVO> q = dbf.createQuery(CephPrimaryStorageMonVO.class);
q.select(CephPrimaryStorageMonVO_.hostname);
q.add(CephPrimaryStorageMonVO_.hostname, Op.IN, hostnames);
List<String> existing = q.listValue();
if (!existing.isEmpty()) {
throw new ApiMessageInterceptionException(argerr("cannot add ceph primary storage, there has been some ceph primary storage using mon[hostnames:%s]", existing));
}
}
private void validate(APIAddMonToCephPrimaryStorageMsg msg) {
checkMonUrls(msg.getMonUrls());
}
private void validate(APIAddMonToCephBackupStorageMsg msg) {
checkMonUrls(msg.getMonUrls());
}
private void validate(APIUpdateCephBackupStorageMonMsg msg) {
if (msg.getHostname() != null && !NetworkUtils.isIpv4Address(msg.getHostname()) && !NetworkUtils.isHostname(msg.getHostname())) {
throw new ApiMessageInterceptionException(argerr("hostname[%s] is neither an IPv4 address nor a valid hostname", msg.getHostname()));
}
SimpleQuery<CephBackupStorageMonVO> q = dbf.createQuery(CephBackupStorageMonVO.class);
q.select(CephBackupStorageMonVO_.backupStorageUuid);
q.add(CephPrimaryStorageMonVO_.uuid, Op.EQ, msg.getMonUuid());
String bsUuid = q.findValue();
msg.setBackupStorageUuid(bsUuid);
}
private void validate(APIUpdateCephPrimaryStorageMonMsg msg) {
if (msg.getHostname() != null && !NetworkUtils.isIpv4Address(msg.getHostname()) && !NetworkUtils.isHostname(msg.getHostname())) {
throw new ApiMessageInterceptionException(argerr(
String.format("hostname[%s] is neither an IPv4 address nor a valid hostname", msg.getHostname())
));
}
SimpleQuery<CephPrimaryStorageMonVO> q = dbf.createQuery(CephPrimaryStorageMonVO.class);
q.select(CephPrimaryStorageMonVO_.primaryStorageUuid);
q.add(CephPrimaryStorageMonVO_.uuid, Op.EQ, msg.getMonUuid());
String psUuid = q.findValue();
msg.setPrimaryStorageUuid(psUuid);
}
private void checkMonUrls(List<String> monUrls) {
List<String> urls = new ArrayList<String>();
for (String monUrl : monUrls) {
String url = String.format("ssh://%s", monUrl);
try {
new MonUri(url);
} catch (OperationFailureException ae) {
throw new ApiMessageInterceptionException(ae.getErrorCode());
} catch (Exception e) {
logger.warn(e.getMessage(), e);
throw new ApiMessageInterceptionException(argerr("invalid monUrl[%s]. A valid url is in format of %s", monUrl, MON_URL_FORMAT));
}
}
}
private void validate(APIAddCephPrimaryStorageMsg msg) {
if (msg.getDataVolumePoolName() != null && msg.getDataVolumePoolName().isEmpty()) {
throw new ApiMessageInterceptionException(argerr(
"dataVolumePoolName can be null but cannot be an empty string"
));
}
if (msg.getRootVolumePoolName() != null && msg.getRootVolumePoolName().isEmpty()) {
throw new ApiMessageInterceptionException(argerr(
"rootVolumePoolName can be null but cannot be an empty string"
));
}
if (msg.getImageCachePoolName() != null && msg.getImageCachePoolName().isEmpty()) {
throw new ApiMessageInterceptionException(argerr(
"imageCachePoolName can be null but cannot be an empty string"
));
}
checkMonUrls(msg.getMonUrls());
checkExistingPrimaryStorage(msg.getMonUrls());
}
private void checkExistingBackupStorage(List<String> monUrls) {
List<String> hostnames = CollectionUtils.transformToList(monUrls, new Function<String, String>() {
@Override
public String call(String url) {
MonUri uri = new MonUri(url);
return uri.getHostname();
}
});
SimpleQuery<CephBackupStorageMonVO> q = dbf.createQuery(CephBackupStorageMonVO.class);
q.select(CephBackupStorageMonVO_.hostname);
q.add(CephBackupStorageMonVO_.hostname, Op.IN, hostnames);
List<String> existing = q.listValue();
if (!existing.isEmpty()) {
throw new ApiMessageInterceptionException(argerr("cannot add ceph backup storage, there has been some ceph backup storage using mon[hostnames:%s]", existing));
}
}
private void validate(APIAddCephBackupStorageMsg msg) {
if (msg.getPoolName() != null && msg.getPoolName().isEmpty()) {
throw new ApiMessageInterceptionException(argerr("poolName can be null but cannot be an empty string"));
}else if(msg.isImportImages() && msg.getPoolName() == null){
throw new ApiMessageInterceptionException(argerr("poolName is required when importImages is true"));
}
checkMonUrls(msg.getMonUrls());
checkExistingBackupStorage(msg.getMonUrls());
}
}