server: ability to create disk offerings for domain(s) and zone(s)

Allows creating storage offerings associated with particular domain(s) and zone(s). In create disk/storage offfering form UI, a mult-select control has been addded to select desired zone(s) and domain select element has been made multi-select.
createDiskOffering API has been modified to allow passing list of domain and zone IDs with keys domainids and zoneids respectively. These lists are stored in DB in cloud.disk_offering_details table with 'domainids' and 'zoneids' key as string of comma separated list of IDs. Response for create, update and list disk offering APIs will return domainids, domainnames, zoneids and zonenames in details object of offering.
listDiskOfferings API has been modified to allow passing zoneid to return only offerings which are associated with the zone.

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2019-03-19 18:08:47 +05:30
parent 2aa4842a94
commit c85b3e597a
30 changed files with 532 additions and 125 deletions

View File

@ -25,6 +25,7 @@ import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import com.cloud.dc.DataCenter;
import com.cloud.domain.Domain;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.offering.DiskOffering;
@ -98,7 +99,7 @@ public interface AccountService {
void checkAccess(Account account, ServiceOffering so) throws PermissionDeniedException;
void checkAccess(Account account, DiskOffering dof) throws PermissionDeniedException;
void checkAccess(Account account, DiskOffering dof, DataCenter zone) throws PermissionDeniedException;
void checkAccess(User user, ControlledEntity entity);

View File

@ -138,5 +138,5 @@ public interface SecurityChecker extends Adapter {
public boolean checkAccess(Account account, ServiceOffering so) throws PermissionDeniedException;
boolean checkAccess(Account account, DiskOffering dof) throws PermissionDeniedException;
boolean checkAccess(Account account, DiskOffering dof, DataCenter zone) throws PermissionDeniedException;
}

View File

@ -113,6 +113,8 @@ public class ApiConstants {
public static final String DOMAIN_PATH = "domainpath";
public static final String DOMAIN_ID = "domainid";
public static final String DOMAIN__ID = "domainId";
public static final String DOMAIN_ID_LIST = "domainids";
public static final String DOMAIN_NAME_LIST = "domainnames";
public static final String DURATION = "duration";
public static final String ELIGIBLE = "eligible";
public static final String EMAIL = "email";
@ -711,6 +713,7 @@ public class ApiConstants {
public static final String NETSCALER_SERVICEPACKAGE_ID = "netscalerservicepackageid";
public static final String ZONE_ID_LIST = "zoneids";
public static final String ZONE_NAME_LIST = "zonenames";
public static final String DESTINATION_ZONE_ID_LIST = "destzoneids";
public static final String ADMIN = "admin";
public static final String CHECKSUM_PARAMETER_PREFIX_DESCRIPTION = "The parameter containing the checksum will be considered a MD5sum if it is not prefixed\n"

View File

@ -16,7 +16,8 @@
// under the License.
package org.apache.cloudstack.api.command.admin.offering;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
@ -26,10 +27,12 @@ import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.DiskOfferingResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.log4j.Logger;
import com.cloud.storage.Storage.ProvisioningType;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.storage.Storage.ProvisioningType;
import com.cloud.user.Account;
@APICommand(name = "createDiskOffering", description = "Creates a disk offering.", responseObject = DiskOfferingResponse.class,
@ -64,6 +67,24 @@ public class CreateDiskOfferingCmd extends BaseCmd {
description = "the ID of the containing domain, null for public offerings")
private Long domainId;
@Parameter(name = ApiConstants.DOMAIN_ID_LIST,
type = CommandType.LIST,
collectionType = CommandType.UUID,
entityType = DomainResponse.class,
required = false,
description = "the ID of the domains offering is associated with, null for all domain offerings",
since = "4.13")
private List<Long> domainIds;
@Parameter(name = ApiConstants.ZONE_ID_LIST,
type = CommandType.LIST,
collectionType = CommandType.UUID,
entityType = ZoneResponse.class,
required = false,
description = "the ID of the zones offering is associated with, null for all zone offerings",
since = "4.13")
private List<Long> zoneIds;
@Parameter(name = ApiConstants.STORAGE_TYPE, type = CommandType.STRING, description = "the storage type of the disk offering. Values are local and shared.")
private String storageType = ServiceOffering.StorageType.shared.toString();
@ -170,6 +191,20 @@ public class CreateDiskOfferingCmd extends BaseCmd {
return domainId;
}
public List<Long> getDomainIds() {
if (domainId != null) {
if (domainIds == null) {
domainIds = new ArrayList<>();
}
domainIds.add(domainId);
}
return domainIds;
}
public List<Long> getZoneIds() {
return zoneIds;
}
public Long getBytesReadRate() {
return bytesReadRate;
}

View File

@ -16,8 +16,6 @@
// under the License.
package org.apache.cloudstack.api.command.admin.offering;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
@ -25,6 +23,7 @@ import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.DiskOfferingResponse;
import org.apache.log4j.Logger;
import com.cloud.offering.DiskOffering;
import com.cloud.user.Account;

View File

@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.api.command.user.offering;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand;
@ -42,6 +43,13 @@ public class ListDiskOfferingsCmd extends BaseListDomainResourcesCmd {
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the disk offering")
private String diskOfferingName;
@Parameter(name = ApiConstants.ZONE_ID,
type = CommandType.UUID,
entityType = ZoneResponse.class,
description = "id of zone disk offering is associated with",
since = "4.13")
private Long zoneId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -54,6 +62,10 @@ public class ListDiskOfferingsCmd extends BaseListDomainResourcesCmd {
return diskOfferingName;
}
public Long getZoneId() {
return zoneId;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -17,8 +17,7 @@
package org.apache.cloudstack.api.response;
import java.util.Date;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
@ -26,6 +25,7 @@ import org.apache.cloudstack.api.EntityReference;
import com.cloud.offering.DiskOffering;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
@EntityReference(value = DiskOffering.class)
public class DiskOfferingResponse extends BaseResponse {
@ -144,6 +144,10 @@ public class DiskOfferingResponse extends BaseResponse {
@Param(description = "whether to display the offering to the end user or not.")
private Boolean displayOffering;
@SerializedName(ApiConstants.DETAILS)
@Param(description = "the details of the disk offering", since = "4.13.0")
private Map<String, String> details;
public Boolean getDisplayOffering() {
return displayOffering;
}
@ -328,4 +332,12 @@ public class DiskOfferingResponse extends BaseResponse {
public void setIopsWriteRateMaxLength(Long iopsWriteRateMaxLength) {
this.iopsWriteRateMaxLength = iopsWriteRateMaxLength;
}
public Map<String, String> getDetails() {
return details;
}
public void setDetails(Map<String, String> details) {
this.details = details;
}
}

View File

@ -177,7 +177,7 @@ public interface ConfigurationManager {
void checkZoneAccess(Account caller, DataCenter zone);
void checkDiskOfferingAccess(Account caller, DiskOffering dof);
void checkDiskOfferingAccess(Account caller, DiskOffering dof, DataCenter zone);
/**
* Creates a new network offering

View File

@ -121,4 +121,6 @@ public interface DataCenterDao extends GenericDao<DataCenterVO, Long> {
List<DataCenterVO> findByKeyword(String keyword);
List<DataCenterVO> listAllZones();
List<DataCenterVO> list(Object[] ids);
}

View File

@ -437,4 +437,12 @@ public class DataCenterDaoImpl extends GenericDaoBase<DataCenterVO, Long> implem
return dcs;
}
@Override
public List<DataCenterVO> list(Object[] ids) {
SearchBuilder<DataCenterVO> sb = createSearchBuilder();
SearchCriteria<DataCenterVO> sc = sb.create();
sc.addAnd("id", SearchCriteria.Op.IN, ids);
return listBy(sc);
}
}

View File

@ -40,4 +40,6 @@ public interface DomainDao extends GenericDao<DomainVO, Long> {
Set<Long> getDomainParentIds(long domainId);
List<Long> getDomainChildrenIds(String path);
List<DomainVO> list(Object[] ids);
}

View File

@ -291,4 +291,11 @@ public class DomainDaoImpl extends GenericDaoBase<DomainVO, Long> implements Dom
return parentDomains;
}
@Override
public List<DomainVO> list(Object[] ids) {
SearchBuilder<DomainVO> sb = createSearchBuilder();
SearchCriteria<DomainVO> sc = sb.create();
sc.addAnd("id", SearchCriteria.Op.IN, ids);
return listBy(sc);
}
}

View File

@ -51,7 +51,7 @@ public class DiskOfferingVO implements DiskOffering {
long id;
@Column(name = "domain_id")
Long domainId;
Long domainId = null;
@Column(name = "unique_name")
private String uniqueName;
@ -182,6 +182,24 @@ public class DiskOfferingVO implements DiskOffering {
this.cacheMode = cacheMode;
}
public DiskOfferingVO(String name, String displayText, Storage.ProvisioningType provisioningType, long diskSize, String tags, boolean isCustomized, Boolean isCustomizedIops,
Long minIops, Long maxIops) {
this.name = name;
this.displayText = displayText;
this.provisioningType = provisioningType;
this.diskSize = diskSize;
this.tags = tags;
recreatable = false;
type = Type.Disk;
useLocalStorage = false;
customized = isCustomized;
uuid = UUID.randomUUID().toString();
customizedIops = isCustomizedIops;
this.minIops = minIops;
this.maxIops = maxIops;
state = State.Active;
}
public DiskOfferingVO(Long domainId, String name, String displayText, Storage.ProvisioningType provisioningType, long diskSize, String tags, boolean isCustomized, Boolean isCustomizedIops,
Long minIops, Long maxIops) {
this.domainId = domainId;

View File

@ -16,42 +16,45 @@
// under the License.
package com.cloud.storage.dao;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.persistence.EntityExistsException;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.springframework.stereotype.Component;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.offering.DiskOffering.Type;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.utils.db.Attribute;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.SearchCriteria.Op;
import com.google.common.base.Strings;
@Component
public class DiskOfferingDaoImpl extends GenericDaoBase<DiskOfferingVO, Long> implements DiskOfferingDao {
private final SearchBuilder<DiskOfferingVO> DomainIdSearch;
@Inject
protected DiskOfferingDetailsDao detailsDao;
private final SearchBuilder<DiskOfferingVO> PrivateDiskOfferingSearch;
private final SearchBuilder<DiskOfferingVO> PublicDiskOfferingSearch;
protected final SearchBuilder<DiskOfferingVO> UniqueNameSearch;
private final Attribute _typeAttr;
protected DiskOfferingDaoImpl() {
DomainIdSearch = createSearchBuilder();
DomainIdSearch.and("domainId", DomainIdSearch.entity().getDomainId(), SearchCriteria.Op.EQ);
DomainIdSearch.and("removed", DomainIdSearch.entity().getRemoved(), SearchCriteria.Op.NULL);
DomainIdSearch.done();
PrivateDiskOfferingSearch = createSearchBuilder();
PrivateDiskOfferingSearch.and("diskSize", PrivateDiskOfferingSearch.entity().getDiskSize(), SearchCriteria.Op.EQ);
PrivateDiskOfferingSearch.done();
PublicDiskOfferingSearch = createSearchBuilder();
PublicDiskOfferingSearch.and("domainId", PublicDiskOfferingSearch.entity().getDomainId(), SearchCriteria.Op.NULL);
PublicDiskOfferingSearch.and("system", PublicDiskOfferingSearch.entity().isSystemUse(), SearchCriteria.Op.EQ);
PublicDiskOfferingSearch.and("removed", PublicDiskOfferingSearch.entity().getRemoved(), SearchCriteria.Op.NULL);
PublicDiskOfferingSearch.done();
@ -65,11 +68,7 @@ public class DiskOfferingDaoImpl extends GenericDaoBase<DiskOfferingVO, Long> im
@Override
public List<DiskOfferingVO> listByDomainId(long domainId) {
SearchCriteria<DiskOfferingVO> sc = DomainIdSearch.create();
sc.setParameters("domainId", domainId);
// FIXME: this should not be exact match, but instead should find all
// available disk offerings from parent domains
return listBy(sc);
return filterOfferingsForDomain(listAll(), domainId);
}
@Override
@ -108,7 +107,19 @@ public class DiskOfferingDaoImpl extends GenericDaoBase<DiskOfferingVO, Long> im
public List<DiskOfferingVO> findPublicDiskOfferings() {
SearchCriteria<DiskOfferingVO> sc = PublicDiskOfferingSearch.create();
sc.setParameters("system", false);
return listBy(sc);
List<DiskOfferingVO> offerings = listBy(sc);
if(offerings!=null) {
for (int i = offerings.size() - 1; i >= 0; i--) {
DiskOfferingVO offering = offerings.get(i);
Map<String, String> offeringDetails = detailsDao.listDetailsKeyPairs(offering.getId());
if (!Strings.isNullOrEmpty(offeringDetails.get(ApiConstants.DOMAIN_ID_LIST))) {
// TODO: For ROOT domainId?
offerings.remove(i);
}
}
}
return offerings;
}
@Override
@ -145,4 +156,26 @@ public class DiskOfferingDaoImpl extends GenericDaoBase<DiskOfferingVO, Long> im
return update(id, diskOffering);
}
private List<DiskOfferingVO> filterOfferingsForDomain(final List<DiskOfferingVO> offerings, Long domainId) {
List<DiskOfferingVO> filteredOfferings = null;
if (offerings != null && !offerings.isEmpty() && domainId != null) {
filteredOfferings = new ArrayList<>(offerings);
for (int i = filteredOfferings.size() - 1; i >= 0; i--) {
DiskOfferingVO offering = offerings.get(i);
Map<String, String> offeringDetails = detailsDao.listDetailsKeyPairs(offering.getId());
if (!Strings.isNullOrEmpty(offeringDetails.get(ApiConstants.DOMAIN_ID_LIST))) {
String[] domainIdsArray = offeringDetails.get(ApiConstants.DOMAIN_ID_LIST).split(",");
List<Long> domainIds = new ArrayList<>();
for (String dIdStr : domainIdsArray) {
domainIds.add(Long.valueOf(dIdStr.trim()));
}
if (!domainIds.contains(domainId)) {
filteredOfferings.remove(i);
}
}
}
}
return filteredOfferings;
}
}

View File

@ -41,6 +41,7 @@ import org.apache.cloudstack.context.CallContext;
import com.cloud.api.query.vo.ControlledViewEntity;
import com.cloud.configuration.ResourceLimit;
import com.cloud.configuration.dao.ResourceCountDao;
import com.cloud.dc.DataCenter;
import com.cloud.domain.Domain;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.PermissionDeniedException;
@ -435,7 +436,7 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
}
@Override
public void checkAccess(Account account, DiskOffering dof) throws PermissionDeniedException {
public void checkAccess(Account account, DiskOffering dof, DataCenter zone) throws PermissionDeniedException {
// TODO Auto-generated method stub
}

View File

@ -16,13 +16,18 @@
// under the License.
package com.cloud.acl;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.affinity.AffinityGroup;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.springframework.stereotype.Component;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DedicatedResourceVO;
@ -44,6 +49,7 @@ import com.cloud.user.AccountService;
import com.cloud.user.User;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.component.AdapterBase;
import com.google.common.base.Strings;
@Component
public class DomainChecker extends AdapterBase implements SecurityChecker {
@ -64,6 +70,8 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
private DedicatedResourceDao _dedicatedDao;
@Inject
AccountService _accountService;
@Inject
DiskOfferingDetailsDao diskOfferingDetailsDao;
protected DomainChecker() {
super();
@ -167,13 +175,15 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
}
@Override
public boolean checkAccess(Account account, DiskOffering dof) throws PermissionDeniedException {
if (account == null || dof == null || dof.getDomainId() == null) {//public offering
return true;
public boolean checkAccess(Account account, DiskOffering dof, DataCenter zone) throws PermissionDeniedException {
boolean isAccess = false;
Map<String, String> details = null;
if (account == null || dof == null) {//public offering
isAccess = true;
} else {
//admin has all permissions
if (_accountService.isRootAdmin(account.getId())) {
return true;
isAccess = true;
}
//if account is normal user or domain admin
//check if account's domain is a child of zone's domain (Note: This is made consistent with the list command for disk offering)
@ -181,28 +191,33 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
|| account.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN
|| _accountService.isDomainAdmin(account.getId())
|| account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
if (account.getDomainId() == dof.getDomainId()) {
return true; //disk offering and account at exact node
} else {
Domain domainRecord = _domainDao.findById(account.getDomainId());
if (domainRecord != null) {
while (true) {
if (domainRecord.getId() == dof.getDomainId()) {
//found as a child
return true;
}
if (domainRecord.getParent() != null) {
domainRecord = _domainDao.findById(domainRecord.getParent());
} else {
break;
}
details = diskOfferingDetailsDao.listDetailsKeyPairs(dof.getId());
isAccess = true;
if (details.containsKey(ApiConstants.DOMAIN_ID_LIST) &&
!Strings.isNullOrEmpty(details.get(ApiConstants.DOMAIN_ID_LIST))) {
List<String> domainIds = Arrays.asList(details.get(ApiConstants.DOMAIN_ID_LIST).split(","));
for (String domainId : domainIds) {
if (!_domainDao.isChildDomain(Long.valueOf(domainId), account.getDomainId())) {
isAccess = false;
break;
}
}
}
}
}
// Check for zones
if (isAccess && dof != null && zone != null) {
if (details == null)
details = diskOfferingDetailsDao.listDetailsKeyPairs(dof.getId());
if (details.containsKey(ApiConstants.ZONE_ID_LIST) &&
!Strings.isNullOrEmpty(details.get(ApiConstants.ZONE_ID_LIST))) {
List<String> zoneIds = Arrays.asList(details.get(ApiConstants.ZONE_ID_LIST).split(","));
isAccess = zoneIds.contains(String.valueOf(zone.getId()));
}
}
//not found
return false;
return isAccess;
}
@Override

View File

@ -33,6 +33,7 @@ import org.apache.cloudstack.affinity.AffinityGroup;
import org.apache.cloudstack.affinity.AffinityGroupResponse;
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
import org.apache.cloudstack.api.ApiCommandJobType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiConstants.DomainDetails;
import org.apache.cloudstack.api.ApiConstants.HostDetails;
import org.apache.cloudstack.api.ApiConstants.VMDetails;
@ -68,6 +69,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.jobs.AsyncJob;
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
@ -314,6 +316,7 @@ import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
import com.cloud.user.AccountManager;
import com.cloud.network.dao.FirewallRulesDcidrsDao;
import com.google.common.base.Strings;
public class ApiDBUtils {
private static ManagementServer s_ms;
@ -335,6 +338,7 @@ public class ApiDBUtils {
static CapacityDao s_capacityDao;
static DiskOfferingDao s_diskOfferingDao;
static DiskOfferingJoinDao s_diskOfferingJoinDao;
static DiskOfferingDetailsDao s_diskOfferingDetailsDao;
static DataCenterJoinDao s_dcJoinDao;
static DomainDao s_domainDao;
static DomainJoinDao s_domainJoinDao;
@ -471,6 +475,8 @@ public class ApiDBUtils {
@Inject
private DiskOfferingJoinDao diskOfferingJoinDao;
@Inject
private DiskOfferingDetailsDao diskOfferingDetailsDao;
@Inject
private DomainDao domainDao;
@Inject
private DomainJoinDao domainJoinDao;
@ -688,6 +694,7 @@ public class ApiDBUtils {
s_dcJoinDao = dcJoinDao;
s_diskOfferingDao = diskOfferingDao;
s_diskOfferingJoinDao = diskOfferingJoinDao;
s_diskOfferingDetailsDao = diskOfferingDetailsDao;
s_domainDao = domainDao;
s_domainJoinDao = domainJoinDao;
s_domainRouterDao = domainRouterDao;
@ -1906,7 +1913,40 @@ public class ApiDBUtils {
}
public static DiskOfferingResponse newDiskOfferingResponse(DiskOfferingJoinVO offering) {
return s_diskOfferingJoinDao.newDiskOfferingResponse(offering);
DiskOfferingResponse diskOfferingResponse = s_diskOfferingJoinDao.newDiskOfferingResponse(offering);
if(diskOfferingResponse!=null) {
Map<String, String> details = s_diskOfferingDetailsDao.listDetailsKeyPairs(offering.getId());
if (details != null && !details.isEmpty()) {
if(details.containsKey(ApiConstants.DOMAIN_ID_LIST) &&
!Strings.isNullOrEmpty(details.get(ApiConstants.DOMAIN_ID_LIST))) {
String[] domainIdsArray = details.get(ApiConstants.DOMAIN_ID_LIST).split(",");
List<DomainVO> domains = s_domainDao.list(domainIdsArray);
List<String> domainIdsList = new ArrayList<>();
List<String> domainNamesList = new ArrayList<>();
for (DomainVO domain : domains) {
domainIdsList.add(domain.getUuid());
domainNamesList.add(domain.getName());
}
details.put(ApiConstants.DOMAIN_ID_LIST, String.join(",", domainIdsList));
details.put(ApiConstants.DOMAIN_NAME_LIST, String.join(", ", domainNamesList));
}
if(details.containsKey(ApiConstants.ZONE_ID_LIST) &&
!Strings.isNullOrEmpty(details.get(ApiConstants.ZONE_ID_LIST))) {
String[] zoneIdsArray = details.get(ApiConstants.ZONE_ID_LIST).split(",");
List<DataCenterVO> zones = s_zoneDao.list(zoneIdsArray);
List<String> zoneIdsList = new ArrayList<>();
List<String> zoneNamesList = new ArrayList<>();
for (DataCenterVO zone : zones) {
zoneIdsList.add(zone.getUuid());
zoneNamesList.add(zone.getName());
}
details.put(ApiConstants.ZONE_ID_LIST, String.join(",", zoneIdsList));
details.put(ApiConstants.ZONE_NAME_LIST, String.join(", ", zoneNamesList));
}
diskOfferingResponse.setDetails(details);
}
}
return diskOfferingResponse;
}
public static DiskOfferingJoinVO newDiskOfferingView(DiskOffering offering) {

View File

@ -25,14 +25,13 @@ import java.util.Set;
import javax.inject.Inject;
import com.cloud.cluster.ManagementServerHostVO;
import com.cloud.cluster.dao.ManagementServerHostDao;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO;
import org.apache.cloudstack.affinity.AffinityGroupResponse;
import org.apache.cloudstack.affinity.AffinityGroupVMMapVO;
import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao;
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
import org.apache.cloudstack.api.ResourceDetail;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
@ -108,6 +107,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.query.QueryService;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@ -156,6 +156,8 @@ import com.cloud.api.query.vo.TemplateJoinVO;
import com.cloud.api.query.vo.UserAccountJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.api.query.vo.VolumeJoinVO;
import com.cloud.cluster.ManagementServerHostVO;
import com.cloud.cluster.dao.ManagementServerHostDao;
import com.cloud.dc.DedicatedResourceVO;
import com.cloud.dc.dao.DedicatedResourceDao;
import com.cloud.domain.Domain;
@ -223,6 +225,7 @@ import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
import com.google.common.base.Strings;
@Component
public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements QueryService, Configurable {
@ -321,6 +324,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
@Inject
private DiskOfferingJoinDao _diskOfferingJoinDao;
@Inject
private DiskOfferingDetailsDao diskOfferingDetailsDao;
@Inject
private ServiceOfferingJoinDao _srvOfferingJoinDao;
@ -2538,7 +2544,6 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
}
}
List<Long> domainIds = null;
// For non-root users, only return all offerings for the user's domain,
// and everything above till root
if ((_accountMgr.isNormalUser(account.getId()) || _accountMgr.isDomainAdmin(account.getId())) || account.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) {
@ -2546,27 +2551,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
throw new InvalidParameterValueException("Only ROOT admins and Domain admins can list disk offerings with isrecursive=true");
}
DomainVO domainRecord = _domainDao.findById(account.getDomainId());
sc.addAnd("domainPath", SearchCriteria.Op.LIKE, domainRecord.getPath() + "%");
} else { // domain + all ancestors
// find all domain Id up to root domain for this account
domainIds = new ArrayList<Long>();
DomainVO domainRecord = _domainDao.findById(account.getDomainId());
if (domainRecord == null) {
s_logger.error("Could not find the domainId for account:" + account.getAccountName());
throw new CloudAuthenticationException("Could not find the domainId for account:" + account.getAccountName());
}
domainIds.add(domainRecord.getId());
while (domainRecord.getParent() != null) {
domainRecord = _domainDao.findById(domainRecord.getParent());
domainIds.add(domainRecord.getId());
}
SearchCriteria<DiskOfferingJoinVO> spc = _diskOfferingJoinDao.createSearchCriteria();
spc.addOr("domainId", SearchCriteria.Op.IN, domainIds.toArray());
spc.addOr("domainId", SearchCriteria.Op.NULL); // include public offering as where
sc.addAnd("domainId", SearchCriteria.Op.SC, spc);
sc.addAnd("systemUse", SearchCriteria.Op.EQ, false); // non-root users should not see system offering at all
}
@ -2610,7 +2595,56 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
* domain.getPath() + "%"); // }
*/
return _diskOfferingJoinDao.searchAndCount(sc, searchFilter);
Pair<List<DiskOfferingJoinVO>, Integer> result = _diskOfferingJoinDao.searchAndCount(sc, searchFilter);
// Remove offerings that are not associated with caller's domain and passed zone
// TODO: Better approach
if (result.first() != null && !result.first().isEmpty()) {
List<DiskOfferingJoinVO> offerings = result.first();
for (int i = offerings.size() - 1; i >= 0; i--) {
DiskOfferingJoinVO offering = offerings.get(i);
Map<String, String> details = diskOfferingDetailsDao.listDetailsKeyPairs(offering.getId());
boolean toRemove = account.getType() == Account.ACCOUNT_TYPE_ADMIN ? false : isRecursive;
if (account.getType() != Account.ACCOUNT_TYPE_ADMIN &&
details.containsKey(ApiConstants.DOMAIN_ID_LIST) &&
!Strings.isNullOrEmpty(details.get(ApiConstants.DOMAIN_ID_LIST))) {
toRemove = true;
String[] domainIdsArray = details.get(ApiConstants.DOMAIN_ID_LIST).split(",");
for (String dIdStr : domainIdsArray) {
Long dId = Long.valueOf(dIdStr.trim());
if(isRecursive) {
if (_domainDao.isChildDomain(account.getDomainId(), dId)) {
toRemove = false;
break;
}
} else {
if (_domainDao.isChildDomain(dId, account.getDomainId())) {
toRemove = false;
break;
}
}
}
}
if (toRemove) {
offerings.remove(i);
} else {
// If zoneid is passed remove offerings that are not associated with the zone
if (cmd.getZoneId() != null) {
final Long zoneId = cmd.getZoneId();
if (details.containsKey(ApiConstants.ZONE_ID_LIST) &&
!Strings.isNullOrEmpty(details.get(ApiConstants.ZONE_ID_LIST))) {
String[] zoneIdsArray = details.get(ApiConstants.ZONE_ID_LIST).split(",");
List<Long> zoneIds = new ArrayList<>();
for (String zId : zoneIdsArray)
zoneIds.add(Long.valueOf(zId.trim()));
if (!zoneIds.contains(zoneId)) {
offerings.remove(i);
}
}
}
}
}
}
return result;
}
private List<ServiceOfferingJoinVO> filterOfferingsOnCurrentTags(List<ServiceOfferingJoinVO> offerings, ServiceOfferingVO currentVmOffering) {

View File

@ -86,6 +86,7 @@ import org.apache.cloudstack.region.PortableIpVO;
import org.apache.cloudstack.region.Region;
import org.apache.cloudstack.region.RegionVO;
import org.apache.cloudstack.region.dao.RegionDao;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
@ -267,6 +268,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
@Inject
DiskOfferingDao _diskOfferingDao;
@Inject
DiskOfferingDetailsDao diskOfferingDetailsDao;
@Inject
NetworkOfferingDao _networkOfferingDao;
@Inject
VlanDao _vlanDao;
@ -2608,7 +2611,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
protected DiskOfferingVO createDiskOffering(final Long userId, final Long domainId, final String name, final String description, final String provisioningType,
protected DiskOfferingVO createDiskOffering(final Long userId, final List<Long> domainIds, final List<Long> zoneIds, final String name, final String description, final String provisioningType,
final Long numGibibytes, String tags, boolean isCustomized, final boolean localStorageRequired,
final boolean isDisplayOfferingEnabled, final Boolean isCustomizedIops, Long minIops, Long maxIops,
Long bytesReadRate, Long bytesReadRateMax, Long bytesReadRateMaxLength,
@ -2654,6 +2657,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
// Filter child domains when both parent and child domains are present
List<Long> filteredDomainIds = filterChildSubDomains(domainIds);
// Check if user exists in the system
final User user = _userDao.findById(userId);
@ -2662,21 +2667,30 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
final Account account = _accountDao.findById(user.getAccountId());
if (account.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) {
if (domainId == null) {
if (filteredDomainIds.isEmpty()) {
throw new InvalidParameterValueException("Unable to create public disk offering by id " + userId + " because it is domain-admin");
}
if (tags != null) {
throw new InvalidParameterValueException("Unable to create disk offering with storage tags by id " + userId + " because it is domain-admin");
}
if (! _domainDao.isChildDomain(account.getDomainId(), domainId)) {
throw new InvalidParameterValueException("Unable to create disk offering by another domain admin with id " + userId);
for (Long domainId : filteredDomainIds) {
if (domainId == null || !_domainDao.isChildDomain(account.getDomainId(), domainId)) {
throw new InvalidParameterValueException("Unable to create disk offering by another domain admin with id " + userId);
}
}
} else if (account.getType() != Account.ACCOUNT_TYPE_ADMIN) {
throw new InvalidParameterValueException("Unable to create disk offering by id " + userId + " because it is not root-admin or domain-admin");
}
if (zoneIds != null) {
for (Long zoneId : zoneIds) {
if (_zoneDao.findById(zoneId) == null)
throw new InvalidParameterValueException("Unable to create disk offering associated with invalid zone, " + zoneId);
}
}
tags = StringUtils.cleanupTags(tags);
final DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, typedProvisioningType, diskSize, tags, isCustomized,
final DiskOfferingVO newDiskOffering = new DiskOfferingVO(name, description, typedProvisioningType, diskSize, tags, isCustomized,
isCustomizedIops, minIops, maxIops);
newDiskOffering.setUseLocalStorage(localStorageRequired);
newDiskOffering.setDisplayOffering(isDisplayOfferingEnabled);
@ -2727,11 +2741,22 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
CallContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId());
final DiskOfferingVO offering = _diskOfferingDao.persist(newDiskOffering);
if (offering != null) {
if(!filteredDomainIds.isEmpty()) {
List<String> domainIdsStringList = new ArrayList<>();
for(Long domainId : filteredDomainIds)
domainIdsStringList.add(String.valueOf(domainId));
diskOfferingDetailsDao.addDetail(offering.getId(), ApiConstants.DOMAIN_ID_LIST, String.join(",", domainIdsStringList), true);
}
if(zoneIds!=null && !zoneIds.isEmpty()) {
List<String> zoneIdsStringList = new ArrayList<>();
for(Long zoneId : zoneIds)
zoneIdsStringList.add(String.valueOf(zoneId));
diskOfferingDetailsDao.addDetail(offering.getId(), ApiConstants.ZONE_ID_LIST, String.join(",", zoneIdsStringList), true);
}
CallContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId());
return offering;
} else {
return null;
}
return null;
}
@Override
@ -2746,11 +2771,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
// by
// default
final String tags = cmd.getTags();
// Long domainId = cmd.getDomainId() != null ? cmd.getDomainId() :
// Long.valueOf(DomainVO.ROOT_DOMAIN); // disk offering
// always gets created under the root domain.Bug # 6055 if not passed in
// cmd
final Long domainId = cmd.getDomainId();
final List<Long> domainIds = cmd.getDomainIds();
final List<Long> zoneIds = cmd.getZoneIds();
if (!isCustomized && numGibibytes == null) {
throw new InvalidParameterValueException("Disksize is required for a non-customized disk offering");
@ -2788,7 +2812,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
final Integer hypervisorSnapshotReserve = cmd.getHypervisorSnapshotReserve();
final Long userId = CallContext.current().getCallingUserId();
return createDiskOffering(userId, domainId, name, description, provisioningType, numGibibytes, tags, isCustomized,
return createDiskOffering(userId, domainIds, zoneIds, name, description, provisioningType, numGibibytes, tags, isCustomized,
localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops,
maxIops, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength,
iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength,
@ -2806,6 +2830,15 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
// Check if diskOffering exists
final DiskOffering diskOfferingHandle = _entityMgr.findById(DiskOffering.class, diskOfferingId);
List<Long> existingDomainIds = new ArrayList<>();
Map<String, String> details = diskOfferingDetailsDao.listDetailsKeyPairs(diskOfferingId);
if (details.containsKey(ApiConstants.DOMAIN_ID_LIST) &&
!Strings.isNullOrEmpty(details.get(ApiConstants.DOMAIN_ID_LIST))) {
String[] domainIdsArray = details.get(ApiConstants.DOMAIN_ID_LIST).split(",");
for (String dIdStr : domainIdsArray) {
existingDomainIds.add(Long.valueOf(dIdStr.trim()));
}
}
if (diskOfferingHandle == null) {
throw new InvalidParameterValueException("Unable to find disk offering by id " + diskOfferingId);
@ -2820,12 +2853,15 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
throw new InvalidParameterValueException("Unable to find active user by id " + userId);
}
final Account account = _accountDao.findById(user.getAccountId());
if (account.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) {
if (diskOfferingHandle.getDomainId() == null) {
if (existingDomainIds.isEmpty()) {
throw new InvalidParameterValueException("Unable to update public disk offering by id " + userId + " because it is domain-admin");
}
if (! _domainDao.isChildDomain(account.getDomainId(), diskOfferingHandle.getDomainId() )) {
throw new InvalidParameterValueException("Unable to update disk offering by another domain admin with id " + userId);
for (Long domainId : existingDomainIds) {
if (!_domainDao.isChildDomain(account.getDomainId(), domainId)) {
throw new InvalidParameterValueException("Unable to update disk offering by another domain admin with id " + userId);
}
}
} else if (account.getType() != Account.ACCOUNT_TYPE_ADMIN) {
throw new InvalidParameterValueException("Unable to update disk offering by id " + userId + " because it is not root-admin or domain-admin");
@ -2908,11 +2944,22 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
final Account account = _accountDao.findById(user.getAccountId());
if (account.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) {
if (offering.getDomainId() == null) {
List<Long> existingDomainIds = new ArrayList<>();
Map<String, String> details = diskOfferingDetailsDao.listDetailsKeyPairs(diskOfferingId);
if (details.containsKey(ApiConstants.DOMAIN_ID_LIST) &&
!Strings.isNullOrEmpty(details.get(ApiConstants.DOMAIN_ID_LIST))) {
String[] domainIdsArray = details.get(ApiConstants.DOMAIN_ID_LIST).split(",");
for (String dIdStr : domainIdsArray) {
existingDomainIds.add(Long.valueOf(dIdStr.trim()));
}
}
if (existingDomainIds.isEmpty()) {
throw new InvalidParameterValueException("Unable to delete public disk offering by id " + userId + " because it is domain-admin");
}
if (! _domainDao.isChildDomain(account.getDomainId(), offering.getDomainId() )) {
throw new InvalidParameterValueException("Unable to delete disk offering by another domain admin with id " + userId);
for (Long domainId : existingDomainIds) {
if (!_domainDao.isChildDomain(account.getDomainId(), domainId)) {
throw new InvalidParameterValueException("Unable to delete disk offering by another domain admin with id " + userId);
}
}
} else if (account.getType() != Account.ACCOUNT_TYPE_ADMIN) {
throw new InvalidParameterValueException("Unable to delete disk offering by id " + userId + " because it is not root-admin or domain-admin");
@ -4229,15 +4276,15 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
@Override
public void checkDiskOfferingAccess(final Account caller, final DiskOffering dof) {
public void checkDiskOfferingAccess(final Account caller, final DiskOffering dof, DataCenter zone) {
for (final SecurityChecker checker : _secChecker) {
if (checker.checkAccess(caller, dof)) {
if (checker.checkAccess(caller, dof, zone)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access granted to " + caller + " to disk offering:" + dof.getId() + " by " + checker.getName());
}
return;
} else {
throw new PermissionDeniedException("Access denied to " + caller + " by " + checker.getName());
throw new PermissionDeniedException(String.format("Access denied to %s for disk offering: %s, zone: %s by %s", caller, dof.getUuid(), zone.getUuid(), checker.getName()));
}
}
@ -5758,6 +5805,30 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
return false;
}
private List<Long> filterChildSubDomains(final List<Long> domainIds) {
List<Long> filteredDomainIds = new ArrayList<>();
if (domainIds != null) {
filteredDomainIds.addAll(domainIds);
}
if (filteredDomainIds.size() > 1) {
for (int i = filteredDomainIds.size() - 1; i >= 1; i--) {
long first = filteredDomainIds.get(i);
for (int j = i - 1; j >= 0; j--) {
long second = filteredDomainIds.get(j);
if (_domainDao.isChildDomain(filteredDomainIds.get(i), filteredDomainIds.get(j))) {
filteredDomainIds.remove(j);
i--;
}
if (_domainDao.isChildDomain(filteredDomainIds.get(j), filteredDomainIds.get(i))) {
filteredDomainIds.remove(i);
break;
}
}
}
}
return filteredDomainIds;
}
public List<SecurityChecker> getSecChecker() {
return _secChecker;
}

View File

@ -436,11 +436,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
throw new InvalidParameterValueException("Please specify a custom sized disk offering.");
}
if (diskOffering.getDomainId() == null) {
// do nothing as offering is public
} else {
_configMgr.checkDiskOfferingAccess(volumeOwner, diskOffering);
}
_configMgr.checkDiskOfferingAccess(volumeOwner, diskOffering, zone);
}
return false;
@ -603,11 +599,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
throw new InvalidParameterValueException("This disk offering does not allow custom size");
}
if (diskOffering.getDomainId() == null) {
// do nothing as offering is public
} else {
_configMgr.checkDiskOfferingAccess(caller, diskOffering);
}
_configMgr.checkDiskOfferingAccess(caller, diskOffering, _dcDao.findById(zoneId));
if (diskOffering.getDiskSize() > 0) {
size = diskOffering.getDiskSize();
@ -666,6 +658,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
// if zoneId is not provided, we default to create volume in the same zone as the snapshot zone.
zoneId = snapshotCheck.getDataCenterId();
}
_configMgr.checkDiskOfferingAccess(null, diskOffering, _dcDao.findById(zoneId));
size = snapshotCheck.getSize(); // ; disk offering is used for tags
// purposes
@ -949,10 +944,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
throw new InvalidParameterValueException("There are no tags on the current disk offering. The new disk offering needs to have no tags, as well.");
}
if (newDiskOffering.getDomainId() != null) {
// not a public offering; check access
_configMgr.checkDiskOfferingAccess(CallContext.current().getCallingAccount(), newDiskOffering);
}
_configMgr.checkDiskOfferingAccess(CallContext.current().getCallingAccount(), newDiskOffering, _dcDao.findById(volume.getDataCenterId()));
if (newDiskOffering.isCustomized()) {
newSize = cmd.getSize();
@ -2187,7 +2179,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
throw new InvalidParameterValueException(String.format("We cannot assign a removed disk offering [id=%s] to a volume. ", newDiskOffering.getUuid()));
}
Account caller = CallContext.current().getCallingAccount();
_accountMgr.checkAccess(caller, newDiskOffering);
DataCenter zone = null;
Volume volume = _volsDao.findById(cmd.getId());
if (volume != null) {
zone = _dcDao.findById(volume.getDataCenterId());
}
_accountMgr.checkAccess(caller, newDiskOffering, zone);
return newDiskOffering;
}

View File

@ -77,6 +77,7 @@ import com.cloud.configuration.ResourceCountVO;
import com.cloud.configuration.ResourceLimit;
import com.cloud.configuration.dao.ResourceCountDao;
import com.cloud.configuration.dao.ResourceLimitDao;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.DedicatedResourceVO;
import com.cloud.dc.dao.DataCenterDao;
@ -2844,13 +2845,15 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
}
@Override
public void checkAccess(Account account, DiskOffering dof) throws PermissionDeniedException {
public void checkAccess(Account account, DiskOffering dof, DataCenter zone) throws PermissionDeniedException {
for (SecurityChecker checker : _securityCheckers) {
if (checker.checkAccess(account, dof)) {
if (checker.checkAccess(account, dof, zone)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access granted to " + account + " to " + dof + " by " + checker.getName());
}
return;
} else {
throw new PermissionDeniedException(String.format("Access denied to %s for disk offering: %s, zone: %s by %s", account, dof.getUuid(), zone.getUuid(), checker.getName()));
}
}

View File

@ -3015,7 +3015,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// Verify that owner can use the service offering
_accountMgr.checkAccess(owner, serviceOffering);
_accountMgr.checkAccess(owner, _diskOfferingDao.findById(diskOfferingId));
_accountMgr.checkAccess(owner, _diskOfferingDao.findById(diskOfferingId), zone);
// Get default guest network in Basic zone
Network defaultNetwork = _networkModel.getExclusiveGuestNetwork(zone.getId());
@ -3074,7 +3074,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// Verify that owner can use the service offering
_accountMgr.checkAccess(owner, serviceOffering);
_accountMgr.checkAccess(owner, _diskOfferingDao.findById(diskOfferingId));
_accountMgr.checkAccess(owner, _diskOfferingDao.findById(diskOfferingId), zone);
// If no network is specified, find system security group enabled network
if (networkIdList == null || networkIdList.isEmpty()) {
@ -3182,7 +3182,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// Verify that owner can use the service offering
_accountMgr.checkAccess(owner, serviceOffering);
_accountMgr.checkAccess(owner, _diskOfferingDao.findById(diskOfferingId));
_accountMgr.checkAccess(owner, _diskOfferingDao.findById(diskOfferingId), zone);
List<HypervisorType> vpcSupportedHTypes = _vpcMgr.getSupportedVpcHypervisors();
if (networkIdList == null || networkIdList.isEmpty()) {

View File

@ -36,6 +36,7 @@ import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import com.cloud.api.query.vo.ControlledViewEntity;
import com.cloud.dc.DataCenter;
import com.cloud.domain.Domain;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.PermissionDeniedException;
@ -217,7 +218,7 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco
}
@Override
public void checkAccess(Account account, DiskOffering dof) throws PermissionDeniedException {
public void checkAccess(Account account, DiskOffering dof, DataCenter zone) throws PermissionDeniedException {
// TODO Auto-generated method stub
}

View File

@ -438,7 +438,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
* @see com.cloud.configuration.ConfigurationManager#checkDiskOfferingAccess(com.cloud.user.Account, com.cloud.offering.DiskOffering)
*/
@Override
public void checkDiskOfferingAccess(Account caller, DiskOffering dof) {
public void checkDiskOfferingAccess(Account caller, DiskOffering dof, DataCenter zone) {
// TODO Auto-generated method stub
}

View File

@ -54,5 +54,6 @@
<bean id="userIpAddressDetailsDao" class="org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDaoImpl" />
<bean id="loadBalancerVMMapDaoImpl" class="com.cloud.network.dao.LoadBalancerVMMapDaoImpl" />
<bean id="imageStoreDaoImpl" class="org.apache.cloudstack.storage.datastore.db.ImageStoreDaoImpl" />
<bean id="messageBus" class="org.apache.cloudstack.framework.messagebus.MessageBusBase" />
<bean id="messageBus" class="org.apache.cloudstack.framework.messagebus.MessageBusBase" />
<bean id="DiskOfferingDetailsDaoImpl" class="org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDaoImpl" />
</beans>

View File

@ -707,6 +707,7 @@ var dictionary = {
"label.dns.1":"DNS 1",
"label.dns.2":"DNS 2",
"label.domain":"Domain",
"label.domains":"Domains",
"label.domain.admin":"Domain Admin",
"label.domain.details":"Domain details",
"label.domain.id":"Domain ID",

View File

@ -1825,8 +1825,9 @@
preFilter: function(args) {
if (isAdmin()) {
} else {
args.$form.find('.form-item[rel=isPublic]').find('input[name=isPublic]').prop('checked', false);
args.$form.find('.form-item[rel=isPublic]').hide();
args.$form.find('.form-item[rel=domainId]').css('display', 'inline-block'); //shown
args.$form.find('.form-item[rel=domain]').css('display', 'inline-block'); //shown
args.$form.find('.form-item[rel=tags]').hide();
}
},
@ -2105,10 +2106,11 @@
isChecked: false,
docID: 'helpDiskOfferingPublic'
},
domainId: {
domain: {
label: 'label.domain',
docID: 'helpDiskOfferingDomain',
dependsOn: 'isPublic',
isMultiple: true,
select: function(args) {
$.ajax({
url: createURL('listDomains'),
@ -2137,6 +2139,42 @@
});
},
isHidden: true
},
zone: {
label: 'label.zone',
docID: 'helpDiskOfferingZone',
isMultiple: true,
validation: {
allzonesonly: true
},
select: function(args) {
$.ajax({
url: createURL("listZones&available=true"),
dataType: "json",
async: true,
success: function(json) {
var zoneObjs = [];
var items = json.listzonesresponse.zone;
if (items != null) {
for (var i = 0; i < items.length; i++) {
zoneObjs.push({
id: items[i].id,
description: items[i].name
});
}
}
if (isAdmin()) {
zoneObjs.unshift({
id: -1,
description: "All Zones"
});
}
args.response.success({
data: zoneObjs
});
}
});
}
}
}
},
@ -2217,8 +2255,41 @@
}
if (args.data.isPublic != "on") {
var domains = "";
if (Object.prototype.toString.call(args.data.domain) === '[object Array]') {
domains = args.data.domain.join(",");
} else {
if (args.data.domain != null) {
domains = args.data.domain;
}
}
if (domains != "") {
$.extend(data, {
domainids: domains
});
}
}
var zones = "";
if (Object.prototype.toString.call(args.data.zone) === '[object Array]') {
var allZonesSelected = false;
args.data.zone.forEach(function (zone) {
if (zone === null) {
allZonesSelected = true;
break;
}
});
if(!allZonesSelected) {
zones = args.data.zone.join(",");
}
} else {
if (args.data.zone != null) {
zones = args.data.zone;
}
}
if (zones != "") {
$.extend(data, {
domainid: args.data.domainId
zoneids: zones
});
}
@ -2390,8 +2461,11 @@
tags: {
label: 'label.storage.tags'
},
domain: {
label: 'label.domain'
domains: {
label: 'label.domains'
},
zones: {
label: 'label.zones'
},
storagetype: {
label: 'label.storage.type'
@ -2410,6 +2484,19 @@
data: data
};
var diskOfferings = cloudStack.listDiskOfferings(listDiskOfferingsOptions);
var diskOffering = diskOfferings[0]
if(diskOffering.details) {
if(diskOffering.details.domainnames) {
$.extend(diskOffering, {
domains: diskOffering.details.domainnames
});
}
if(diskOffering.details.zonenames) {
$.extend(diskOffering, {
zones: diskOffering.details.zonenames
});
}
}
args.response.success({
actionFilter: diskOfferingActionfilter,
data: diskOfferings[0]

View File

@ -383,7 +383,11 @@ cloudStack.docs = {
externalLink: ''
},
helpDiskOfferingDomain: {
desc: 'Select the subdomain in which this offering is available',
desc: 'Select the domains in which this offering is available (Tip: Use Ctrl to choose multiple domains)',
externalLink: ''
},
helpDiskOfferingZone: {
desc: 'Select the zones in which this offering is available (Tip: Use Ctrl to choose multiple zones)',
externalLink: ''
},
// Add domain

View File

@ -377,6 +377,7 @@
// Step 4: Data disk offering
function(args) {
var isRequired = (args.currentData["select-template"] == "select-iso" ? true : false);
var zoneid = args.currentData["zoneid"]
var templateFilter = 'executable'
if (isAdmin()) {
templateFilter = 'all'
@ -384,6 +385,9 @@
$.ajax({
url: createURL("listDiskOfferings"),
dataType: "json",
data: {
zoneid: zoneid
},
async: true,
success: function(json) {
diskOfferingObjs = json.listdiskofferingsresponse.diskoffering;

View File

@ -168,7 +168,7 @@
});
}
var selectedDiskOfferingObj = null;
var diskOfferingsObjList, selectedDiskOfferingObj = null;
cloudStack.sections.storage = {
title: 'label.storage',
@ -274,6 +274,21 @@
});
}
});
args.$select.change(function() {
var diskOfferingSelect = $(this).closest('form').find('select[name=diskOffering]');
if(diskOfferingSelect) {
$(diskOfferingSelect).find('option').remove().end();
var data = {
zoneid: $(this).val(),
};
console.log(data);
var diskOfferings = cloudStack.listDiskOfferings({ data: data });
diskOfferingsObjList = diskOfferings;
$(diskOfferings).each(function() {
$(diskOfferingSelect).append(new Option(this.displaytext, this.id));
});
}
});
}
},
diskOffering: {
@ -281,6 +296,7 @@
docID: 'helpVolumeDiskOffering',
select: function(args) {
var diskOfferings = cloudStack.listDiskOfferings({});
diskOfferingsObjList = diskOfferings;
var items = [];
$(diskOfferings).each(function() {
items.push({
@ -293,7 +309,7 @@
});
args.$select.change(function() {
var diskOfferingId = $(this).val();
$(diskOfferings).each(function() {
$(diskOfferingsObjList).each(function() {
if (this.id == diskOfferingId) {
selectedDiskOfferingObj = this;
return false; //break the $.each() loop