forked from apache/cloudstack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBaseCmd.java
More file actions
413 lines (368 loc) · 15.2 KB
/
BaseCmd.java
File metadata and controls
413 lines (368 loc) · 15.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with 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.
package org.apache.cloudstack.api;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.inject.Inject;
import com.cloud.utils.HttpUtils;
import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.affinity.AffinityGroupService;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService;
import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService;
import org.apache.cloudstack.query.QueryService;
import org.apache.cloudstack.usage.UsageService;
import com.cloud.configuration.ConfigurationService;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.NetworkModel;
import com.cloud.network.NetworkService;
import com.cloud.network.NetworkUsageService;
import com.cloud.network.StorageNetworkService;
import com.cloud.network.VpcVirtualNetworkApplianceService;
import com.cloud.network.as.AutoScaleService;
import com.cloud.network.firewall.FirewallService;
import com.cloud.network.lb.LoadBalancingRulesService;
import com.cloud.network.rules.RulesService;
import com.cloud.network.security.SecurityGroupService;
import com.cloud.network.vpc.NetworkACLService;
import com.cloud.network.vpc.VpcProvisioningService;
import com.cloud.network.vpc.VpcService;
import com.cloud.network.vpn.RemoteAccessVpnService;
import com.cloud.network.vpn.Site2SiteVpnService;
import com.cloud.projects.ProjectService;
import com.cloud.resource.ResourceService;
import com.cloud.server.ManagementService;
import com.cloud.server.ResourceMetaDataService;
import com.cloud.server.TaggedResourceService;
import com.cloud.storage.DataStoreProviderApiService;
import com.cloud.storage.StorageService;
import com.cloud.storage.VolumeApiService;
import com.cloud.storage.snapshot.SnapshotApiService;
import com.cloud.template.TemplateApiService;
import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.user.DomainService;
import com.cloud.user.ResourceLimitService;
import com.cloud.utils.ReflectUtil;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.db.UUIDManager;
import com.cloud.vm.UserVmService;
import com.cloud.vm.snapshot.VMSnapshotService;
public abstract class BaseCmd {
private static final Logger s_logger = Logger.getLogger(BaseCmd.class.getName());
public static final String RESPONSE_TYPE_XML = HttpUtils.RESPONSE_TYPE_XML;
public static final String RESPONSE_TYPE_JSON = HttpUtils.RESPONSE_TYPE_JSON;
public static final DateFormat INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
public static final DateFormat NEW_INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static final String USER_ERROR_MESSAGE = "Internal error executing command, please contact your system administrator";
public static Pattern newInputDateFormat = Pattern.compile("[\\d]+-[\\d]+-[\\d]+ [\\d]+:[\\d]+:[\\d]+");
private static final DateFormat s_outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
protected static final Map<Class<?>, List<Field>> fieldsForCmdClass = new HashMap<Class<?>, List<Field>>();
public static enum HTTPMethod {
GET, POST, PUT, DELETE
}
public static enum CommandType {
BOOLEAN, DATE, FLOAT, INTEGER, SHORT, LIST, LONG, OBJECT, MAP, STRING, TZDATE, UUID
}
private Object _responseObject;
private Map<String, String> fullUrlParams;
private HTTPMethod httpMethod;
@Parameter(name = "response", type = CommandType.STRING)
private String responseType;
@Inject
public ConfigurationService _configService;
@Inject
public AccountService _accountService;
@Inject
public UserVmService _userVmService;
@Inject
public ManagementService _mgr;
@Inject
public StorageService _storageService;
@Inject
public VolumeApiService _volumeService;
@Inject
public ResourceService _resourceService;
@Inject
public NetworkService _networkService;
@Inject
public TemplateApiService _templateService;
@Inject
public SecurityGroupService _securityGroupService;
@Inject
public SnapshotApiService _snapshotService;
@Inject
public VpcVirtualNetworkApplianceService _routerService;
@Inject
public ResponseGenerator _responseGenerator;
@Inject
public EntityManager _entityMgr;
@Inject
public RulesService _rulesService;
@Inject
public AutoScaleService _autoScaleService;
@Inject
public LoadBalancingRulesService _lbService;
@Inject
public RemoteAccessVpnService _ravService;
@Inject
public ProjectService _projectService;
@Inject
public FirewallService _firewallService;
@Inject
public DomainService _domainService;
@Inject
public ResourceLimitService _resourceLimitService;
@Inject
public StorageNetworkService _storageNetworkService;
@Inject
public TaggedResourceService _taggedResourceService;
@Inject
public ResourceMetaDataService _resourceMetaDataService;
@Inject
public VpcService _vpcService;
@Inject
public NetworkACLService _networkACLService;
@Inject
public Site2SiteVpnService _s2sVpnService;
@Inject
public QueryService _queryService;
@Inject
public UsageService _usageService;
@Inject
public NetworkUsageService _networkUsageService;
@Inject
public VMSnapshotService _vmSnapshotService;
@Inject
public DataStoreProviderApiService dataStoreProviderApiService;
@Inject
public VpcProvisioningService _vpcProvSvc;
@Inject
public ApplicationLoadBalancerService _newLbSvc;
@Inject
public ApplicationLoadBalancerService _appLbService;
@Inject
public AffinityGroupService _affinityGroupService;
@Inject
public InternalLoadBalancerElementService _internalLbElementSvc;
@Inject
public InternalLoadBalancerVMService _internalLbSvc;
@Inject
public NetworkModel _ntwkModel;
@Inject
public AlertService _alertSvc;
@Inject
public UUIDManager _uuidMgr;
public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException, NetworkRuleConflictException;
public void configure() {
}
public HTTPMethod getHttpMethod() {
return httpMethod;
}
public void setHttpMethod(final String method) {
if (method != null) {
if (method.equalsIgnoreCase("GET"))
httpMethod = HTTPMethod.GET;
else if (method.equalsIgnoreCase("PUT"))
httpMethod = HTTPMethod.PUT;
else if (method.equalsIgnoreCase("POST"))
httpMethod = HTTPMethod.POST;
else if (method.equalsIgnoreCase("DELETE"))
httpMethod = HTTPMethod.DELETE;
} else {
httpMethod = HTTPMethod.GET;
}
}
public String getResponseType() {
if (responseType == null) {
return RESPONSE_TYPE_XML;
}
return responseType;
}
public void setResponseType(final String responseType) {
this.responseType = responseType;
}
/**
* For some reason this method does not return the actual command name, but more a name that
* is used to create the response. So you can expect for a XCmd a value like xcmdresponse. Anyways
* this methods is used in too many places so for now instead of changing it we just create another
* method {@link BaseCmd#getActualCommandName()} that returns the value from {@link APICommand#name()}
*
* @return
*/
public abstract String getCommandName();
/**
* Gets the CommandName based on the class annotations: the value from {@link APICommand#name()}
*
* @return the value from {@link APICommand#name()}
*/
public String getActualCommandName() {
String cmdName = null;
if (this.getClass().getAnnotation(APICommand.class) != null) {
cmdName = this.getClass().getAnnotation(APICommand.class).name();
} else {
cmdName = this.getClass().getName();
}
return cmdName;
}
/**
* For commands the API framework needs to know the owner of the object being acted upon. This method is
* used to determine that information.
*
* @return the id of the account that owns the object being acted upon
*/
public abstract long getEntityOwnerId();
public Object getResponseObject() {
return _responseObject;
}
public void setResponseObject(final Object responseObject) {
_responseObject = responseObject;
}
public static String getDateString(final Date date) {
if (date == null) {
return "";
}
String formattedString = null;
synchronized (s_outputFormat) {
formattedString = s_outputFormat.format(date);
}
return formattedString;
}
protected List<Field> getAllFieldsForClass(final Class<?> clazz) {
List<Field> filteredFields = fieldsForCmdClass.get(clazz);
// If list of fields was not cached yet
if (filteredFields == null) {
final List<Field> allFields = ReflectUtil.getAllFieldsForClass(this.getClass(), BaseCmd.class);
filteredFields = new ArrayList<Field>();
for (final Field field : allFields) {
final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
if ((parameterAnnotation != null) && parameterAnnotation.expose()) {
filteredFields.add(field);
}
}
// Cache the prepared list for future use
fieldsForCmdClass.put(clazz, filteredFields);
}
return filteredFields;
}
/**
* This method doesn't return all the @{link Parameter}, but only the ones exposed
* and allowed for current @{link RoleType}. This method will get the fields for a given
* Cmd class only once and never again, so in case of a dynamic update the result would
* be obsolete (this might be a plugin update. It is agreed upon that we will not do
* upgrades dynamically but in case we come back on that decision we need to revisit this)
*
* @return
*/
public List<Field> getParamFields() {
final List<Field> allFields = getAllFieldsForClass(this.getClass());
final List<Field> validFields = new ArrayList<Field>();
final Account caller = CallContext.current().getCallingAccount();
for (final Field field : allFields) {
final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
//TODO: Annotate @Validate on API Cmd classes, FIXME how to process Validate
final RoleType[] allowedRoles = parameterAnnotation.authorized();
boolean roleIsAllowed = true;
if (allowedRoles.length > 0) {
roleIsAllowed = false;
for (final RoleType allowedRole : allowedRoles) {
if (allowedRole.getValue() == caller.getType()) {
roleIsAllowed = true;
break;
}
}
}
if (roleIsAllowed) {
validFields.add(field);
} else {
s_logger.debug("Ignoring paremeter " + parameterAnnotation.name() + " as the caller is not authorized to pass it in");
}
}
return validFields;
}
public void setFullUrlParams(final Map<String, String> map) {
fullUrlParams = map;
}
public Map<String, String> getFullUrlParams() {
return fullUrlParams;
}
/**
* To be overwritten by any class who needs specific validation
*/
public void validateSpecificParameters(final Map<String, String> params){
// To be overwritten by any class who needs specific validation
}
/**
* display flag is used to control the display of the resource only to the end user. It doesnt affect Root Admin.
* @return display flag
*/
public boolean isDisplay(){
CallContext context = CallContext.current();
Map<Object, Object> contextMap = context.getContextParameters();
boolean isDisplay = true;
// Iterate over all the first class entities in context and check their display property.
for(Map.Entry<Object, Object> entry : contextMap.entrySet()){
try{
Object key = entry.getKey();
Class clz = Class.forName((String)key);
if(Displayable.class.isAssignableFrom(clz)){
final Object objVO = getEntityVO(clz, entry.getValue());
isDisplay = ((Displayable) objVO).isDisplay();
}
// If the flag is false break immediately
if(!isDisplay)
break;
} catch (Exception e){
s_logger.trace("Caught exception while checking first class entities for display property, continuing on", e);
}
}
context.setEventDisplayEnabled(isDisplay);
return isDisplay;
}
private Object getEntityVO(Class entityType, Object entityId){
// entityId can be internal db id or UUID so accordingly call findbyId or findByUUID
if (entityId instanceof Long){
// Its internal db id - use findById
return _entityMgr.findById(entityType, (Long)entityId);
} else if(entityId instanceof String){
try{
// In case its an async job the internal db id would be a string because of json deserialization
Long internalId = Long.valueOf((String) entityId);
return _entityMgr.findById(entityType, internalId);
} catch (NumberFormatException e){
// It is uuid - use findByUuid`
return _entityMgr.findByUuid(entityType, (String)entityId);
}
}
return null;
}
}