Skip to content

Commit 447430c

Browse files
Antonio FornieDaanHoogland
authored andcommitted
CLOUDSTACK-6003 fixing plus refactoring dispatcher
Signed-off-by: Daan Hoogland <[email protected]> (cherry picked from commit a9bcc1ea3b7dfd3fcc5c795b0095c77851ebe618) Signed-off-by: Daan Hoogland <[email protected]>
1 parent ca1b340 commit 447430c

25 files changed

Lines changed: 2576 additions & 1766 deletions

‎api/src/org/apache/cloudstack/api/ApiConstants.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ public class ApiConstants {
4848
public static final String CLUSTER_ID = "clusterid";
4949
public static final String CLUSTER_NAME = "clustername";
5050
public static final String CLUSTER_TYPE = "clustertype";
51+
public static final String COMMAND = "command";
5152
public static final String COMPONENT = "component";
5253
public static final String CPU_NUMBER = "cpunumber";
5354
public static final String CPU_SPEED = "cpuspeed";
5455
public static final String CREATED = "created";
56+
public static final String CTX_START_EVENT_ID = "ctxStartEventId";
5557
public static final String CUSTOMIZED = "customized";
5658
public static final String CUSTOMIZED_IOPS = "customizediops";
5759
public static final String CUSTOM_ID = "customid";
@@ -78,6 +80,7 @@ public class ApiConstants {
7880
public static final String IP6_DNS2 = "ip6dns2";
7981
public static final String DOMAIN = "domain";
8082
public static final String DOMAIN_ID = "domainid";
83+
public static final String DOMAIN__ID = "domainId";
8184
public static final String DURATION = "duration";
8285
public static final String EMAIL = "email";
8386
public static final String END_DATE = "enddate";
@@ -208,6 +211,7 @@ public class ApiConstants {
208211
public static final String SENT = "sent";
209212
public static final String SENT_BYTES = "sentbytes";
210213
public static final String SERVICE_OFFERING_ID = "serviceofferingid";
214+
public static final String SESSIONKEY = "sessionkey";
211215
public static final String SHOW_CAPACITIES = "showcapacities";
212216
public static final String SHOW_REMOVED = "showremoved";
213217
public static final String SIZE = "size";
@@ -276,6 +280,7 @@ public class ApiConstants {
276280
public static final String NETWORKRATE = "networkrate";
277281
public static final String HOST_TAGS = "hosttags";
278282
public static final String SSH_KEYPAIR = "keypair";
283+
public static final String HTTPMETHOD = "httpmethod";
279284
public static final String HOST_CPU_CAPACITY = "hostcpucapacity";
280285
public static final String HOST_CPU_NUM = "hostcpunum";
281286
public static final String HOST_MEM_CAPACITY = "hostmemcapacity";

‎api/src/org/apache/cloudstack/api/BaseCmd.java‎

Lines changed: 76 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,24 @@
1717

1818
package org.apache.cloudstack.api;
1919

20+
import java.lang.reflect.Field;
2021
import java.text.DateFormat;
2122
import java.text.SimpleDateFormat;
23+
import java.util.ArrayList;
2224
import java.util.Date;
2325
import java.util.HashMap;
26+
import java.util.List;
2427
import java.util.Map;
2528
import java.util.regex.Pattern;
2629

2730
import javax.inject.Inject;
2831

2932
import org.apache.log4j.Logger;
3033

34+
import org.apache.cloudstack.acl.RoleType;
3135
import org.apache.cloudstack.affinity.AffinityGroupService;
3236
import org.apache.cloudstack.alert.AlertService;
37+
import org.apache.cloudstack.context.CallContext;
3338
import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
3439
import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService;
3540
import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService;
@@ -75,6 +80,7 @@
7580
import com.cloud.user.AccountService;
7681
import com.cloud.user.DomainService;
7782
import com.cloud.user.ResourceLimitService;
83+
import com.cloud.utils.ReflectUtil;
7884
import com.cloud.utils.db.EntityManager;
7985
import com.cloud.vm.UserVmService;
8086
import com.cloud.vm.snapshot.VMSnapshotService;
@@ -97,6 +103,8 @@ public enum CommandType {
97103
public static Pattern newInputDateFormat = Pattern.compile("[\\d]+-[\\d]+-[\\d]+ [\\d]+:[\\d]+:[\\d]+");
98104
private static final DateFormat s_outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
99105

106+
protected static final Map<Class<?>, List<Field>> fieldsForCmdClass = new HashMap<Class<?>, List<Field>>();
107+
100108
private Object _responseObject = null;
101109
private Map<String, String> fullUrlParams;
102110

@@ -203,7 +211,7 @@ public HTTPMethod getHttpMethod() {
203211
return httpMethod;
204212
}
205213

206-
public void setHttpMethod(String method) {
214+
public void setHttpMethod(final String method) {
207215
if (method != null) {
208216
if (method.equalsIgnoreCase("GET"))
209217
httpMethod = HTTPMethod.GET;
@@ -225,7 +233,7 @@ public String getResponseType() {
225233
return responseType;
226234
}
227235

228-
public void setResponseType(String responseType) {
236+
public void setResponseType(final String responseType) {
229237
this.responseType = responseType;
230238
}
231239

@@ -243,15 +251,15 @@ public Object getResponseObject() {
243251
return _responseObject;
244252
}
245253

246-
public void setResponseObject(Object responseObject) {
254+
public void setResponseObject(final Object responseObject) {
247255
_responseObject = responseObject;
248256
}
249257

250258
public ManagementService getMgmtServiceRef() {
251259
return _mgr;
252260
}
253261

254-
public static String getDateString(Date date) {
262+
public static String getDateString(final Date date) {
255263
if (date == null) {
256264
return "";
257265
}
@@ -262,120 +270,102 @@ public static String getDateString(Date date) {
262270
return formattedString;
263271
}
264272

265-
// FIXME: move this to a utils method so that maps can be unpacked and integer/long values can be appropriately cast
266-
@SuppressWarnings({"unchecked", "rawtypes"})
267-
public Map<String, Object> unpackParams(Map<String, String> params) {
268-
Map<String, Object> lowercaseParams = new HashMap<String, Object>();
269-
for (String key : params.keySet()) {
270-
int arrayStartIndex = key.indexOf('[');
271-
int arrayStartLastIndex = key.lastIndexOf('[');
272-
if (arrayStartIndex != arrayStartLastIndex) {
273-
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
274-
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
275-
}
273+
protected List<Field> getAllFieldsForClass(final Class<?> clazz) {
274+
List<Field> filteredFields = fieldsForCmdClass.get(clazz);
276275

277-
if (arrayStartIndex > 0) {
278-
int arrayEndIndex = key.indexOf(']');
279-
int arrayEndLastIndex = key.lastIndexOf(']');
280-
if ((arrayEndIndex < arrayStartIndex) || (arrayEndIndex != arrayEndLastIndex)) {
281-
// malformed parameter
282-
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
283-
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
284-
}
276+
// If list of fields was not cached yet
277+
if (filteredFields == null) {
278+
final List<Field> allFields = ReflectUtil.getAllFieldsForClass(this.getClass(), BaseCmd.class);
279+
filteredFields = new ArrayList<Field>();
285280

286-
// Now that we have an array object, check for a field name in the case of a complex object
287-
int fieldIndex = key.indexOf('.');
288-
String fieldName = null;
289-
if (fieldIndex < arrayEndIndex) {
290-
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
291-
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
292-
} else {
293-
fieldName = key.substring(fieldIndex + 1);
281+
for (final Field field : allFields) {
282+
final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
283+
if ((parameterAnnotation != null) && parameterAnnotation.expose()) {
284+
filteredFields.add(field);
294285
}
286+
}
295287

296-
// parse the parameter name as the text before the first '[' character
297-
String paramName = key.substring(0, arrayStartIndex);
298-
paramName = paramName.toLowerCase();
299-
300-
Map<Integer, Map> mapArray = null;
301-
Map<String, Object> mapValue = null;
302-
String indexStr = key.substring(arrayStartIndex + 1, arrayEndIndex);
303-
int index = 0;
304-
boolean parsedIndex = false;
305-
try {
306-
if (indexStr != null) {
307-
index = Integer.parseInt(indexStr);
308-
parsedIndex = true;
309-
}
310-
} catch (NumberFormatException nfe) {
311-
s_logger.warn("Invalid parameter " + key + " received, unable to parse object array, returning an error.");
312-
}
288+
// Cache the prepared list for future use
289+
fieldsForCmdClass.put(clazz, filteredFields);
290+
}
291+
return filteredFields;
292+
}
313293

314-
if (!parsedIndex) {
315-
throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key +
316-
"; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup");
317-
}
294+
protected Account getCurrentContextAccount() {
295+
return CallContext.current().getCallingAccount();
296+
}
318297

319-
Object value = lowercaseParams.get(paramName);
320-
if (value == null) {
321-
// for now, assume object array with sub fields
322-
mapArray = new HashMap<Integer, Map>();
323-
mapValue = new HashMap<String, Object>();
324-
mapArray.put(Integer.valueOf(index), mapValue);
325-
} else if (value instanceof Map) {
326-
mapArray = (HashMap)value;
327-
mapValue = mapArray.get(Integer.valueOf(index));
328-
if (mapValue == null) {
329-
mapValue = new HashMap<String, Object>();
330-
mapArray.put(Integer.valueOf(index), mapValue);
298+
/**
299+
* this method doesn't return all the @{link Parameter}, but only the ones exposed
300+
* and allowed for current @{link RoleType}
301+
*
302+
* @return
303+
*/
304+
public List<Field> getParamFields() {
305+
final List<Field> allFields = getAllFieldsForClass(this.getClass());
306+
final List<Field> validFields = new ArrayList<Field>();
307+
final Account caller = getCurrentContextAccount();
308+
309+
for (final Field field : allFields) {
310+
final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
311+
312+
//TODO: Annotate @Validate on API Cmd classes, FIXME how to process Validate
313+
final RoleType[] allowedRoles = parameterAnnotation.authorized();
314+
boolean roleIsAllowed = true;
315+
if (allowedRoles.length > 0) {
316+
roleIsAllowed = false;
317+
for (final RoleType allowedRole : allowedRoles) {
318+
if (allowedRole.getValue() == caller.getType()) {
319+
roleIsAllowed = true;
320+
break;
331321
}
332322
}
323+
}
333324

334-
// we are ready to store the value for a particular field into the map for this object
335-
mapValue.put(fieldName, params.get(key));
336-
337-
lowercaseParams.put(paramName, mapArray);
325+
if (roleIsAllowed) {
326+
validFields.add(field);
338327
} else {
339-
lowercaseParams.put(key.toLowerCase(), params.get(key));
328+
s_logger.debug("Ignoring paremeter " + parameterAnnotation.name() + " as the caller is not authorized to pass it in");
340329
}
341330
}
342-
return lowercaseParams;
331+
332+
return validFields;
343333
}
344334

345-
protected long getInstanceIdFromJobSuccessResult(String result) {
335+
protected long getInstanceIdFromJobSuccessResult(final String result) {
346336
s_logger.debug("getInstanceIdFromJobSuccessResult not overridden in subclass " + this.getClass().getName());
347337
return 0;
348338
}
349339

350-
public static boolean isAdmin(short accountType) {
340+
public static boolean isAdmin(final short accountType) {
351341
return ((accountType == Account.ACCOUNT_TYPE_ADMIN) || (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) ||
352342
(accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN));
353343
}
354344

355-
public static boolean isRootAdmin(short accountType) {
345+
public static boolean isRootAdmin(final short accountType) {
356346
return ((accountType == Account.ACCOUNT_TYPE_ADMIN));
357347
}
358348

359-
public void setFullUrlParams(Map<String, String> map) {
349+
public void setFullUrlParams(final Map<String, String> map) {
360350
fullUrlParams = map;
361351
}
362352

363353
public Map<String, String> getFullUrlParams() {
364354
return fullUrlParams;
365355
}
366356

367-
public Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly) {
357+
public Long finalyzeAccountId(final String accountName, final Long domainId, final Long projectId, final boolean enabledOnly) {
368358
if (accountName != null) {
369359
if (domainId == null) {
370360
throw new InvalidParameterValueException("Account must be specified with domainId parameter");
371361
}
372362

373-
Domain domain = _domainService.getDomain(domainId);
363+
final Domain domain = _domainService.getDomain(domainId);
374364
if (domain == null) {
375365
throw new InvalidParameterValueException("Unable to find domain by id");
376366
}
377367

378-
Account account = _accountService.getActiveAccountByName(accountName, domainId);
368+
final Account account = _accountService.getActiveAccountByName(accountName, domainId);
379369
if (account != null && account.getType() != Account.ACCOUNT_TYPE_PROJECT) {
380370
if (!enabledOnly || account.getState() == Account.State.enabled) {
381371
return account.getId();
@@ -392,12 +382,12 @@ public Long finalyzeAccountId(String accountName, Long domainId, Long projectId,
392382
}
393383

394384
if (projectId != null) {
395-
Project project = _projectService.getProject(projectId);
385+
final Project project = _projectService.getProject(projectId);
396386
if (project != null) {
397387
if (!enabledOnly || project.getState() == Project.State.Active) {
398388
return project.getProjectAccountId();
399389
} else {
400-
PermissionDeniedException ex =
390+
final PermissionDeniedException ex =
401391
new PermissionDeniedException("Can't add resources to the project with specified projectId in state=" + project.getState() +
402392
" as it's no longer active");
403393
ex.addProxyObject(project.getUuid(), "projectId");
@@ -409,4 +399,11 @@ public Long finalyzeAccountId(String accountName, Long domainId, Long projectId,
409399
}
410400
return null;
411401
}
402+
403+
/**
404+
* To be overwritten by any class who needs specific validation
405+
*/
406+
public void validateSpecificParameters(final Map<String, Object> params){
407+
// To be overwritten by any class who needs specific validation
408+
}
412409
}

‎api/src/org/apache/cloudstack/api/BaseListCmd.java‎

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
// under the License.
1717
package org.apache.cloudstack.api;
1818

19+
import java.util.Map;
20+
1921
import com.cloud.exception.InvalidParameterValueException;
22+
import com.cloud.utils.exception.CSExceptionErrorCode;
2023

2124
public abstract class BaseListCmd extends BaseCmd {
2225

@@ -83,7 +86,7 @@ public long getEntityOwnerId() {
8386

8487
public Long getPageSizeVal() {
8588
Long defaultPageSize = s_maxPageSize;
86-
Integer pageSizeInt = getPageSize();
89+
final Integer pageSizeInt = getPageSize();
8790
if (pageSizeInt != null) {
8891
defaultPageSize = pageSizeInt.longValue();
8992
}
@@ -96,12 +99,12 @@ public Long getPageSizeVal() {
9699

97100
public Long getStartIndex() {
98101
Long startIndex = Long.valueOf(0);
99-
Long pageSizeVal = getPageSizeVal();
102+
final Long pageSizeVal = getPageSizeVal();
100103

101104
if (pageSizeVal == null) {
102105
startIndex = null;
103106
} else if (page != null) {
104-
int pageNum = page.intValue();
107+
final int pageNum = page.intValue();
105108
if (pageNum > 0) {
106109
startIndex = Long.valueOf(pageSizeVal * (pageNum - 1));
107110
}
@@ -112,4 +115,25 @@ public Long getStartIndex() {
112115
public ApiCommandJobType getInstanceType() {
113116
return ApiCommandJobType.None;
114117
}
118+
119+
@Override
120+
public void validateSpecificParameters(final Map<String, Object> params){
121+
super.validateSpecificParameters(params);
122+
123+
final Object pageSizeObj = params.get(ApiConstants.PAGE_SIZE);
124+
Long pageSize = null;
125+
if (pageSizeObj != null) {
126+
pageSize = Long.valueOf((String)pageSizeObj);
127+
}
128+
129+
if (params.get(ApiConstants.PAGE) == null &&
130+
pageSize != null &&
131+
!pageSize.equals(BaseListCmd.s_pageSizeUnlimited)) {
132+
final ServerApiException ex = new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"page\" parameter is required when \"pagesize\" is specified");
133+
ex.setCSErrorCode(CSExceptionErrorCode.getCSErrCode(ex.getClass().getName()));
134+
throw ex;
135+
} else if (pageSize == null && (params.get(ApiConstants.PAGE) != null)) {
136+
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"pagesize\" parameter is required when \"page\" is specified");
137+
}
138+
}
115139
}

0 commit comments

Comments
 (0)