This commit is contained in:
Min Chen 2013-01-24 16:46:10 -08:00
commit 4ccbdbd23d
21 changed files with 166 additions and 82 deletions

View File

@ -47,6 +47,15 @@ public interface EntityManager {
*/
public <T> T findByUuid(Class<T> entityType, String uuid);
/**
* Finds a unique entity by uuid string
* @param <T> entity class
* @param entityType type of entity you're looking for.
* @param uuid the unique id
* @return T if found, null if not.
*/
public <T> T findByUuidIncludingRemoved(Class<T> entityType, String uuid);
/**
* Finds an entity by external id which is always String
* @param <T> entity class

View File

@ -18,7 +18,7 @@ package org.apache.cloudstack.api;
public abstract class BaseListAccountResourcesCmd extends BaseListDomainResourcesCmd {
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "List resources by account. Must be used with the domainId parameter.")
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "list resources by account. Must be used with the domainId parameter.")
private String accountName;
public String getAccountName() {

View File

@ -47,7 +47,7 @@ public class AttachIsoCmd extends BaseAsyncCmd {
required=true, description="the ID of the ISO file")
private Long id;
@Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType = TemplateResponse.class,
@Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType = UserVmResponse.class,
required=true, description="the ID of the virtual machine")
private Long virtualMachineId;

View File

@ -42,7 +42,7 @@ public class DetachIsoCmd extends BaseAsyncCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType = TemplateResponse.class,
@Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType = UserVmResponse.class,
required=true, description="The ID of the virtual machine")
private Long virtualMachineId;

View File

@ -51,7 +51,7 @@ public class CopyTemplateCmd extends BaseAsyncCmd {
required=true, description="ID of the zone the template is being copied to.")
private Long destZoneId;
@Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = UserVmResponse.class,
@Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = TemplateResponse.class,
required=true, description="Template ID.")
private Long id;

View File

@ -17,7 +17,7 @@
package org.apache.cloudstack.api.command.user.template;
import org.apache.cloudstack.api.*;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.TemplateResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.log4j.Logger;
@ -38,7 +38,7 @@ public class DeleteTemplateCmd extends BaseAsyncCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = UserVmResponse.class,
@Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = TemplateResponse.class,
required=true, description="the ID of the template")
private Long id;

View File

@ -17,7 +17,7 @@
package org.apache.cloudstack.api.command.user.template;
import org.apache.cloudstack.api.*;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.TemplateResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.log4j.Logger;
@ -40,7 +40,7 @@ public class ExtractTemplateCmd extends BaseAsyncCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = UserVmResponse.class,
@Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = TemplateResponse.class,
required=true, description="the ID of the template")
private Long id;

View File

@ -64,15 +64,6 @@ public class ListVPCsCmd extends BaseListTaggedResourcesCmd{
, description="list by ID of the VPC offering")
private Long VpcOffId;
@Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="list by account associated with the VPC. " +
"Must be used with the domainId parameter.")
private String accountName;
@Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class,
description="list by domain ID associated with the VPC. " +
"If used with the account parameter returns the VPC associated with the account for the specified domain.")
private Long domainId;
@Parameter(name=ApiConstants.SUPPORTED_SERVICES, type=CommandType.LIST, collectionType=CommandType.STRING,
description="list VPC supporting certain services")
private List<String> supportedServices;
@ -87,14 +78,6 @@ public class ListVPCsCmd extends BaseListTaggedResourcesCmd{
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public String getAccountName() {
return accountName;
}
public Long getDomainId() {
return domainId;
}
public Long getZoneId() {
return zoneId;
}

View File

@ -107,7 +107,7 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService {
}
}
Field[] fields = ReflectUtil.getAllFieldsForClass(cmdClass,
Set<Field> fields = ReflectUtil.getAllFieldsForClass(cmdClass,
new Class<?>[]{BaseCmd.class, BaseAsyncCmd.class, BaseAsyncCreateCmd.class});
boolean isAsync = ReflectUtil.isCmdClassAsync(cmdClass,

View File

@ -27,6 +27,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
@ -157,8 +158,7 @@ public class ApiDispatcher {
}
if (queueSizeLimit != null) {
_asyncMgr
.syncAsyncJobExecution(asyncCmd.getJob(), asyncCmd.getSyncObjType(), asyncCmd.getSyncObjId().longValue(), queueSizeLimit);
_asyncMgr.syncAsyncJobExecution(asyncCmd.getJob(), asyncCmd.getSyncObjType(), asyncCmd.getSyncObjId().longValue(), queueSizeLimit);
} else {
s_logger.trace("The queue size is unlimited, skipping the synchronizing");
}
@ -190,8 +190,7 @@ public class ApiDispatcher {
}
}
Field[] fields = ReflectUtil.getAllFieldsForClass(cmd.getClass(),
new Class<?>[] {BaseCmd.class});
List<Field> fields = ReflectUtil.getAllFieldsForClass(cmd.getClass(), BaseCmd.class);
for (Field field : fields) {
PlugService plugServiceAnnotation = field.getAnnotation(PlugService.class);
@ -360,8 +359,9 @@ public class ApiDispatcher {
// Go through each entity which is an interface to a VO class and get a VO object
// Try to getId() for the object using reflection, break on first non-null value
for (Class<?> entity: entities) {
// findByUuid returns one VO object using uuid, use reflect to get the Id
Object objVO = s_instance._entityMgr.findByUuid(entity, uuid);
// For backward compatibility, we search within removed entities and let service layer deal
// with removed ones, return empty response or error
Object objVO = s_instance._entityMgr.findByUuidIncludingRemoved(entity, uuid);
if (objVO == null) {
continue;
}
@ -377,11 +377,10 @@ public class ApiDispatcher {
break;
}
if (internalId == null) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Object entity with uuid=" + uuid + " does not exist in the database.");
}
if (s_logger.isDebugEnabled())
s_logger.debug("Object entity uuid = " + uuid + " does not exist in the database.");
throw new InvalidParameterValueException("Invalid parameter value=" + uuid
+ " due to incorrect long value, entity not found, or an annotation bug.");
+ " due to incorrect long value format, or entity was not found as it may have been deleted, or due to incorrect parameter annotation for the field in api cmd.");
}
return internalId;
}

View File

@ -28,6 +28,7 @@ import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import com.cloud.utils.IteratorUtil;
import com.cloud.utils.ReflectUtil;
import org.apache.cloudstack.api.*;
import org.apache.log4j.Logger;
@ -135,7 +136,7 @@ public class ApiXmlDocWriter {
String commandRoleMask = preProcessedCommand.substring(splitIndex + 1);
Class<?> cmdClass = _apiNameCmdClassMap.get(key);
if (cmdClass == null) {
System.out.println("Check, Null Value for key: " + key + " preProcessedCommand=" + preProcessedCommand);
System.out.println("Check, is this api part of another build profile? Null value for key: " + key + " preProcessedCommand=" + preProcessedCommand);
continue;
}
String commandName = cmdClass.getName();
@ -349,7 +350,7 @@ public class ApiXmlDocWriter {
apiCommand.setAsync(isAsync);
Field[] fields = ReflectUtil.getAllFieldsForClass(clas,
Set<Field> fields = ReflectUtil.getAllFieldsForClass(clas,
new Class<?>[] {BaseCmd.class, BaseAsyncCmd.class, BaseAsyncCreateCmd.class});
request = setRequestFields(fields);
@ -422,10 +423,10 @@ public class ApiXmlDocWriter {
out.writeObject(apiCommand);
}
private static ArrayList<Argument> setRequestFields(Field[] fields) {
private static ArrayList<Argument> setRequestFields(Set<Field> fields) {
ArrayList<Argument> arguments = new ArrayList<Argument>();
ArrayList<Argument> requiredArguments = new ArrayList<Argument>();
ArrayList<Argument> optionalArguments = new ArrayList<Argument>();
Set<Argument> requiredArguments = new HashSet<Argument>();
Set<Argument> optionalArguments = new HashSet<Argument>();
Argument id = null;
for (Field f : fields) {
Parameter parameterAnnotation = f.getAnnotation(Parameter.class);
@ -444,7 +445,7 @@ public class ApiXmlDocWriter {
reqArg.setSinceVersion(parameterAnnotation.since());
}
if (reqArg.isRequired() == true) {
if (reqArg.isRequired()) {
if (parameterAnnotation.name().equals("id")) {
id = reqArg;
} else {
@ -456,15 +457,12 @@ public class ApiXmlDocWriter {
}
}
Collections.sort(requiredArguments);
Collections.sort(optionalArguments);
// sort required and optional arguments here
if (id != null) {
arguments.add(id);
}
arguments.addAll(requiredArguments);
arguments.addAll(optionalArguments);
arguments.addAll(IteratorUtil.asSortedList(requiredArguments));
arguments.addAll(IteratorUtil.asSortedList(optionalArguments));
return arguments;
}

View File

@ -51,6 +51,13 @@ public class EntityManagerImpl implements EntityManager, Manager {
return dao.findByUuid(uuid);
}
@Override
public <T> T findByUuidIncludingRemoved(Class<T> entityType, String uuid) {
// Finds and returns a unique VO using uuid, null if entity not found in db
GenericDao<? extends T, String> dao = (GenericDao<? extends T, String>)GenericDaoBase.getDao(entityType);
return dao.findByUuidIncludingRemoved(uuid);
}
@Override
public <T> T findByXId(Class<T> entityType, String xid) {
return null;

View File

@ -75,7 +75,8 @@ class CloudMonkeyShell(cmd.Cmd, object):
# datastructure {'verb': {cmd': ['api', [params], doc, required=[]]}}
cache_verbs = precached_verbs
def __init__(self):
def __init__(self, pname):
self.program_name = pname
if os.path.exists(self.config_file):
config = self.read_config()
else:
@ -262,8 +263,9 @@ class CloudMonkeyShell(cmd.Cmd, object):
return
isAsync = isAsync and (self.asyncblock == "true")
if isAsync and 'jobid' in response[response.keys()[0]]:
jobId = response[response.keys()[0]]['jobid']
responsekey = filter(lambda x: 'response' in x, response.keys())[0]
if isAsync and 'jobid' in response[responsekey]:
jobId = response[responsekey]['jobid']
command = "queryAsyncJobResult"
requests = {'jobid': jobId}
timeout = int(self.timeout)
@ -282,7 +284,7 @@ class CloudMonkeyShell(cmd.Cmd, object):
jobstatus = result['jobstatus']
if jobstatus == 2:
jobresult = result["jobresult"]
self.print_shell("Async query failed for jobid=",
self.print_shell("\rAsync query failed for jobid",
jobId, "\nError", jobresult["errorcode"],
jobresult["errortext"])
return
@ -293,7 +295,7 @@ class CloudMonkeyShell(cmd.Cmd, object):
timeout = timeout - pollperiod
progress += 1
logger.debug("job: %s to timeout in %ds" % (jobId, timeout))
self.print_shell("Error:", "Async query timeout for jobid=", jobId)
self.print_shell("Error:", "Async query timeout for jobid", jobId)
return response
@ -306,7 +308,19 @@ class CloudMonkeyShell(cmd.Cmd, object):
return None
return api_mod
def pipe_runner(self, args):
if args.find(' |') > -1:
pname = self.program_name
if '.py' in pname:
pname = "python " + pname
self.do_shell("%s %s" % (pname, args))
return True
return False
def default(self, args):
if self.pipe_runner(args):
return
lexp = shlex.shlex(args.strip())
lexp.whitespace = " "
lexp.whitespace_split = True
@ -387,7 +401,8 @@ class CloudMonkeyShell(cmd.Cmd, object):
self.cache_verbs[verb][subject][1])
search_string = text
autocompletions.append("filter=")
if self.tabularize == "true":
autocompletions.append("filter=")
return [s for s in autocompletions if s.startswith(search_string)]
def do_api(self, args):
@ -504,22 +519,21 @@ def main():
for rule in grammar:
def add_grammar(rule):
def grammar_closure(self, args):
if '|' in args: # FIXME: Consider parsing issues
prog_name = sys.argv[0]
if '.py' in prog_name:
prog_name = "python " + prog_name
self.do_shell("%s %s %s" % (prog_name, rule, args))
if self.pipe_runner("%s %s" % (rule, args)):
return
try:
args_partition = args.partition(" ")
res = self.cache_verbs[rule][args_partition[0]]
cmd = res[0]
helpdoc = res[2]
args = args_partition[2]
except KeyError, e:
self.print_shell("Error: invalid %s api arg" % rule, e)
return
if ' --help' in args or ' -h' in args:
self.print_shell(res[2])
self.print_shell(helpdoc)
return
self.default(res[0] + " " + args_partition[2])
self.default("%s %s" % (cmd, args))
return grammar_closure
grammar_handler = add_grammar(rule)
@ -527,7 +541,7 @@ def main():
grammar_handler.__name__ = 'do_' + rule
setattr(self, grammar_handler.__name__, grammar_handler)
shell = CloudMonkeyShell()
shell = CloudMonkeyShell(sys.argv[0])
if len(sys.argv) > 1:
shell.onecmd(' '.join(sys.argv[1:]))
else:

View File

@ -53,7 +53,7 @@ setup(
include_package_data = True,
zip_safe = False,
classifiers = [
"Development Status :: 4 - Beta",
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: End Users/Desktop",

View File

@ -16,7 +16,7 @@
// under the License.
(function($, cloudStack) {
var zoneObjs, hypervisorObjs, featuredTemplateObjs, communityTemplateObjs, myTemplateObjs, featuredIsoObjs, community;
var zoneObjs, hypervisorObjs, featuredTemplateObjs, communityTemplateObjs, myTemplateObjs, featuredIsoObjs, community, networkObjs;
var selectedZoneObj, selectedTemplateObj, selectedHypervisor, selectedDiskOfferingObj;
var step5ContainerType = 'nothing-to-select'; //'nothing-to-select', 'select-network', 'select-security-group'
@ -40,6 +40,33 @@
!data.vpcid;
},
// Runs when advanced SG-enabled zone is run, before
// the security group step
//
// -- if it returns false, then 'Select Security Group' is skipped.
//
advSGFilter: function(args) {
var selectedNetworks;
if ($.isArray(args.data['my-networks'])) {
selectedNetworks = $(args.data['my-networks']).map(function(index, myNetwork) {
return $.grep(networkObjs, function(networkObj) {
return networkObj.id == myNetwork;
});
});
} else {
selectedNetworks = $.grep(networkObjs, function(networkObj) {
return networkObj.id == args.data['my-networks'];
});
}
return $.grep(selectedNetworks, function(network) {
return $.grep(network.service, function(service) {
return service.name == 'SecurityGroup';
}).length;
}).length;
},
// Data providers for each wizard step
steps: [
@ -347,7 +374,7 @@
networkData.account = g_account;
}
var networkObjs, vpcObjs;
var vpcObjs;
//listVPCs without account/domainid/listAll parameter will return only VPCs belonging to the current login. That's what should happen in Instances page's VM Wizard.
//i.e. If the current login is root-admin, do not show VPCs belonging to regular-user/domain-admin in Instances page's VM Wizard.
@ -365,7 +392,7 @@
async: false,
success: function(json) {
networkObjs = json.listnetworksresponse.network ? json.listnetworksresponse.network : [];
if(networkObjs.length > 0) {
for(var i = 0; i < networkObjs.length; i++) {
var networkObj = networkObjs[i];

View File

@ -1026,7 +1026,7 @@
createForm: {
desc: 'message.enter.token',
fields: {
projectid: { label: 'label.project.id', validation: { required: true, docID: 'helpEnterTokenProjectID' }},
projectid: { label: 'label.project.id', validation: { required: true}, docID: 'helpEnterTokenProjectID' },
token: { label: 'label.token', docID: 'helpEnterTokenToken', validation: { required: true }}
}
},

View File

@ -821,6 +821,16 @@
cloudStack.dialog.notice({ message: 'message.step.4.continue' });
return false;
}
if ($activeStep.hasClass('next-use-security-groups')) {
var advSGFilter = args.advSGFilter({
data: cloudStack.serializeForm($form)
});
if (!advSGFilter) {
showStep(6);
}
}
}
//step 6 - review (spcifiy displyname, group as well)

View File

@ -16,8 +16,11 @@
// under the License.
package com.cloud.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
public class IteratorUtil {
public static <T> Iterable<T> enumerationAsIterable(final Enumeration<T> e) {
@ -51,4 +54,11 @@ public class IteratorUtil {
}
};
}
public static
<T extends Comparable<? super T>> List<T> asSortedList(Collection<T> c) {
List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
}

View File

@ -19,7 +19,10 @@ package com.cloud.utils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.cloud.utils.exception.CloudRuntimeException;
@ -87,26 +90,39 @@ public class ReflectUtil {
return isAsync;
}
// Returns all fields across the base class for a cmd
public static Field[] getAllFieldsForClass(Class<?> cmdClass,
Class<?>[] searchClasses) {
Field[] fields = cmdClass.getDeclaredFields();
// Returns all fields until a base class for a cmd class
public static List<Field> getAllFieldsForClass(Class<?> cmdClass,
Class<?> baseClass) {
List<Field> fields = new ArrayList<Field>();
Collections.addAll(fields, cmdClass.getDeclaredFields());
Class<?> superClass = cmdClass.getSuperclass();
while (baseClass.isAssignableFrom(superClass)) {
Field[] superClassFields = superClass.getDeclaredFields();
if (superClassFields != null)
Collections.addAll(fields, superClassFields);
superClass = superClass.getSuperclass();
}
return fields;
}
// Returns all unique fields except excludeClasses for a cmd class
public static Set<Field> getAllFieldsForClass(Class<?> cmdClass,
Class<?>[] excludeClasses) {
Set<Field> fields = new HashSet<Field>();
Collections.addAll(fields, cmdClass.getDeclaredFields());
Class<?> superClass = cmdClass.getSuperclass();
while (superClass != null && superClass != Object.class) {
String superName = superClass.getName();
for (Class<?> baseClass: searchClasses) {
if(!baseClass.isAssignableFrom(superClass))
continue;
if (!superName.equals(baseClass.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;
}
}
boolean isNameEqualToSuperName = false;
for (Class<?> baseClass: excludeClasses)
if (superName.equals(baseClass.getName()))
isNameEqualToSuperName = true;
if (!isNameEqualToSuperName) {
Field[] superClassFields = superClass.getDeclaredFields();
if (superClassFields != null)
Collections.addAll(fields, superClassFields);
}
superClass = superClass.getSuperclass();
}

View File

@ -57,6 +57,9 @@ public interface GenericDao<T, ID extends Serializable> {
// Finds one unique VO using uuid
T findByUuid(String uuid);
// Finds one unique VO using uuid including removed entities
T findByUuidIncludingRemoved(String uuid);
/**
* @return VO object ready to be used for update. It won't have any fields filled in.

View File

@ -921,6 +921,14 @@ public abstract class GenericDaoBase<T, ID extends Serializable> implements Gene
return findOneBy(sc);
}
@Override @DB(txn=false)
@SuppressWarnings("unchecked")
public T findByUuidIncludingRemoved(final String uuid) {
SearchCriteria<T> sc = createSearchCriteria();
sc.addAnd("uuid", SearchCriteria.Op.EQ, uuid);
return findOneIncludingRemovedBy(sc);
}
@Override @DB(txn=false)
public T findByIdIncludingRemoved(ID id) {
return findById(id, true, null);