package org.zstack.configuration;
import javassist.Modifier;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.stereotype.Component;
import org.zstack.header.configuration.NoPython;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.identity.APISessionMessage;
import org.zstack.header.message.APIListMessage;
import org.zstack.header.message.APIMessage;
import org.zstack.header.query.APIQueryMessage;
import org.zstack.header.search.APIGetMessage;
import org.zstack.header.search.APISearchMessage;
import org.zstack.utils.TypeUtils;
import org.zstack.utils.path.PathUtil;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class PythonApiActionGenerator {
public static void generatePythonApiAction(List<String> basePkgs, String resultFolder) throws IOException {
StringBuilder pysb = new StringBuilder();
pysb.append(String.format("from apibinding import inventory"));
pysb.append(String.format("\nfrom apibinding import api"));
pysb.append(String.format("\nfrom zstacklib.utils import jsonobject"));
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AssignableTypeFilter(APIMessage.class));
scanner.addExcludeFilter(new AnnotationTypeFilter(NoPython.class));
scanner.addExcludeFilter(new AnnotationTypeFilter(Component.class));
for (String pkg : basePkgs) {
List<Class<?>> clazzList = new ArrayList<>();
for (BeanDefinition bd : scanner.findCandidateComponents(pkg)) {
try {
Class<?> clazz = Class.forName(bd.getBeanClassName());
if (Modifier.isAbstract(clazz.getModifiers())) {
continue;
}
if (TypeUtils.isTypeOf(clazz, APISearchMessage.class, APIGetMessage.class, APIListMessage.class)) {
continue;
}
clazzList.add(clazz);
} catch (Exception e) {
throw new CloudRuntimeException(e);
}
}
List<Class<?>> sortedClazzList = clazzList.stream().sorted(
(c1, c2) -> {
return populateActionName(c1).compareTo(populateActionName(c2));
}
).collect(Collectors.toList());
for (Class<?> clazz : sortedClazzList) {
if (APIQueryMessage.class.isAssignableFrom(clazz)) {
generateQueryMsg(pysb, clazz);
} else {
generateApiMsg(pysb, clazz);
}
}
}
String pyStr = pysb.toString();
FileUtils.write(new File(PathUtil.join(resultFolder, "api_actions.py")), pyStr);
}
private static void generateApiMsg(StringBuilder pysb, Class<?> clazz) {
String actionName = populateActionName(clazz);
pysb.append(String.format("\n\nclass %s(inventory.%s):", actionName, clazz.getSimpleName()));
pysb.append(String.format("\n%sdef __init__(self):", whiteSpace(4)));
pysb.append(String.format("\n%ssuper(%s, self).__init__()", whiteSpace(8), actionName));
pysb.append(String.format("\n%sself.sessionUuid = None", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = None", whiteSpace(8)));
pysb.append(String.format("\n%sdef run(self):", whiteSpace(4)));
if (!APISessionMessage.class.isAssignableFrom(clazz)) {
pysb.append(String.format("\n%sif not self.sessionUuid:", whiteSpace(8)));
pysb.append(String.format("\n%sraise Exception('sessionUuid of action[%s] cannot be None')", whiteSpace(12), actionName));
}
pysb.append(String.format("\n%sevt = api.async_call(self, self.sessionUuid)", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = evt", whiteSpace(8)));
pysb.append(String.format("\n%sreturn self.out", whiteSpace(8)));
}
private static void generateSearchMsg(StringBuilder pysb, Class<?> clazz) {
String actionName = populateActionName(clazz);
pysb.append(String.format("\n\nclass %s(inventory.%s):", actionName, clazz.getSimpleName()));
pysb.append(String.format("\n%sdef __init__(self):", whiteSpace(4)));
pysb.append(String.format("\n%ssuper(%s, self).__init__()", whiteSpace(8), actionName));
pysb.append(String.format("\n%sself.sessionUuid = None", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = None", whiteSpace(8)));
pysb.append(String.format("\n%sdef run(self):", whiteSpace(4)));
pysb.append(String.format("\n%sif not self.sessionUuid:", whiteSpace(8)));
pysb.append(String.format("\n%sraise Exception('sessionUuid of action[%s] cannot be None')", whiteSpace(12), actionName));
pysb.append(String.format("\n%sreply = api.sync_call(self, self.sessionUuid)", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = jsonobject.loads(reply.content)", whiteSpace(8)));
pysb.append(String.format("\n%sreturn self.out", whiteSpace(8)));
}
private static void generateQueryMsg(StringBuilder pysb, Class<?> clazz) {
String actionName = populateActionName(clazz);
pysb.append(String.format("\n\nclass %s(inventory.%s):", actionName, clazz.getSimpleName()));
pysb.append(String.format("\n%sdef __init__(self):", whiteSpace(4)));
pysb.append(String.format("\n%ssuper(%s, self).__init__()", whiteSpace(8), actionName));
pysb.append(String.format("\n%sself.sessionUuid = None", whiteSpace(8)));
pysb.append(String.format("\n%sself.reply = None", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = None", whiteSpace(8)));
pysb.append(String.format("\n%sdef run(self):", whiteSpace(4)));
pysb.append(String.format("\n%sif not self.sessionUuid:", whiteSpace(8)));
pysb.append(String.format("\n%sraise Exception('sessionUuid of action[%s] cannot be None')", whiteSpace(12), actionName));
pysb.append(String.format("\n%sreply = api.sync_call(self, self.sessionUuid)", whiteSpace(8)));
pysb.append(String.format("\n%sself.reply = reply", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = reply.inventories", whiteSpace(8)));
pysb.append(String.format("\n%sreturn self.out", whiteSpace(8)));
}
private static void generateGetMsg(StringBuilder pysb, Class<?> clazz) {
String actionName = populateActionName(clazz);
pysb.append(String.format("\n\nclass %s(inventory.%s):", actionName, clazz.getSimpleName()));
pysb.append(String.format("\n%sdef __init__(self):", whiteSpace(4)));
pysb.append(String.format("\n%ssuper(%s, self).__init__()", whiteSpace(8), actionName));
pysb.append(String.format("\n%sself.sessionUuid = None", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = None", whiteSpace(8)));
pysb.append(String.format("\n%sdef run(self):", whiteSpace(4)));
pysb.append(String.format("\n%sif not self.sessionUuid:", whiteSpace(8)));
pysb.append(String.format("\n%sraise Exception('sessionUuid of action[%s] cannot be None')", whiteSpace(12), actionName));
pysb.append(String.format("\n%sreply = api.sync_call(self, self.sessionUuid)", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = jsonobject.loads(reply.inventory)", whiteSpace(8)));
pysb.append(String.format("\n%sreturn self.out", whiteSpace(8)));
}
private static String populateActionName(Class<?> clazz) {
String name = clazz.getSimpleName().replace("API", "").replace("Msg", "");
name = String.format("%sAction", name);
return WordUtils.capitalize(name);
}
private static String whiteSpace(int num) {
return StringUtils.repeat(" ", num);
}
private static void generateListMsg(StringBuilder pysb, Class<?> clazz) {
String actionName = populateActionName(clazz);
pysb.append(String.format("\n\nclass %s(inventory.%s):", actionName, clazz.getSimpleName()));
pysb.append(String.format("\n%sdef __init__(self):", whiteSpace(4)));
pysb.append(String.format("\n%ssuper(%s, self).__init__()", whiteSpace(8), actionName));
pysb.append(String.format("\n%sself.sessionUuid = None", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = None", whiteSpace(8)));
pysb.append(String.format("\n%sdef run(self):", whiteSpace(4)));
pysb.append(String.format("\n%sif not self.sessionUuid:", whiteSpace(8)));
pysb.append(String.format("\n%sraise Exception('sessionUuid of action[%s] cannot be None')", whiteSpace(12), actionName));
pysb.append(String.format("\n%sreply = api.sync_call(self, self.sessionUuid)", whiteSpace(8)));
pysb.append(String.format("\n%sself.out = reply.inventories", whiteSpace(8)));
pysb.append(String.format("\n%sreturn self.out", whiteSpace(8)));
}
}