// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.api.doc; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.TreeMap; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.log4j.Logger; import com.cloud.alert.AlertManager; import com.cloud.api.BaseAsyncCmd; import com.cloud.api.BaseAsyncCreateCmd; import com.cloud.api.BaseCmd; import com.cloud.api.Implementation; import com.cloud.api.Parameter; import com.cloud.api.response.AsyncJobResponse; import com.cloud.api.response.BaseResponse; import com.cloud.api.response.HostResponse; import com.cloud.api.response.IPAddressResponse; import com.cloud.api.response.LoadBalancerResponse; import com.cloud.api.response.SecurityGroupResponse; import com.cloud.api.response.SnapshotResponse; import com.cloud.api.response.StoragePoolResponse; import com.cloud.api.response.TemplateResponse; import com.cloud.api.response.UserVmResponse; import com.cloud.api.response.VolumeResponse; import com.cloud.serializer.Param; import com.cloud.server.api.response.ExternalLoadBalancerResponse; import com.google.gson.annotations.SerializedName; import com.thoughtworks.xstream.XStream; public class ApiXmlDocWriter { public static final Logger s_logger = Logger.getLogger(ApiXmlDocWriter.class.getName()); private static final short DOMAIN_ADMIN_COMMAND = 4; private static final short USER_COMMAND = 8; private static LinkedHashMap<Object, String> all_api_commands = new LinkedHashMap<Object, String>(); private static LinkedHashMap<Object, String> domain_admin_api_commands = new LinkedHashMap<Object, String>(); private static LinkedHashMap<Object, String> regular_user_api_commands = new LinkedHashMap<Object, String>(); private static TreeMap<Object, String> all_api_commands_sorted = new TreeMap<Object, String>(); private static TreeMap<Object, String> domain_admin_api_commands_sorted = new TreeMap<Object, String>(); private static TreeMap<Object, String> regular_user_api_commands_sorted = new TreeMap<Object, String>(); private static String dirName = ""; private static final List<String> _asyncResponses = setAsyncResponses(); private static List<String> setAsyncResponses() { List<String> asyncResponses = new ArrayList<String>(); asyncResponses.add(TemplateResponse.class.getName()); asyncResponses.add(VolumeResponse.class.getName()); //asyncResponses.add(LoadBalancerResponse.class.getName()); asyncResponses.add(HostResponse.class.getName()); asyncResponses.add(IPAddressResponse.class.getName()); asyncResponses.add(StoragePoolResponse.class.getName()); asyncResponses.add(UserVmResponse.class.getName()); asyncResponses.add(SecurityGroupResponse.class.getName()); //asyncResponses.add(ExternalLoadBalancerResponse.class.getName()); asyncResponses.add(SnapshotResponse.class.getName()); return asyncResponses; } public static void main(String[] args) { LinkedProperties preProcessedCommands = new LinkedProperties(); String[] fileNames = null; List<String> argsList = Arrays.asList(args); Iterator<String> iter = argsList.iterator(); while (iter.hasNext()) { String arg = iter.next(); // populate the file names if (arg.equals("-f")) { fileNames = iter.next().split(","); } if (arg.equals("-d")) { dirName = iter.next(); } } if ((fileNames == null) || (fileNames.length == 0)) { System.out.println("Please specify input file(s) separated by coma using -f option"); System.exit(2); } for (String fileName : fileNames) { try { FileInputStream in = new FileInputStream(fileName); preProcessedCommands.load(in); } catch (FileNotFoundException ex) { System.out.println("Can't find file " + fileName); System.exit(2); } catch (IOException ex1) { System.out.println("Error reading from file " + ex1); System.exit(2); } } Iterator<?> propertiesIterator = preProcessedCommands.keys.iterator(); // Get command classes and response object classes while (propertiesIterator.hasNext()) { String key = (String) propertiesIterator.next(); String preProcessedCommand = preProcessedCommands.getProperty(key); String[] commandParts = preProcessedCommand.split(";"); String commandName = commandParts[0]; all_api_commands.put(key, commandName); short cmdPermissions = 1; if (commandParts.length > 1 && commandParts[1] != null) { cmdPermissions = Short.parseShort(commandParts[1]); } if ((cmdPermissions & DOMAIN_ADMIN_COMMAND) != 0) { domain_admin_api_commands.put(key, commandName); } if ((cmdPermissions & USER_COMMAND) != 0) { regular_user_api_commands.put(key, commandName); } } // Login and logout commands are hardcoded all_api_commands.put("login", "login"); domain_admin_api_commands.put("login", "login"); regular_user_api_commands.put("login", "login"); all_api_commands.put("logout", "logout"); domain_admin_api_commands.put("logout", "logout"); regular_user_api_commands.put("logout", "logout"); all_api_commands_sorted.putAll(all_api_commands); domain_admin_api_commands_sorted.putAll(domain_admin_api_commands); regular_user_api_commands_sorted.putAll(regular_user_api_commands); try { // Create object writer XStream xs = new XStream(); xs.alias("command", Command.class); xs.alias("arg", Argument.class); String xmlDocDir = dirName + "/xmldoc"; String rootAdminDirName = xmlDocDir + "/root_admin"; String domainAdminDirName = xmlDocDir + "/domain_admin"; String regularUserDirName = xmlDocDir + "/regular_user"; (new File(rootAdminDirName)).mkdirs(); (new File(domainAdminDirName)).mkdirs(); (new File(regularUserDirName)).mkdirs(); ObjectOutputStream out = xs.createObjectOutputStream(new FileWriter(dirName + "/commands.xml"), "commands"); ObjectOutputStream rootAdmin = xs.createObjectOutputStream(new FileWriter(rootAdminDirName + "/" + "rootAdminSummary.xml"), "commands"); ObjectOutputStream rootAdminSorted = xs.createObjectOutputStream(new FileWriter(rootAdminDirName + "/" + "rootAdminSummarySorted.xml"), "commands"); ObjectOutputStream domainAdmin = xs.createObjectOutputStream(new FileWriter(domainAdminDirName + "/" + "domainAdminSummary.xml"), "commands"); ObjectOutputStream outDomainAdminSorted = xs.createObjectOutputStream(new FileWriter(domainAdminDirName + "/" + "domainAdminSummarySorted.xml"), "commands"); ObjectOutputStream regularUser = xs.createObjectOutputStream(new FileWriter(regularUserDirName + "/regularUserSummary.xml"), "commands"); ObjectOutputStream regularUserSorted = xs.createObjectOutputStream(new FileWriter(regularUserDirName + "/regularUserSummarySorted.xml"), "commands"); // Write commands in the order they are represented in commands.properties.in file Iterator<?> it = all_api_commands.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); // Write admin commands if (key.equals("login")) { writeLoginCommand(out); writeLoginCommand(rootAdmin); writeLoginCommand(domainAdmin); writeLoginCommand(regularUser); ObjectOutputStream singleRootAdminCommandOs = xs.createObjectOutputStream(new FileWriter(rootAdminDirName + "/" + "login" + ".xml"), "command"); writeLoginCommand(singleRootAdminCommandOs); singleRootAdminCommandOs.close(); ObjectOutputStream singleDomainAdminCommandOs = xs.createObjectOutputStream(new FileWriter(domainAdminDirName + "/" + "login" + ".xml"), "command"); writeLoginCommand(singleDomainAdminCommandOs); singleDomainAdminCommandOs.close(); ObjectOutputStream singleRegularUserCommandOs = xs.createObjectOutputStream(new FileWriter(regularUserDirName + "/" + "login" + ".xml"), "command"); writeLoginCommand(singleRegularUserCommandOs); singleRegularUserCommandOs.close(); } else if (key.equals("logout")) { writeLogoutCommand(out); writeLogoutCommand(rootAdmin); writeLogoutCommand(domainAdmin); writeLogoutCommand(regularUser); ObjectOutputStream singleRootAdminCommandOs = xs.createObjectOutputStream(new FileWriter(rootAdminDirName + "/" + "logout" + ".xml"), "command"); writeLogoutCommand(singleRootAdminCommandOs); singleRootAdminCommandOs.close(); ObjectOutputStream singleDomainAdminCommandOs = xs.createObjectOutputStream(new FileWriter(domainAdminDirName + "/" + "logout" + ".xml"), "command"); writeLogoutCommand(singleDomainAdminCommandOs); singleDomainAdminCommandOs.close(); ObjectOutputStream singleRegularUserCommandOs = xs.createObjectOutputStream(new FileWriter(regularUserDirName + "/" + "logout" + ".xml"), "command"); writeLogoutCommand(singleRegularUserCommandOs); singleRegularUserCommandOs.close(); } else { writeCommand(out, key); writeCommand(rootAdmin, key); // Write single commands to separate xml files if (!key.equals("login")) { ObjectOutputStream singleRootAdminCommandOs = xs.createObjectOutputStream(new FileWriter(rootAdminDirName + "/" + key + ".xml"), "command"); writeCommand(singleRootAdminCommandOs, key); singleRootAdminCommandOs.close(); } if (domain_admin_api_commands.containsKey(key)) { writeCommand(domainAdmin, key); ObjectOutputStream singleDomainAdminCommandOs = xs.createObjectOutputStream(new FileWriter(domainAdminDirName + "/" + key + ".xml"), "command"); writeCommand(singleDomainAdminCommandOs, key); singleDomainAdminCommandOs.close(); } if (regular_user_api_commands.containsKey(key)) { writeCommand(regularUser, key); ObjectOutputStream singleRegularUserCommandOs = xs.createObjectOutputStream(new FileWriter(regularUserDirName + "/" + key + ".xml"), "command"); writeCommand(singleRegularUserCommandOs, key); singleRegularUserCommandOs.close(); } } } // Write sorted commands it = all_api_commands_sorted.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); if (key.equals("login")) { writeLoginCommand(rootAdminSorted); writeLoginCommand(outDomainAdminSorted); writeLoginCommand(regularUserSorted); } else if (key.equals("logout")) { writeLogoutCommand(rootAdminSorted); writeLogoutCommand(outDomainAdminSorted); writeLogoutCommand(regularUserSorted); } else { writeCommand(rootAdminSorted, key); if (domain_admin_api_commands.containsKey(key)) { writeCommand(outDomainAdminSorted, key); } if (regular_user_api_commands.containsKey(key)) { writeCommand(regularUserSorted, key); } } } out.close(); rootAdmin.close(); rootAdminSorted.close(); domainAdmin.close(); outDomainAdminSorted.close(); regularUser.close(); regularUserSorted.close(); // write alerttypes to xml writeAlertTypes(xmlDocDir); // gzip directory with xml doc // zipDir(dirName + "xmldoc.zip", xmlDocDir); // Delete directory // deleteDir(new File(xmlDocDir)); } catch (Exception ex) { ex.printStackTrace(); System.exit(2); } } private static void writeCommand(ObjectOutputStream out, String command) throws ClassNotFoundException, IOException { Class<?> clas = Class.forName(all_api_commands.get(command)); ArrayList<Argument> request = new ArrayList<Argument>(); ArrayList<Argument> response = new ArrayList<Argument>(); // Create a new command, set name/description/usage Command apiCommand = new Command(); apiCommand.setName(command); Implementation impl = clas.getAnnotation(Implementation.class); if (impl == null) { impl = clas.getSuperclass().getAnnotation(Implementation.class); } if (impl.includeInApiDoc()) { String commandDescription = impl.description(); if (commandDescription != null && !commandDescription.isEmpty()) { apiCommand.setDescription(commandDescription); } else { System.out.println("Command " + apiCommand.getName() + " misses description"); } String commandUsage = impl.usage(); if (commandUsage != null && !commandUsage.isEmpty()) { apiCommand.setUsage(commandUsage); } //Set version when the API is added if(!impl.since().isEmpty()){ apiCommand.setSinceVersion(impl.since()); } // Set request parameters Field[] fields = clas.getDeclaredFields(); // Get fields from superclass Class<?> superClass = clas.getSuperclass(); boolean isAsync = false; while (superClass != null && superClass != Object.class) { String superName = superClass.getName(); if (!superName.equals(BaseCmd.class.getName()) && !superName.equals(BaseAsyncCmd.class.getName()) && !superName.equals(BaseAsyncCreateCmd.class.getName())) { Field[] superClassFields = superClass.getDeclaredFields(); if (superClassFields != null) { Field[] tmpFields = new Field[fields.length + superClassFields.length]; System.arraycopy(fields, 0, tmpFields, 0, fields.length); System.arraycopy(superClassFields, 0, tmpFields, fields.length, superClassFields.length); fields = tmpFields; } } superClass = superClass.getSuperclass(); // Set Async information for the command if (superName.equals(BaseAsyncCmd.class.getName()) || superName.equals(BaseAsyncCreateCmd.class.getName())) { isAsync = true; } } apiCommand.setAsync(isAsync); request = setRequestFields(fields); // Get response parameters Class<?> responseClas = impl.responseObject(); Field[] responseFields = responseClas.getDeclaredFields(); response = setResponseFields(responseFields, responseClas); apiCommand.setRequest(request); apiCommand.setResponse(response); out.writeObject(apiCommand); } else { s_logger.debug("Command " + command + " is not exposed in api doc"); } } private static void writeLoginCommand(ObjectOutputStream out) throws ClassNotFoundException, IOException { ArrayList<Argument> request = new ArrayList<Argument>(); ArrayList<Argument> response = new ArrayList<Argument>(); // Create a new command, set name and description Command apiCommand = new Command(); apiCommand.setName("login"); apiCommand .setDescription("Logs a user into the CloudStack. A successful login attempt will generate a JSESSIONID cookie value that can be passed in subsequent Query command calls until the \"logout\" command has been issued or the session has expired."); // Generate request request.add(new Argument("username", "Username", true)); request.add(new Argument("password", "Hashed password (Default is MD5). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter See Docs section.", true)); request.add(new Argument("domain", "path of the domain that the user belongs to. Example: domain=/com/cloud/internal. If no domain is passed in, the ROOT domain is assumed.", false)); request.add(new Argument("domainId", "id of the domain that the user belongs to. If both domain and domainId are passed in, \"domainId\" parameter takes precendence", false)); apiCommand.setRequest(request); // Generate response response.add(new Argument("username", "Username")); response.add(new Argument("userid", "User id")); response.add(new Argument("password", "Password")); response.add(new Argument("domainid", "domain ID that the user belongs to")); response.add(new Argument("timeout", "the time period before the session has expired")); response.add(new Argument("account", "the account name the user belongs to")); response.add(new Argument("firstname", "first name of the user")); response.add(new Argument("lastname", "last name of the user")); response.add(new Argument("type", "the account type (admin, domain-admin, read-only-admin, user)")); response.add(new Argument("timezone", "user time zone")); response.add(new Argument("timezoneoffset", "user time zone offset from UTC 00:00")); response.add(new Argument("sessionkey", "Session key that can be passed in subsequent Query command calls")); apiCommand.setResponse(response); out.writeObject(apiCommand); } private static void writeLogoutCommand(ObjectOutputStream out) throws ClassNotFoundException, IOException { ArrayList<Argument> request = new ArrayList<Argument>(); ArrayList<Argument> response = new ArrayList<Argument>(); // Create a new command, set name and description Command apiCommand = new Command(); apiCommand.setName("logout"); apiCommand.setDescription("Logs out the user"); // Generate request - no request parameters apiCommand.setRequest(request); // Generate response response.add(new Argument("description", "success if the logout action succeeded")); apiCommand.setResponse(response); out.writeObject(apiCommand); } private static ArrayList<Argument> setRequestFields(Field[] fields) { ArrayList<Argument> arguments = new ArrayList<Argument>(); ArrayList<Argument> requiredArguments = new ArrayList<Argument>(); ArrayList<Argument> optionalArguments = new ArrayList<Argument>(); Argument id = null; for (Field f : fields) { Parameter parameterAnnotation = f.getAnnotation(Parameter.class); if (parameterAnnotation != null && parameterAnnotation.expose() && parameterAnnotation.includeInApiDoc()) { Argument reqArg = new Argument(parameterAnnotation.name()); reqArg.setRequired(parameterAnnotation.required()); if (!parameterAnnotation.description().isEmpty()) { reqArg.setDescription(parameterAnnotation.description()); } if (parameterAnnotation.type() == BaseCmd.CommandType.LIST || parameterAnnotation.type() == BaseCmd.CommandType.MAP) { reqArg.setType(parameterAnnotation.type().toString().toLowerCase()); } if(!parameterAnnotation.since().isEmpty()){ reqArg.setSinceVersion(parameterAnnotation.since()); } if (reqArg.isRequired() == true) { if (parameterAnnotation.name().equals("id")) { id = reqArg; } else { requiredArguments.add(reqArg); } } else { optionalArguments.add(reqArg); } } } Collections.sort(requiredArguments); Collections.sort(optionalArguments); // sort required and optional arguments here if (id != null) { arguments.add(id); } arguments.addAll(requiredArguments); arguments.addAll(optionalArguments); return arguments; } private static ArrayList<Argument> setResponseFields(Field[] responseFields, Class<?> responseClas) { ArrayList<Argument> arguments = new ArrayList<Argument>(); ArrayList<Argument> sortedChildlessArguments = new ArrayList<Argument>(); ArrayList<Argument> sortedArguments = new ArrayList<Argument>(); Argument id = null; for (Field responseField : responseFields) { SerializedName nameAnnotation = responseField.getAnnotation(SerializedName.class); if (nameAnnotation != null) { Param paramAnnotation = responseField.getAnnotation(Param.class); Argument respArg = new Argument(nameAnnotation.value()); boolean hasChildren = false; if (paramAnnotation != null && paramAnnotation.includeInApiDoc()) { String description = paramAnnotation.description(); Class fieldClass = paramAnnotation.responseObject(); if (description != null && !description.isEmpty()) { respArg.setDescription(description); } if(!paramAnnotation.since().isEmpty()){ respArg.setSinceVersion(paramAnnotation.since()); } if (fieldClass != null) { Class<?> superClass = fieldClass.getSuperclass(); if (superClass != null) { String superName = superClass.getName(); if (superName.equals(BaseResponse.class.getName())) { ArrayList<Argument> fieldArguments = new ArrayList<Argument>(); Field[] fields = fieldClass.getDeclaredFields(); fieldArguments = setResponseFields(fields, fieldClass); respArg.setArguments(fieldArguments); hasChildren = true; } } } } if (paramAnnotation != null && paramAnnotation.includeInApiDoc()) { if (nameAnnotation.value().equals("id")) { id = respArg; } else { if (hasChildren) { respArg.setName(nameAnnotation.value() + "(*)"); sortedArguments.add(respArg); } else { sortedChildlessArguments.add(respArg); } } } } } Collections.sort(sortedArguments); Collections.sort(sortedChildlessArguments); if (id != null) { arguments.add(id); } arguments.addAll(sortedChildlessArguments); arguments.addAll(sortedArguments); if (responseClas.getName().equalsIgnoreCase(AsyncJobResponse.class.getName())) { Argument jobIdArg = new Argument("jobid", "the ID of the async job"); arguments.add(jobIdArg); } else if (_asyncResponses.contains(responseClas.getName())) { Argument jobIdArg = new Argument("jobid", "the ID of the latest async job acting on this object"); Argument jobStatusArg = new Argument("jobstatus", "the current status of the latest async job acting on this object"); arguments.add(jobIdArg); arguments.add(jobStatusArg); } return arguments; } private static void zipDir(String zipFileName, String dir) throws Exception { File dirObj = new File(dir); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName)); addDir(dirObj, out); out.close(); } static void addDir(File dirObj, ZipOutputStream out) throws IOException { File[] files = dirObj.listFiles(); byte[] tmpBuf = new byte[1024]; String pathToDir = dirName; for (int i = 0; i < files.length; i++) { if (files[i].isDirectory()) { addDir(files[i], out); continue; } FileInputStream in = new FileInputStream(files[i].getPath()); out.putNextEntry(new ZipEntry(files[i].getPath().substring(pathToDir.length()))); int len; while ((len = in.read(tmpBuf)) > 0) { out.write(tmpBuf, 0, len); } out.closeEntry(); in.close(); } } private static void deleteDir(File dir) { if (dir.isDirectory()) { String[] children = dir.list(); for (int i = 0; i < children.length; i++) { deleteDir(new File(dir, children[i])); } } dir.delete(); } private static void writeAlertTypes(String dirName) { XStream xs = new XStream(); xs.alias("alert", Alert.class); try { ObjectOutputStream out = xs.createObjectOutputStream(new FileWriter(dirName + "/alert_types.xml"), "alerts"); for (Field f : AlertManager.class.getFields()) { String name = f.getName().substring(11); Alert alert = new Alert(name, f.getInt(null)); out.writeObject(alert); } out.close(); } catch (IOException e) { s_logger.error("Failed to create output stream to write an alert types ", e); } catch (IllegalAccessException e) { s_logger.error("Failed to read alert fields ", e); } } private static class LinkedProperties extends Properties { private final LinkedList<Object> keys = new LinkedList<Object>(); @Override public Enumeration<Object> keys() { return Collections.<Object> enumeration(keys); } @Override public Object put(Object key, Object value) { // System.out.println("Adding key" + key); keys.add(key); return super.put(key, value); } } }