package org.zstack.storage.fusionstor;
import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.db.DatabaseFacade;
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.fusionstor.backup.APIAddFusionstorBackupStorageMsg;
import org.zstack.storage.fusionstor.backup.APIAddMonToFusionstorBackupStorageMsg;
import org.zstack.storage.fusionstor.backup.APIUpdateFusionstorBackupStorageMonMsg;
import org.zstack.storage.fusionstor.backup.FusionstorBackupStorageMonVO;
import org.zstack.storage.fusionstor.backup.FusionstorBackupStorageMonVO_;
import org.zstack.storage.fusionstor.primary.APIAddFusionstorPrimaryStorageMsg;
import org.zstack.storage.fusionstor.primary.APIAddMonToFusionstorPrimaryStorageMsg;
import org.zstack.storage.fusionstor.primary.APIUpdateFusionstorPrimaryStorageMonMsg;
import org.zstack.storage.fusionstor.primary.FusionstorPrimaryStorageMonVO;
import org.zstack.storage.fusionstor.primary.FusionstorPrimaryStorageMonVO_;
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 java.util.ArrayList;
import java.util.List;
/**
* Created by frank on 7/29/2015.
*/
public class FusionstorApiInterceptor implements ApiMessageInterceptor {
private static final CLogger logger = Utils.getLogger(FusionstorApiInterceptor.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 APIAddFusionstorBackupStorageMsg) {
validate((APIAddFusionstorBackupStorageMsg) msg);
} else if (msg instanceof APIAddFusionstorPrimaryStorageMsg) {
validate((APIAddFusionstorPrimaryStorageMsg) msg);
} else if (msg instanceof APIAddMonToFusionstorBackupStorageMsg) {
validate((APIAddMonToFusionstorBackupStorageMsg) msg);
} else if (msg instanceof APIAddMonToFusionstorPrimaryStorageMsg) {
validate((APIAddMonToFusionstorPrimaryStorageMsg) msg);
} else if (msg instanceof APIUpdateFusionstorPrimaryStorageMonMsg) {
validate((APIUpdateFusionstorPrimaryStorageMonMsg) msg);
} else if (msg instanceof APIUpdateFusionstorBackupStorageMonMsg) {
validate((APIUpdateFusionstorBackupStorageMonMsg) msg);
}
return msg;
}
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<FusionstorPrimaryStorageMonVO> q = dbf.createQuery(FusionstorPrimaryStorageMonVO.class);
q.select(FusionstorPrimaryStorageMonVO_.hostname);
q.add(FusionstorPrimaryStorageMonVO_.hostname, Op.IN, hostnames);
List<String> existing = q.listValue();
if (!existing.isEmpty()) {
throw new ApiMessageInterceptionException(errf.stringToInvalidArgumentError(
String.format("cannot add fusionstor primary storage, there has been some fusionstor primary storage using mon[hostnames:%s]", existing)
));
}
}
private void validate(APIAddMonToFusionstorPrimaryStorageMsg msg) {
checkMonUrls(msg.getMonUrls());
}
private void validate(APIAddMonToFusionstorBackupStorageMsg msg) {
checkMonUrls(msg.getMonUrls());
}
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(errf.stringToInvalidArgumentError(
String.format("invalid monUrl[%s]. A valid url is in format of %s", monUrl, MON_URL_FORMAT)
));
}
}
}
private void validate(APIAddFusionstorPrimaryStorageMsg msg) {
if (msg.getDataVolumePoolName() != null && msg.getDataVolumePoolName().isEmpty()) {
throw new ApiMessageInterceptionException(errf.stringToInvalidArgumentError(
"dataVolumePoolName can be null but cannot be an empty string"
));
}
if (msg.getRootVolumePoolName() != null && msg.getRootVolumePoolName().isEmpty()) {
throw new ApiMessageInterceptionException(errf.stringToInvalidArgumentError(
"rootVolumePoolName can be null but cannot be an empty string"
));
}
if (msg.getImageCachePoolName() != null && msg.getImageCachePoolName().isEmpty()) {
throw new ApiMessageInterceptionException(errf.stringToInvalidArgumentError(
"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<FusionstorBackupStorageMonVO> q = dbf.createQuery(FusionstorBackupStorageMonVO.class);
q.select(FusionstorBackupStorageMonVO_.hostname);
q.add(FusionstorBackupStorageMonVO_.hostname, Op.IN, hostnames);
List<String> existing = q.listValue();
if (!existing.isEmpty()) {
throw new ApiMessageInterceptionException(errf.stringToInvalidArgumentError(
String.format("cannot add fusionstor backup storage, there has been some fusionstor backup storage using mon[hostnames:%s]", existing)
));
}
}
private void validate(APIAddFusionstorBackupStorageMsg msg) {
if (msg.getPoolName() != null && msg.getPoolName().isEmpty()) {
throw new ApiMessageInterceptionException(errf.stringToInvalidArgumentError(
"poolName can be null but cannot be an empty string"
));
}
checkMonUrls(msg.getMonUrls());
checkExistingBackupStorage(msg.getMonUrls());
}
private void validate(APIUpdateFusionstorBackupStorageMonMsg msg) {
if (msg.getHostname() != null && !NetworkUtils.isIpv4Address(msg.getHostname()) && !NetworkUtils.isHostname(msg.getHostname())) {
throw new ApiMessageInterceptionException(errf.stringToInvalidArgumentError(
String.format("hostname[%s] is neither an IPv4 address nor a valid hostname", msg.getHostname())
));
}
SimpleQuery<FusionstorBackupStorageMonVO> q = dbf.createQuery(FusionstorBackupStorageMonVO.class);
q.select(FusionstorBackupStorageMonVO_.backupStorageUuid);
q.add(FusionstorBackupStorageMonVO_.uuid, Op.EQ, msg.getMonUuid());
String bsUuid = q.findValue();
msg.setBackupStorageUuid(bsUuid);
}
private void validate(APIUpdateFusionstorPrimaryStorageMonMsg msg) {
if (msg.getHostname() != null && !NetworkUtils.isIpv4Address(msg.getHostname()) && !NetworkUtils.isHostname(msg.getHostname())) {
throw new ApiMessageInterceptionException(errf.stringToInvalidArgumentError(
String.format("hostname[%s] is neither an IPv4 address nor a valid hostname", msg.getHostname())
));
}
SimpleQuery<FusionstorPrimaryStorageMonVO> q = dbf.createQuery(FusionstorPrimaryStorageMonVO.class);
q.select(FusionstorPrimaryStorageMonVO_.primaryStorageUuid);
q.add(FusionstorPrimaryStorageMonVO_.uuid, Op.EQ, msg.getMonUuid());
String psUuid = q.findValue();
msg.setPrimaryStorageUuid(psUuid);
}
}