blob: f32922819b0108243615c5b351a72499d7d0c513 [file] [log] [blame]
// 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.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.inject.Inject;
import org.apache.cloudstack.acl.ProjectRoleService;
import org.apache.cloudstack.acl.RoleService;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.affinity.AffinityGroupService;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.annotation.AnnotationService;
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.storage.object.BucketApiService;
import org.apache.cloudstack.storage.ImageStoreService;
import org.apache.cloudstack.storage.template.VnfTemplateManager;
import org.apache.cloudstack.usage.UsageService;
import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;
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.Ipv6Service;
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.ResourceIconManager;
import com.cloud.server.ResourceManagerUtil;
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.HttpUtils;
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_SUFFIX = "response";
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 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, DOUBLE, INTEGER, SHORT, LIST, LONG, OBJECT, MAP, STRING, 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 RoleService roleService;
@Inject
public ProjectRoleService projRoleService;
@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 ImageStoreService _imageStoreService;
@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 ResourceManagerUtil resourceManagerUtil;
@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;
@Inject
public AnnotationService annotationService;
@Inject
public ResourceIconManager resourceIconManager;
@Inject
public Ipv6Service ipv6Service;
@Inject
public VnfTemplateManager vnfTemplateManager;
@Inject
public BucketApiService _bucketService;
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;
}
/**
* Gets the CommandName based on the class annotations: the value from {@link APICommand#name()}
*
* @return the value from {@link APICommand#name()}
*/
public static String getCommandNameByClass(Class<?> clazz) {
String cmdName = null;
APICommand apiClassAnnotation = clazz.getAnnotation(APICommand.class);
if (apiClassAnnotation != null && apiClassAnnotation.name() != null) {
cmdName = apiClassAnnotation.name();
} else {
cmdName = clazz.getName();
}
return cmdName;
}
public String getActualCommandName() {
return getCommandNameByClass(this.getClass());
}
public String getCommandName() {
return getResponseNameByClass(this.getClass());
}
/**
* Retrieves the name defined in {@link APICommand#name()}, in lower case, with the prefix {@link BaseCmd#RESPONSE_SUFFIX}
*/
public static String getResponseNameByClass(Class<?> clazz) {
return getCommandNameByClass(clazz).toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
}
/**
* 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 List<Long> getEntityOwnerIds() {
return null;
}
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.getAccountType() == caller.getType()) {
roleIsAllowed = true;
break;
}
}
}
if (roleIsAllowed) {
validFields.add(field);
} else {
s_logger.debug("Ignoring parameter " + 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 doesn't 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;
}
/**
* Commands that generate action events associated to a resource and
* async commands that want to be tracked as part of the listXXX commands
* need to provide implementations of the two following methods,
* getApiResourceId() and getApiResourceType()
*
* getApiResourceId() should return the id of the object the async command is executing on
* getApiResourceType() should return a type from the ApiCommandResourceType enumeration
*/
public Long getApiResourceId() {
return null;
}
public ApiCommandResourceType getApiResourceType() {
return ApiCommandResourceType.None;
}
public Map<String, String> convertDetailsToMap(Map details) {
Map<String, String> detailsMap = new HashMap<String, String>();
if (MapUtils.isNotEmpty(details)) {
Collection parameterCollection = details.values();
Iterator iter = parameterCollection.iterator();
while (iter.hasNext()) {
HashMap<String, String> value = (HashMap<String, String>)iter.next();
for (Map.Entry<String,String> entry: value.entrySet()) {
detailsMap.put(entry.getKey(),entry.getValue());
}
}
}
return detailsMap;
}
}