mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	CLOUDSTACK-4168 Root Admin should be able to create 'ExplicitDedication' affinity group at domain level and make it available for all accounts in the domain
Changes: - 'ExcplicitDedication' type of group can be created/deleted by Root admin only - Users can no longer create this type of affinity group - RootAdmin can create this type of affinitygroup at domain level. Such a domain level group is available for all accounts in that domain for listing and for use during deployVM. - The domain level affinitygroup should be visible to the users in that domain, domain admins and Root admin. Conflicts: server/src/com/cloud/api/query/QueryManagerImpl.java server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java
This commit is contained in:
		
							parent
							
								
									495259132c
								
							
						
					
					
						commit
						a06bd9fa2b
					
				| @ -28,4 +28,6 @@ public interface AffinityGroup extends ControlledEntity, InternalIdentity, Ident | |||||||
| 
 | 
 | ||||||
|     String getType(); |     String getType(); | ||||||
| 
 | 
 | ||||||
|  |     ACLType getAclType(); | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -58,4 +58,23 @@ public interface AffinityGroupProcessor extends Adapter { | |||||||
|      */ |      */ | ||||||
|     boolean check(VirtualMachineProfile vm, DeployDestination plannedDestination) |     boolean check(VirtualMachineProfile vm, DeployDestination plannedDestination) | ||||||
|             throws AffinityConflictException; |             throws AffinityConflictException; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * isAdminControlledGroup() should return true if the affinity/anti-affinity | ||||||
|  |      * group can only be operated on[create/delete/modify] by the Admin | ||||||
|  |      * | ||||||
|  |      * @return boolean true/false | ||||||
|  |      */ | ||||||
|  |     boolean isAdminControlledGroup(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * canBeSharedDomainWide() should return true if the affinity/anti-affinity | ||||||
|  |      * group can be created for a domain and shared by all accounts under the | ||||||
|  |      * domain. | ||||||
|  |      *  | ||||||
|  |      * @return boolean true/false | ||||||
|  |      */ | ||||||
|  |     boolean canBeSharedDomainWide(); | ||||||
|  | 
 | ||||||
| } | } | ||||||
| @ -75,4 +75,8 @@ public interface AffinityGroupService { | |||||||
| 
 | 
 | ||||||
|     boolean isAffinityGroupProcessorAvailable(String affinityGroupType); |     boolean isAffinityGroupProcessorAvailable(String affinityGroupType); | ||||||
| 
 | 
 | ||||||
|  |     boolean isAdminControlledGroup(AffinityGroup group); | ||||||
|  | 
 | ||||||
|  |     boolean isAffinityGroupAvailableInDomain(long affinityGroupId, long domainId); | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -47,4 +47,14 @@ public class AffinityProcessorBase extends AdapterBase implements AffinityGroupP | |||||||
|             throws AffinityConflictException { |             throws AffinityConflictException { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean isAdminControlledGroup() { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean canBeSharedDomainWide() { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -377,6 +377,7 @@ | |||||||
|   <bean id="StaticRoleBasedAPIAccessChecker" class="org.apache.cloudstack.acl.StaticRoleBasedAPIAccessChecker"/> |   <bean id="StaticRoleBasedAPIAccessChecker" class="org.apache.cloudstack.acl.StaticRoleBasedAPIAccessChecker"/> | ||||||
|   <bean id="databaseIntegrityChecker" class="com.cloud.upgrade.DatabaseIntegrityChecker" /> |   <bean id="databaseIntegrityChecker" class="com.cloud.upgrade.DatabaseIntegrityChecker" /> | ||||||
|   <bean id="domainChecker" class="com.cloud.acl.DomainChecker" /> |   <bean id="domainChecker" class="com.cloud.acl.DomainChecker" /> | ||||||
|  |   <bean id="affinityGroupAccessChecker" class="com.cloud.acl.AffinityGroupAccessChecker" /> | ||||||
|    |    | ||||||
|   <!-- |   <!-- | ||||||
|     Authenticators |     Authenticators | ||||||
| @ -929,6 +930,8 @@ | |||||||
|   </bean> |   </bean> | ||||||
|   <bean id="AffinityGroupVMMapDaoImpl" class="org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDaoImpl"> |   <bean id="AffinityGroupVMMapDaoImpl" class="org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDaoImpl"> | ||||||
|   </bean> |   </bean> | ||||||
|  |   <bean id="AffinityGroupDomainMapDaoImpl" class="org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDaoImpl"> | ||||||
|  |   </bean> | ||||||
|    |    | ||||||
|   <bean id="PlannerHostReservationDaoImpl" class="com.cloud.deploy.dao.PlannerHostReservationDaoImpl"> |   <bean id="PlannerHostReservationDaoImpl" class="com.cloud.deploy.dao.PlannerHostReservationDaoImpl"> | ||||||
|   </bean> |   </bean> | ||||||
|  | |||||||
| @ -146,6 +146,7 @@ | |||||||
|   <bean id="securityCheckers" class="com.cloud.utils.component.AdapterList"> |   <bean id="securityCheckers" class="com.cloud.utils.component.AdapterList"> | ||||||
|     <property name="Adapters"> |     <property name="Adapters"> | ||||||
|       <list> |       <list> | ||||||
|  | 		  <ref bean="affinityGroupAccessChecker"/> | ||||||
|           <ref bean="domainChecker"/> |           <ref bean="domainChecker"/> | ||||||
|       </list> |       </list> | ||||||
|     </property> |     </property> | ||||||
|  | |||||||
| @ -161,7 +161,7 @@ public class AccountVO implements Account { | |||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public String toString() { |     public String toString() { | ||||||
|         return new StringBuilder("Acct[").append(id).append("-").append(accountName).append("]").toString(); |         return new StringBuilder("Acct[").append(uuid).append("-").append(accountName).append("]").toString(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  | |||||||
| @ -0,0 +1,73 @@ | |||||||
|  | // 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.affinity; | ||||||
|  | 
 | ||||||
|  | import javax.persistence.Column; | ||||||
|  | import javax.persistence.Entity; | ||||||
|  | import javax.persistence.GeneratedValue; | ||||||
|  | import javax.persistence.GenerationType; | ||||||
|  | import javax.persistence.Id; | ||||||
|  | import javax.persistence.Table; | ||||||
|  | 
 | ||||||
|  | import com.cloud.domain.PartOf; | ||||||
|  | import org.apache.cloudstack.api.InternalIdentity; | ||||||
|  | 
 | ||||||
|  | @Entity | ||||||
|  | @Table(name = "affinity_group_domain_map") | ||||||
|  | public class AffinityGroupDomainMapVO implements PartOf, InternalIdentity { | ||||||
|  | 
 | ||||||
|  |     @Id | ||||||
|  |     @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||||
|  |     long id; | ||||||
|  | 
 | ||||||
|  |     @Column(name = "domain_id") | ||||||
|  |     long domainId; | ||||||
|  | 
 | ||||||
|  |     @Column(name = "affinity_group_id") | ||||||
|  |     private long affinityGroupId; | ||||||
|  | 
 | ||||||
|  |     @Column(name = "subdomain_access") | ||||||
|  |     public Boolean subdomainAccess; | ||||||
|  | 
 | ||||||
|  |     protected AffinityGroupDomainMapVO() { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public AffinityGroupDomainMapVO(long affinityGroupId, long domainId, Boolean subdomainAccess) { | ||||||
|  |         this.affinityGroupId = affinityGroupId; | ||||||
|  |         this.domainId = domainId; | ||||||
|  |         this.subdomainAccess = subdomainAccess; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public long getId() { | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public long getDomainId() { | ||||||
|  |         return domainId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public long getAffinityGroupId() { | ||||||
|  |         return affinityGroupId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Boolean isSubdomainAccess() { | ||||||
|  |         return subdomainAccess; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -20,11 +20,14 @@ import java.util.UUID; | |||||||
| 
 | 
 | ||||||
| import javax.persistence.Column; | import javax.persistence.Column; | ||||||
| import javax.persistence.Entity; | import javax.persistence.Entity; | ||||||
|  | import javax.persistence.EnumType; | ||||||
|  | import javax.persistence.Enumerated; | ||||||
| import javax.persistence.GeneratedValue; | import javax.persistence.GeneratedValue; | ||||||
| import javax.persistence.GenerationType; | import javax.persistence.GenerationType; | ||||||
| import javax.persistence.Id; | import javax.persistence.Id; | ||||||
| import javax.persistence.Table; | import javax.persistence.Table; | ||||||
| 
 | 
 | ||||||
|  | import org.apache.cloudstack.acl.ControlledEntity; | ||||||
| 
 | 
 | ||||||
| @Entity | @Entity | ||||||
| @Table(name = ("affinity_group")) | @Table(name = ("affinity_group")) | ||||||
| @ -52,17 +55,22 @@ public class AffinityGroupVO implements AffinityGroup { | |||||||
|     @Column(name = "uuid") |     @Column(name = "uuid") | ||||||
|     private String uuid; |     private String uuid; | ||||||
| 
 | 
 | ||||||
|  |     @Column(name = "acl_type") | ||||||
|  |     @Enumerated(value = EnumType.STRING) | ||||||
|  |     ControlledEntity.ACLType aclType; | ||||||
|  | 
 | ||||||
|     public AffinityGroupVO() { |     public AffinityGroupVO() { | ||||||
|     	this.uuid = UUID.randomUUID().toString(); |     	this.uuid = UUID.randomUUID().toString(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public AffinityGroupVO(String name, String type, String description, long domainId, long accountId) { |     public AffinityGroupVO(String name, String type, String description, long domainId, long accountId, ACLType aclType) { | ||||||
|         this.name = name; |         this.name = name; | ||||||
|         this.description = description; |         this.description = description; | ||||||
|         this.domainId = domainId; |         this.domainId = domainId; | ||||||
|         this.accountId = accountId; |         this.accountId = accountId; | ||||||
|     	this.uuid = UUID.randomUUID().toString(); |     	this.uuid = UUID.randomUUID().toString(); | ||||||
|         this.type = type; |         this.type = type; | ||||||
|  |         this.aclType = aclType; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
| @ -104,6 +112,11 @@ public class AffinityGroupVO implements AffinityGroup { | |||||||
|         return type; |         return type; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public ControlledEntity.ACLType getAclType() { | ||||||
|  |         return aclType; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public String toString() { |     public String toString() { | ||||||
|         StringBuilder buf = new StringBuilder("AffinityGroup["); |         StringBuilder buf = new StringBuilder("AffinityGroup["); | ||||||
|  | |||||||
| @ -0,0 +1,31 @@ | |||||||
|  | // 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.affinity.dao; | ||||||
|  | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; | ||||||
|  | 
 | ||||||
|  | import com.cloud.utils.db.GenericDao; | ||||||
|  | 
 | ||||||
|  | public interface AffinityGroupDomainMapDao extends GenericDao<AffinityGroupDomainMapVO, Long> { | ||||||
|  | 
 | ||||||
|  |     AffinityGroupDomainMapVO findByAffinityGroup(long affinityGroupId); | ||||||
|  | 
 | ||||||
|  |     List<AffinityGroupDomainMapVO> listByDomain(Object... domainId); | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,67 @@ | |||||||
|  | // 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.affinity.dao; | ||||||
|  | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | import javax.annotation.PostConstruct; | ||||||
|  | import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | 
 | ||||||
|  | public class AffinityGroupDomainMapDaoImpl extends GenericDaoBase<AffinityGroupDomainMapVO, Long> implements | ||||||
|  |         AffinityGroupDomainMapDao { | ||||||
|  | 
 | ||||||
|  |     private SearchBuilder<AffinityGroupDomainMapVO> ListByAffinityGroup; | ||||||
|  | 
 | ||||||
|  |     private SearchBuilder<AffinityGroupDomainMapVO> DomainsSearch; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public AffinityGroupDomainMapDaoImpl() { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @PostConstruct | ||||||
|  |     protected void init() { | ||||||
|  |         ListByAffinityGroup = createSearchBuilder(); | ||||||
|  |         ListByAffinityGroup.and("affinityGroupId", ListByAffinityGroup.entity().getAffinityGroupId(), | ||||||
|  |                 SearchCriteria.Op.EQ); | ||||||
|  |         ListByAffinityGroup.done(); | ||||||
|  | 
 | ||||||
|  |         DomainsSearch = createSearchBuilder(); | ||||||
|  |         DomainsSearch.and("domainId", DomainsSearch.entity().getDomainId(), Op.IN); | ||||||
|  |         DomainsSearch.done(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public AffinityGroupDomainMapVO findByAffinityGroup(long affinityGroupId) { | ||||||
|  |         SearchCriteria<AffinityGroupDomainMapVO> sc = ListByAffinityGroup.create(); | ||||||
|  |         sc.setParameters("affinityGroupId", affinityGroupId); | ||||||
|  |         return findOneBy(sc); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public List<AffinityGroupDomainMapVO> listByDomain(Object... domainId) { | ||||||
|  |         SearchCriteria<AffinityGroupDomainMapVO> sc = DomainsSearch.create(); | ||||||
|  |         sc.setParameters("domainId", (Object[]) domainId); | ||||||
|  | 
 | ||||||
|  |         return listBy(sc); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -381,4 +381,13 @@ public class ExplicitDedicationProcessor extends AffinityProcessorBase implement | |||||||
|         return domainIds; |         return domainIds; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean isAdminControlledGroup() { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean canBeSharedDomainWide() { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										80
									
								
								server/src/com/cloud/acl/AffinityGroupAccessChecker.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								server/src/com/cloud/acl/AffinityGroupAccessChecker.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | |||||||
|  | // 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 com.cloud.acl; | ||||||
|  | 
 | ||||||
|  | import javax.ejb.Local; | ||||||
|  | import javax.inject.Inject; | ||||||
|  | 
 | ||||||
|  | import org.apache.cloudstack.acl.ControlledEntity; | ||||||
|  | import org.apache.cloudstack.acl.SecurityChecker; | ||||||
|  | import org.apache.cloudstack.acl.ControlledEntity.ACLType; | ||||||
|  | import org.apache.cloudstack.affinity.AffinityGroup; | ||||||
|  | import org.apache.cloudstack.affinity.AffinityGroupService; | ||||||
|  | import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  | 
 | ||||||
|  | import com.cloud.exception.PermissionDeniedException; | ||||||
|  | import com.cloud.user.Account; | ||||||
|  | import com.cloud.user.AccountManager; | ||||||
|  | import com.cloud.user.DomainManager; | ||||||
|  | 
 | ||||||
|  | @Component | ||||||
|  | @Local(value = SecurityChecker.class) | ||||||
|  | public class AffinityGroupAccessChecker extends DomainChecker { | ||||||
|  | 
 | ||||||
|  |     @Inject | ||||||
|  |     AffinityGroupService _affinityGroupService; | ||||||
|  |     @Inject | ||||||
|  |     AccountManager _accountMgr; | ||||||
|  |     @Inject | ||||||
|  |     AffinityGroupDomainMapDao _affinityGroupDomainMapDao; | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean checkAccess(Account caller, ControlledEntity entity, AccessType accessType) | ||||||
|  |             throws PermissionDeniedException { | ||||||
|  |         if (entity instanceof AffinityGroup) { | ||||||
|  |             AffinityGroup group = (AffinityGroup) entity; | ||||||
|  | 
 | ||||||
|  |             if (_affinityGroupService.isAdminControlledGroup(group)) { | ||||||
|  |                 if (accessType != null && accessType == AccessType.ModifyEntry | ||||||
|  |                         && !_accountMgr.isRootAdmin(caller.getType())) { | ||||||
|  |                     throw new PermissionDeniedException(caller + " does not have permission to operate with resource " | ||||||
|  |                             + entity); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (group.getAclType() == ACLType.Domain) { | ||||||
|  |                 if (!_affinityGroupService.isAffinityGroupAvailableInDomain(group.getId(), caller.getDomainId())) { | ||||||
|  |                     throw new PermissionDeniedException("Affinity group is not available in domain id=" | ||||||
|  |                             + caller.getDomainId()); | ||||||
|  |                 } else { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 //acl_type account  | ||||||
|  |                 if (caller.getId() != group.getAccountId()) { | ||||||
|  |                       throw new PermissionDeniedException(caller | ||||||
|  |                       + " does not have permission to operate with resource " + entity); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -21,6 +21,7 @@ import javax.inject.Inject; | |||||||
| 
 | 
 | ||||||
| import org.apache.cloudstack.acl.ControlledEntity; | import org.apache.cloudstack.acl.ControlledEntity; | ||||||
| import org.apache.cloudstack.acl.SecurityChecker; | import org.apache.cloudstack.acl.SecurityChecker; | ||||||
|  | import org.apache.cloudstack.affinity.AffinityGroup; | ||||||
| import org.apache.cloudstack.api.BaseCmd; | import org.apache.cloudstack.api.BaseCmd; | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
| @ -121,6 +122,8 @@ public class DomainChecker extends AdapterBase implements SecurityChecker { | |||||||
|             return true; |             return true; | ||||||
|         } else if (entity instanceof Network && accessType != null && accessType == AccessType.UseNetwork) { |         } else if (entity instanceof Network && accessType != null && accessType == AccessType.UseNetwork) { | ||||||
|             _networkMgr.checkNetworkPermissions(caller, (Network) entity); |             _networkMgr.checkNetworkPermissions(caller, (Network) entity); | ||||||
|  |         } else if (entity instanceof AffinityGroup) { | ||||||
|  |             return false; | ||||||
|         } else { |         } else { | ||||||
|             if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) { |             if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) { | ||||||
|                 Account account = _accountDao.findById(entity.getAccountId()); |                 Account account = _accountDao.findById(entity.getAccountId()); | ||||||
|  | |||||||
| @ -29,9 +29,11 @@ import javax.inject.Inject; | |||||||
| 
 | 
 | ||||||
| import org.apache.log4j.Logger; | import org.apache.log4j.Logger; | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| 
 | import org.apache.cloudstack.acl.ControlledEntity.ACLType; | ||||||
|  | import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; | ||||||
| import org.apache.cloudstack.affinity.AffinityGroupResponse; | import org.apache.cloudstack.affinity.AffinityGroupResponse; | ||||||
| import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; | import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; | ||||||
|  | import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; | ||||||
| import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; | import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; | ||||||
| import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; | import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; | ||||||
| import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; | import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; | ||||||
| @ -137,6 +139,8 @@ import com.cloud.exception.InvalidParameterValueException; | |||||||
| import com.cloud.exception.PermissionDeniedException; | import com.cloud.exception.PermissionDeniedException; | ||||||
| import com.cloud.ha.HighAvailabilityManager; | import com.cloud.ha.HighAvailabilityManager; | ||||||
| import com.cloud.hypervisor.Hypervisor.HypervisorType; | import com.cloud.hypervisor.Hypervisor.HypervisorType; | ||||||
|  | import com.cloud.network.dao.NetworkDomainVO; | ||||||
|  | import com.cloud.network.dao.NetworkVO; | ||||||
| import com.cloud.network.security.SecurityGroupVMMapVO; | import com.cloud.network.security.SecurityGroupVMMapVO; | ||||||
| import com.cloud.network.security.dao.SecurityGroupVMMapDao; | import com.cloud.network.security.dao.SecurityGroupVMMapDao; | ||||||
| import com.cloud.org.Grouping; | import com.cloud.org.Grouping; | ||||||
| @ -168,6 +172,8 @@ import com.cloud.template.VirtualMachineTemplate.TemplateFilter; | |||||||
| import com.cloud.user.Account; | import com.cloud.user.Account; | ||||||
| import com.cloud.user.AccountManager; | import com.cloud.user.AccountManager; | ||||||
| import com.cloud.user.AccountVO; | import com.cloud.user.AccountVO; | ||||||
|  | import com.cloud.user.DomainManager; | ||||||
|  | import com.cloud.user.UserContext; | ||||||
| import com.cloud.user.dao.AccountDao; | import com.cloud.user.dao.AccountDao; | ||||||
| import com.cloud.utils.DateUtil; | import com.cloud.utils.DateUtil; | ||||||
| import com.cloud.utils.Pair; | import com.cloud.utils.Pair; | ||||||
| @ -316,6 +322,12 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { | |||||||
|     @Inject |     @Inject | ||||||
|     private DedicatedResourceDao _dedicatedDao; |     private DedicatedResourceDao _dedicatedDao; | ||||||
| 
 | 
 | ||||||
|  |     @Inject | ||||||
|  |     DomainManager _domainMgr; | ||||||
|  | 
 | ||||||
|  |     @Inject | ||||||
|  |     AffinityGroupDomainMapDao _affinityGroupDomainMapDao; | ||||||
|  | 
 | ||||||
|     /* |     /* | ||||||
|      * (non-Javadoc) |      * (non-Javadoc) | ||||||
|      * |      * | ||||||
| @ -3019,6 +3031,58 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { | |||||||
|         ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); |         ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); | ||||||
| 
 | 
 | ||||||
|         Filter searchFilter = new Filter(AffinityGroupJoinVO.class, "id", true, startIndex, pageSize); |         Filter searchFilter = new Filter(AffinityGroupJoinVO.class, "id", true, startIndex, pageSize); | ||||||
|  |         SearchCriteria<AffinityGroupJoinVO> sc = buildAffinityGroupSearchCriteria(domainId, isRecursive, | ||||||
|  |                 permittedAccounts, listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType); | ||||||
|  |          | ||||||
|  |         Pair<List<AffinityGroupJoinVO>, Integer> uniqueGroupsPair = _affinityGroupJoinDao.searchAndCount(sc, | ||||||
|  |                 searchFilter); | ||||||
|  |         // search group details by ids | ||||||
|  |         List<AffinityGroupJoinVO> vrs = new ArrayList<AffinityGroupJoinVO>(); | ||||||
|  |         Integer count = uniqueGroupsPair.second(); | ||||||
|  |         if (count.intValue() != 0) { | ||||||
|  |             List<AffinityGroupJoinVO> uniqueGroups = uniqueGroupsPair.first(); | ||||||
|  |             Long[] vrIds = new Long[uniqueGroups.size()]; | ||||||
|  |             int i = 0; | ||||||
|  |             for (AffinityGroupJoinVO v : uniqueGroups) { | ||||||
|  |                 vrIds[i++] = v.getId(); | ||||||
|  |             } | ||||||
|  |             vrs = _affinityGroupJoinDao.searchByIds(vrIds); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!permittedAccounts.isEmpty()) { | ||||||
|  |             // add domain level affinity groups | ||||||
|  |             if (domainId != null) { | ||||||
|  |                 SearchCriteria<AffinityGroupJoinVO> scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, | ||||||
|  |                         new ArrayList<Long>(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, | ||||||
|  |                         affinityGroupType); | ||||||
|  |                 vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId)); | ||||||
|  |             } else { | ||||||
|  | 
 | ||||||
|  |                 for (Long permAcctId : permittedAccounts) { | ||||||
|  |                     Account permittedAcct = _accountDao.findById(permAcctId); | ||||||
|  |                     SearchCriteria<AffinityGroupJoinVO> scDomain = buildAffinityGroupSearchCriteria( | ||||||
|  |                             null, isRecursive, new ArrayList<Long>(), | ||||||
|  |                             listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType); | ||||||
|  | 
 | ||||||
|  |                     vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, permittedAcct.getDomainId())); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else if (((permittedAccounts.isEmpty()) && (domainId != null) && isRecursive)) { | ||||||
|  |             // list all domain level affinity groups for the domain admin case | ||||||
|  |             SearchCriteria<AffinityGroupJoinVO> scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, | ||||||
|  |                     new ArrayList<Long>(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, | ||||||
|  |                     affinityGroupType); | ||||||
|  |             vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return new Pair<List<AffinityGroupJoinVO>, Integer>(vrs, vrs.size()); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private SearchCriteria<AffinityGroupJoinVO> buildAffinityGroupSearchCriteria(Long domainId, boolean isRecursive, | ||||||
|  |             List<Long> permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria, | ||||||
|  |             Long affinityGroupId, String affinityGroupName, String affinityGroupType) { | ||||||
|  | 
 | ||||||
|         SearchBuilder<AffinityGroupJoinVO> groupSearch = _affinityGroupJoinDao.createSearchBuilder(); |         SearchBuilder<AffinityGroupJoinVO> groupSearch = _affinityGroupJoinDao.createSearchBuilder(); | ||||||
|         _accountMgr.buildACLViewSearchBuilder(groupSearch, domainId, isRecursive, permittedAccounts, |         _accountMgr.buildACLViewSearchBuilder(groupSearch, domainId, isRecursive, permittedAccounts, | ||||||
|                 listProjectResourcesCriteria); |                 listProjectResourcesCriteria); | ||||||
| @ -3042,22 +3106,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { | |||||||
|             sc.addAnd("type", SearchCriteria.Op.EQ, affinityGroupType); |             sc.addAnd("type", SearchCriteria.Op.EQ, affinityGroupType); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Pair<List<AffinityGroupJoinVO>, Integer> uniqueGroupsPair = _affinityGroupJoinDao.searchAndCount(sc, |         return sc; | ||||||
|                 searchFilter); |  | ||||||
|         // search group details by ids |  | ||||||
|         Integer count = uniqueGroupsPair.second(); |  | ||||||
|         if (count.intValue() == 0) { |  | ||||||
|             // empty result |  | ||||||
|             return uniqueGroupsPair; |  | ||||||
|         } |  | ||||||
|         List<AffinityGroupJoinVO> uniqueGroups = uniqueGroupsPair.first(); |  | ||||||
|         Long[] vrIds = new Long[uniqueGroups.size()]; |  | ||||||
|         int i = 0; |  | ||||||
|         for (AffinityGroupJoinVO v : uniqueGroups) { |  | ||||||
|             vrIds[i++] = v.getId(); |  | ||||||
|         } |  | ||||||
|         List<AffinityGroupJoinVO> vrs = _affinityGroupJoinDao.searchByIds(vrIds); |  | ||||||
|         return new Pair<List<AffinityGroupJoinVO>, Integer>(vrs, count); |  | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -3079,6 +3128,47 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { | |||||||
|         return new Pair<List<AffinityGroupJoinVO>, Integer>(ags, count); |         return new Pair<List<AffinityGroupJoinVO>, Integer>(ags, count); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private List<AffinityGroupJoinVO> listDomainLevelAffinityGroups( | ||||||
|  |             SearchCriteria<AffinityGroupJoinVO> sc, Filter searchFilter, long domainId) { | ||||||
|  |         List<Long> affinityGroupIds = new ArrayList<Long>(); | ||||||
|  |         Set<Long> allowedDomains = _domainMgr.getDomainParentIds(domainId); | ||||||
|  |         List<AffinityGroupDomainMapVO> maps = _affinityGroupDomainMapDao.listByDomain(allowedDomains.toArray()); | ||||||
|  | 
 | ||||||
|  |         for (AffinityGroupDomainMapVO map : maps) { | ||||||
|  |             boolean subdomainAccess = map.isSubdomainAccess(); | ||||||
|  |             if (map.getDomainId() == domainId || subdomainAccess) { | ||||||
|  |                 affinityGroupIds.add(map.getAffinityGroupId()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!affinityGroupIds.isEmpty()) { | ||||||
|  |             SearchCriteria<AffinityGroupJoinVO> domainSC = _affinityGroupJoinDao.createSearchCriteria(); | ||||||
|  |             domainSC.addAnd("id", SearchCriteria.Op.IN, affinityGroupIds.toArray()); | ||||||
|  |             domainSC.addAnd("aclType", SearchCriteria.Op.EQ, ACLType.Domain.toString()); | ||||||
|  | 
 | ||||||
|  |             sc.addAnd("id", SearchCriteria.Op.SC, domainSC); | ||||||
|  | 
 | ||||||
|  |             Pair<List<AffinityGroupJoinVO>, Integer> uniqueGroupsPair = _affinityGroupJoinDao.searchAndCount(sc, | ||||||
|  |                     searchFilter); | ||||||
|  |             // search group by ids | ||||||
|  |             Integer count = uniqueGroupsPair.second(); | ||||||
|  |             if (count.intValue() == 0) { | ||||||
|  |                 // empty result | ||||||
|  |                 return new ArrayList<AffinityGroupJoinVO>(); | ||||||
|  |             } | ||||||
|  |             List<AffinityGroupJoinVO> uniqueGroups = uniqueGroupsPair.first(); | ||||||
|  |             Long[] vrIds = new Long[uniqueGroups.size()]; | ||||||
|  |             int i = 0; | ||||||
|  |             for (AffinityGroupJoinVO v : uniqueGroups) { | ||||||
|  |                 vrIds[i++] = v.getId(); | ||||||
|  |             } | ||||||
|  |             List<AffinityGroupJoinVO> vrs = _affinityGroupJoinDao.searchByIds(vrIds); | ||||||
|  |             return vrs; | ||||||
|  |         } else { | ||||||
|  |             return new ArrayList<AffinityGroupJoinVO>(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public List<ResourceDetailResponse> listResource(ListResourceDetailsCmd cmd) { |     public List<ResourceDetailResponse> listResource(ListResourceDetailsCmd cmd) { | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -23,6 +23,8 @@ import javax.persistence.Enumerated; | |||||||
| import javax.persistence.Id; | import javax.persistence.Id; | ||||||
| import javax.persistence.Table; | import javax.persistence.Table; | ||||||
| 
 | 
 | ||||||
|  | import org.apache.cloudstack.acl.ControlledEntity; | ||||||
|  | 
 | ||||||
| import com.cloud.vm.VirtualMachine; | import com.cloud.vm.VirtualMachine; | ||||||
| 
 | 
 | ||||||
| @Entity | @Entity | ||||||
| @ -85,6 +87,10 @@ public class AffinityGroupJoinVO extends BaseViewVO implements ControlledViewEnt | |||||||
|     @Enumerated(value = EnumType.STRING) |     @Enumerated(value = EnumType.STRING) | ||||||
|     protected VirtualMachine.State vmState = null; |     protected VirtualMachine.State vmState = null; | ||||||
| 
 | 
 | ||||||
|  |     @Column(name = "acl_type") | ||||||
|  |     @Enumerated(value = EnumType.STRING) | ||||||
|  |     ControlledEntity.ACLType aclType; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     public AffinityGroupJoinVO() { |     public AffinityGroupJoinVO() { | ||||||
|     } |     } | ||||||
| @ -256,4 +262,14 @@ public class AffinityGroupJoinVO extends BaseViewVO implements ControlledViewEnt | |||||||
|         // TODO Auto-generated method stub |         // TODO Auto-generated method stub | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     public ControlledEntity.ACLType getAclType() { | ||||||
|  |         return aclType; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setAclType(ControlledEntity.ACLType aclType) { | ||||||
|  |         this.aclType = aclType; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -44,6 +44,7 @@ import org.apache.cloudstack.acl.ControlledEntity; | |||||||
| import org.apache.cloudstack.acl.RoleType; | import org.apache.cloudstack.acl.RoleType; | ||||||
| import org.apache.cloudstack.acl.SecurityChecker; | import org.apache.cloudstack.acl.SecurityChecker; | ||||||
| import org.apache.cloudstack.acl.SecurityChecker.AccessType; | import org.apache.cloudstack.acl.SecurityChecker.AccessType; | ||||||
|  | import org.apache.cloudstack.affinity.AffinityGroup; | ||||||
| import org.apache.cloudstack.affinity.dao.AffinityGroupDao; | import org.apache.cloudstack.affinity.dao.AffinityGroupDao; | ||||||
| import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd; | import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd; | ||||||
| import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; | import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; | ||||||
| @ -395,7 +396,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M | |||||||
|                 Account account = ApiDBUtils.findAccountById(entity.getAccountId()); |                 Account account = ApiDBUtils.findAccountById(entity.getAccountId()); | ||||||
|                 domainId = account != null ? account.getDomainId() : -1; |                 domainId = account != null ? account.getDomainId() : -1; | ||||||
|             } |             } | ||||||
|             if (entity.getAccountId() != -1 && domainId != -1 && !(entity instanceof VirtualMachineTemplate) && !(accessType != null && accessType == AccessType.UseNetwork)) { |             if (entity.getAccountId() != -1 && domainId != -1 && !(entity instanceof VirtualMachineTemplate) | ||||||
|  |                     && !(accessType != null && accessType == AccessType.UseNetwork) | ||||||
|  |                     && !(entity instanceof AffinityGroup)) { | ||||||
|                 List<ControlledEntity> toBeChecked = domains.get(entity.getDomainId()); |                 List<ControlledEntity> toBeChecked = domains.get(entity.getDomainId()); | ||||||
|                 // for templates, we don't have to do cross domains check |                 // for templates, we don't have to do cross domains check | ||||||
|                 if (toBeChecked == null) { |                 if (toBeChecked == null) { | ||||||
|  | |||||||
| @ -2572,8 +2572,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir | |||||||
|                             + " ,type: " + ag.getType() + " , Please try again after removing the affinity group"); |                             + " ,type: " + ag.getType() + " , Please try again after removing the affinity group"); | ||||||
|                 } else { |                 } else { | ||||||
|                     // verify permissions |                     // verify permissions | ||||||
|  |                     if (ag.getAclType() == ACLType.Domain) { | ||||||
|  |                         _accountMgr.checkAccess(caller, null, false, owner, ag); | ||||||
|  |                         // Root admin has access to both VM and AG by default, | ||||||
|  |                         // but | ||||||
|  |                         // make sure the owner of these entities is same | ||||||
|  |                         if (caller.getId() == Account.ACCOUNT_ID_SYSTEM || _accountMgr.isRootAdmin(caller.getType())) { | ||||||
|  |                             if (!_affinityGroupService.isAffinityGroupAvailableInDomain(ag.getId(), owner.getDomainId())) { | ||||||
|  |                                 throw new PermissionDeniedException("Affinity Group " + ag | ||||||
|  |                                         + " does not belong to the VM's domain"); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|                         _accountMgr.checkAccess(caller, null, true, owner, ag); |                         _accountMgr.checkAccess(caller, null, true, owner, ag); | ||||||
|                     // Root admin has access to both VM and AG by default, but |                         // Root admin has access to both VM and AG by default, | ||||||
|  |                         // but | ||||||
|                         // make sure the owner of these entities is same |                         // make sure the owner of these entities is same | ||||||
|                         if (caller.getId() == Account.ACCOUNT_ID_SYSTEM || _accountMgr.isRootAdmin(caller.getType())) { |                         if (caller.getId() == Account.ACCOUNT_ID_SYSTEM || _accountMgr.isRootAdmin(caller.getType())) { | ||||||
|                             if (ag.getAccountId() != owner.getAccountId()) { |                             if (ag.getAccountId() != owner.getAccountId()) { | ||||||
| @ -2584,6 +2597,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (template.getHypervisorType() != null && template.getHypervisorType() != HypervisorType.BareMetal) { |         if (template.getHypervisorType() != null && template.getHypervisorType() != HypervisorType.BareMetal) { | ||||||
|             // check if we have available pools for vm deployment |             // check if we have available pools for vm deployment | ||||||
|  | |||||||
| @ -20,13 +20,18 @@ import java.util.ArrayList; | |||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  | import java.util.Set; | ||||||
| import java.util.Map.Entry; | import java.util.Map.Entry; | ||||||
| 
 | 
 | ||||||
| import javax.ejb.Local; | import javax.ejb.Local; | ||||||
| import javax.inject.Inject; | import javax.inject.Inject; | ||||||
| import javax.naming.ConfigurationException; | import javax.naming.ConfigurationException; | ||||||
| 
 | 
 | ||||||
|  | import org.apache.cloudstack.acl.ControlledEntity; | ||||||
|  | import org.apache.cloudstack.acl.ControlledEntity.ACLType; | ||||||
|  | import org.apache.cloudstack.acl.SecurityChecker.AccessType; | ||||||
| import org.apache.cloudstack.affinity.dao.AffinityGroupDao; | import org.apache.cloudstack.affinity.dao.AffinityGroupDao; | ||||||
|  | import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; | ||||||
| import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; | import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; | ||||||
| import org.apache.cloudstack.context.CallContext; | import org.apache.cloudstack.context.CallContext; | ||||||
| 
 | 
 | ||||||
| @ -36,14 +41,20 @@ import org.springframework.context.annotation.Primary; | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| import com.cloud.deploy.DeploymentPlanner; | import com.cloud.deploy.DeploymentPlanner; | ||||||
|  | import com.cloud.domain.DomainVO; | ||||||
|  | import com.cloud.domain.dao.DomainDao; | ||||||
| import com.cloud.event.ActionEvent; | import com.cloud.event.ActionEvent; | ||||||
| import com.cloud.event.EventTypes; | import com.cloud.event.EventTypes; | ||||||
| import com.cloud.exception.InvalidParameterValueException; | import com.cloud.exception.InvalidParameterValueException; | ||||||
| import com.cloud.exception.PermissionDeniedException; | import com.cloud.exception.PermissionDeniedException; | ||||||
| import com.cloud.exception.ResourceInUseException; | import com.cloud.exception.ResourceInUseException; | ||||||
|  | import com.cloud.network.Network; | ||||||
|  | import com.cloud.network.dao.NetworkDomainVO; | ||||||
| import com.cloud.network.security.SecurityGroup; | import com.cloud.network.security.SecurityGroup; | ||||||
| import com.cloud.user.Account; | import com.cloud.user.Account; | ||||||
| import com.cloud.user.AccountManager; | import com.cloud.user.AccountManager; | ||||||
|  | import com.cloud.user.DomainManager; | ||||||
|  | import com.cloud.user.UserContext; | ||||||
| import com.cloud.uservm.UserVm; | import com.cloud.uservm.UserVm; | ||||||
| import com.cloud.utils.Pair; | import com.cloud.utils.Pair; | ||||||
| import com.cloud.utils.component.ComponentContext; | import com.cloud.utils.component.ComponentContext; | ||||||
| @ -79,9 +90,18 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro | |||||||
|     @Inject |     @Inject | ||||||
|     AffinityGroupVMMapDao _affinityGroupVMMapDao; |     AffinityGroupVMMapDao _affinityGroupVMMapDao; | ||||||
| 
 | 
 | ||||||
|  |     @Inject | ||||||
|  |     AffinityGroupDomainMapDao _affinityGroupDomainMapDao; | ||||||
|  | 
 | ||||||
|     @Inject |     @Inject | ||||||
|     private UserVmDao _userVmDao; |     private UserVmDao _userVmDao; | ||||||
| 
 | 
 | ||||||
|  |     @Inject | ||||||
|  |     DomainDao _domainDao; | ||||||
|  | 
 | ||||||
|  |     @Inject | ||||||
|  |     DomainManager _domainMgr; | ||||||
|  | 
 | ||||||
|     protected List<AffinityGroupProcessor> _affinityProcessors; |     protected List<AffinityGroupProcessor> _affinityProcessors; | ||||||
| 
 | 
 | ||||||
|     public List<AffinityGroupProcessor> getAffinityGroupProcessors() { |     public List<AffinityGroupProcessor> getAffinityGroupProcessors() { | ||||||
| @ -92,20 +112,13 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro | |||||||
|         this._affinityProcessors = affinityProcessors; |         this._affinityProcessors = affinityProcessors; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @DB | ||||||
|     @Override |     @Override | ||||||
|     @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_CREATE, eventDescription = "Creating Affinity Group", create = true) |     @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_CREATE, eventDescription = "Creating Affinity Group", create = true) | ||||||
|     public AffinityGroup createAffinityGroup(String account, Long domainId, String affinityGroupName, |     public AffinityGroup createAffinityGroup(String account, Long domainId, String affinityGroupName, | ||||||
|             String affinityGroupType, String description) { |             String affinityGroupType, String description) { | ||||||
| 
 | 
 | ||||||
|         Account caller = CallContext.current().getCallingAccount(); |         Account caller = UserContext.current().getCaller(); | ||||||
|         Account owner = _accountMgr.finalizeOwner(caller, account, domainId, null); |  | ||||||
| 
 |  | ||||||
|         if (_affinityGroupDao.isNameInUse(owner.getAccountId(), owner.getDomainId(), affinityGroupName)) { |  | ||||||
|             throw new InvalidParameterValueException("Unable to create affinity group, a group with name " |  | ||||||
|                     + affinityGroupName |  | ||||||
|                     + " already exisits."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|         //validate the affinityGroupType |         //validate the affinityGroupType | ||||||
|         Map<String, AffinityGroupProcessor> typeProcessorMap = getAffinityTypeToProcessorMap(); |         Map<String, AffinityGroupProcessor> typeProcessorMap = getAffinityTypeToProcessorMap(); | ||||||
| @ -119,14 +132,65 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro | |||||||
|                     "Unable to create affinity group, no Affinity Group Types configured"); |                     "Unable to create affinity group, no Affinity Group Types configured"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (domainId == null) { |         AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); | ||||||
|             domainId = owner.getDomainId(); | 
 | ||||||
|  |         if (processor.isAdminControlledGroup() && !_accountMgr.isRootAdmin(caller.getType())) { | ||||||
|  |             throw new PermissionDeniedException("Cannot create the affinity group"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         AffinityGroupVO group = new AffinityGroupVO(affinityGroupName, affinityGroupType, description, domainId, |         ControlledEntity.ACLType aclType = null; | ||||||
|                 owner.getId()); |         Account owner = null; | ||||||
|  | 
 | ||||||
|  |         if (account != null && domainId != null) { | ||||||
|  | 
 | ||||||
|  |             owner = _accountMgr.finalizeOwner(caller, account, domainId, null); | ||||||
|  |             aclType = ControlledEntity.ACLType.Account; | ||||||
|  | 
 | ||||||
|  |         } else if (domainId != null && account == null) { | ||||||
|  | 
 | ||||||
|  |             if (!_accountMgr.isRootAdmin(caller.getType())) { | ||||||
|  |                 // non root admin need to pass both account and domain | ||||||
|  |                 throw new InvalidParameterValueException( | ||||||
|  |                         "Unable to create affinity group, account name must be passed with the domainId"); | ||||||
|  |             } else if (!processor.canBeSharedDomainWide()) { | ||||||
|  |                 // cannot be domain level | ||||||
|  |                 throw new InvalidParameterValueException("Unable to create affinity group, account name is needed"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             DomainVO domain = _domainDao.findById(domainId); | ||||||
|  |             if (domain == null) { | ||||||
|  |                 throw new InvalidParameterValueException("Unable to find domain by specified id"); | ||||||
|  |             } | ||||||
|  |             _accountMgr.checkAccess(caller, domain); | ||||||
|  | 
 | ||||||
|  |             // domain level group, owner is SYSTEM. | ||||||
|  |             owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM); | ||||||
|  |             aclType = ControlledEntity.ACLType.Domain; | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             owner = caller; | ||||||
|  |             aclType = ControlledEntity.ACLType.Account; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (_affinityGroupDao.isNameInUse(owner.getAccountId(), owner.getDomainId(), affinityGroupName)) { | ||||||
|  |             throw new InvalidParameterValueException("Unable to create affinity group, a group with name " | ||||||
|  |                     + affinityGroupName + " already exisits."); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Transaction txn = Transaction.currentTxn(); | ||||||
|  |         txn.start(); | ||||||
|  |          | ||||||
|  |         AffinityGroupVO group = new AffinityGroupVO(affinityGroupName, affinityGroupType, description, owner.getDomainId(), | ||||||
|  |                 owner.getId(), aclType); | ||||||
|         _affinityGroupDao.persist(group); |         _affinityGroupDao.persist(group); | ||||||
| 
 | 
 | ||||||
|  |         if (domainId != null && aclType == ACLType.Domain) { | ||||||
|  |             AffinityGroupDomainMapVO domainMap = new AffinityGroupDomainMapVO(group.getId(), domainId, false); | ||||||
|  |             _affinityGroupDomainMapDao.persist(domainMap); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         txn.commit(); | ||||||
|  | 
 | ||||||
|         if (s_logger.isDebugEnabled()) { |         if (s_logger.isDebugEnabled()) { | ||||||
|             s_logger.debug("Created affinity group =" + affinityGroupName); |             s_logger.debug("Created affinity group =" + affinityGroupName); | ||||||
|         } |         } | ||||||
| @ -134,6 +198,7 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro | |||||||
|         return group; |         return group; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     @DB |     @DB | ||||||
|     @Override |     @Override | ||||||
|     @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_DELETE, eventDescription = "Deleting affinity group") |     @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_DELETE, eventDescription = "Deleting affinity group") | ||||||
| @ -163,7 +228,7 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro | |||||||
|             affinityGroupId = group.getId(); |             affinityGroupId = group.getId(); | ||||||
|         } |         } | ||||||
|         // check permissions |         // check permissions | ||||||
|         _accountMgr.checkAccess(caller, null, true, group); |         _accountMgr.checkAccess(caller, AccessType.ModifyEntry, true, group); | ||||||
| 
 | 
 | ||||||
|         final Transaction txn = Transaction.currentTxn(); |         final Transaction txn = Transaction.currentTxn(); | ||||||
|         txn.start(); |         txn.start(); | ||||||
| @ -251,12 +316,18 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro | |||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public List<String> listAffinityGroupTypes() { |     public List<String> listAffinityGroupTypes() { | ||||||
|  |         Account caller = UserContext.current().getCaller(); | ||||||
|  | 
 | ||||||
|         List<String> types = new ArrayList<String>(); |         List<String> types = new ArrayList<String>(); | ||||||
|         Map<String, AffinityGroupProcessor> componentMap = ComponentContext.getComponentsOfType(AffinityGroupProcessor.class); |         Map<String, AffinityGroupProcessor> componentMap = ComponentContext.getComponentsOfType(AffinityGroupProcessor.class); | ||||||
| 
 | 
 | ||||||
|         if (componentMap.size() > 0) { |         if (componentMap.size() > 0) { | ||||||
|             for (Entry<String, AffinityGroupProcessor> entry : componentMap.entrySet()) { |             for (Entry<String, AffinityGroupProcessor> entry : componentMap.entrySet()) { | ||||||
|                 types.add(entry.getValue().getType()); |                 AffinityGroupProcessor processor = entry.getValue(); | ||||||
|  |                 if (processor.isAdminControlledGroup() && !_accountMgr.isRootAdmin(caller.getType())) { | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 types.add(processor.getType()); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
| @ -276,6 +347,23 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro | |||||||
|         return typeProcessorMap; |         return typeProcessorMap; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean isAdminControlledGroup(AffinityGroup group) { | ||||||
|  | 
 | ||||||
|  |         if (group != null) { | ||||||
|  |             String affinityGroupType = group.getType(); | ||||||
|  |             Map<String, AffinityGroupProcessor> typeProcessorMap = getAffinityTypeToProcessorMap(); | ||||||
|  |             if (typeProcessorMap != null && !typeProcessorMap.isEmpty()) { | ||||||
|  |                 AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); | ||||||
|  |                 if (processor != null) { | ||||||
|  |                     return processor.isAdminControlledGroup(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException { |     public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException { | ||||||
|         _name = name; |         _name = name; | ||||||
| @ -381,4 +469,29 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean isAffinityGroupAvailableInDomain(long affinityGroupId, long domainId) { | ||||||
|  |         Long groupDomainId = null; | ||||||
|  | 
 | ||||||
|  |         AffinityGroupDomainMapVO domainMap = _affinityGroupDomainMapDao.findByAffinityGroup(affinityGroupId); | ||||||
|  |         if (domainMap == null) { | ||||||
|  |             return false; | ||||||
|  |         } else { | ||||||
|  |             groupDomainId = domainMap.getDomainId(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (domainId == groupDomainId.longValue()) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (domainMap.subdomainAccess) { | ||||||
|  |             Set<Long> parentDomains = _domainMgr.getDomainParentIds(domainId); | ||||||
|  |             if (parentDomains.contains(groupDomainId)) { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -33,6 +33,11 @@ import javax.inject.Inject; | |||||||
| import javax.naming.ConfigurationException; | import javax.naming.ConfigurationException; | ||||||
| 
 | 
 | ||||||
| import org.junit.After; | import org.junit.After; | ||||||
|  | import org.apache.cloudstack.acl.ControlledEntity; | ||||||
|  | import org.apache.cloudstack.affinity.dao.AffinityGroupDao; | ||||||
|  | import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; | ||||||
|  | import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; | ||||||
|  | import org.apache.cloudstack.test.utils.SpringUtils; | ||||||
| import org.junit.Before; | import org.junit.Before; | ||||||
| import org.junit.BeforeClass; | import org.junit.BeforeClass; | ||||||
| import org.junit.Test; | import org.junit.Test; | ||||||
| @ -57,6 +62,9 @@ import org.apache.cloudstack.test.utils.SpringUtils; | |||||||
| 
 | 
 | ||||||
| import com.cloud.dc.dao.DedicatedResourceDao; | import com.cloud.dc.dao.DedicatedResourceDao; | ||||||
| import com.cloud.event.ActionEventUtils; | import com.cloud.event.ActionEventUtils; | ||||||
|  | import com.cloud.dc.dao.DedicatedResourceDao; | ||||||
|  | import com.cloud.domain.dao.DomainDao; | ||||||
|  | import com.cloud.event.ActionEventUtils; | ||||||
| import com.cloud.event.EventVO; | import com.cloud.event.EventVO; | ||||||
| import com.cloud.event.dao.EventDao; | import com.cloud.event.dao.EventDao; | ||||||
| import com.cloud.exception.InvalidParameterValueException; | import com.cloud.exception.InvalidParameterValueException; | ||||||
| @ -67,6 +75,8 @@ import com.cloud.user.AccountManager; | |||||||
| import com.cloud.user.AccountService; | import com.cloud.user.AccountService; | ||||||
| import com.cloud.user.AccountVO; | import com.cloud.user.AccountVO; | ||||||
| import com.cloud.user.UserVO; | import com.cloud.user.UserVO; | ||||||
|  | import com.cloud.user.DomainManager; | ||||||
|  | import com.cloud.user.UserContext; | ||||||
| import com.cloud.user.dao.AccountDao; | import com.cloud.user.dao.AccountDao; | ||||||
| import com.cloud.user.dao.UserDao; | import com.cloud.user.dao.UserDao; | ||||||
| import com.cloud.utils.component.ComponentContext; | import com.cloud.utils.component.ComponentContext; | ||||||
| @ -135,7 +145,8 @@ public class AffinityApiUnitTest { | |||||||
|         when(_processor.getType()).thenReturn("mock"); |         when(_processor.getType()).thenReturn("mock"); | ||||||
|         when(_accountDao.findByIdIncludingRemoved(0L)).thenReturn(acct); |         when(_accountDao.findByIdIncludingRemoved(0L)).thenReturn(acct); | ||||||
| 
 | 
 | ||||||
|         AffinityGroupVO group = new AffinityGroupVO("group1", "mock", "mock group", domainId, 200L); |         AffinityGroupVO group = new AffinityGroupVO("group1", "mock", "mock group", domainId, 200L, | ||||||
|  |                 ControlledEntity.ACLType.Account); | ||||||
|         Mockito.when(_affinityGroupDao.persist(Mockito.any(AffinityGroupVO.class))).thenReturn(group); |         Mockito.when(_affinityGroupDao.persist(Mockito.any(AffinityGroupVO.class))).thenReturn(group); | ||||||
|         Mockito.when(_affinityGroupDao.findById(Mockito.anyLong())).thenReturn(group); |         Mockito.when(_affinityGroupDao.findById(Mockito.anyLong())).thenReturn(group); | ||||||
|         Mockito.when(_affinityGroupDao.findByAccountAndName(Mockito.anyLong(), Mockito.anyString())).thenReturn(group); |         Mockito.when(_affinityGroupDao.findByAccountAndName(Mockito.anyLong(), Mockito.anyString())).thenReturn(group); | ||||||
| @ -242,6 +253,11 @@ public class AffinityApiUnitTest { | |||||||
|             return Mockito.mock(AccountManager.class); |             return Mockito.mock(AccountManager.class); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         @Bean | ||||||
|  |         public DomainManager domainManager() { | ||||||
|  |             return Mockito.mock(DomainManager.class); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         @Bean |         @Bean | ||||||
|         public EventDao eventDao() { |         public EventDao eventDao() { | ||||||
|             return Mockito.mock(EventDao.class); |             return Mockito.mock(EventDao.class); | ||||||
| @ -257,6 +273,16 @@ public class AffinityApiUnitTest { | |||||||
|             return Mockito.mock(UserDao.class); |             return Mockito.mock(UserDao.class); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         @Bean | ||||||
|  |         public AffinityGroupDomainMapDao affinityGroupDomainMapDao() { | ||||||
|  |             return Mockito.mock(AffinityGroupDomainMapDao.class); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Bean | ||||||
|  |         public DomainDao domainDao() { | ||||||
|  |             return Mockito.mock(DomainDao.class); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         public static class Library implements TypeFilter { |         public static class Library implements TypeFilter { | ||||||
| 
 | 
 | ||||||
|             @Override |             @Override | ||||||
|  | |||||||
| @ -410,6 +410,7 @@ CREATE TABLE `cloud`.`affinity_group` ( | |||||||
|   `description` varchar(4096) NULL, |   `description` varchar(4096) NULL, | ||||||
|   `domain_id` bigint unsigned NOT NULL, |   `domain_id` bigint unsigned NOT NULL, | ||||||
|   `account_id` bigint unsigned NOT NULL, |   `account_id` bigint unsigned NOT NULL, | ||||||
|  |   `acl_type` varchar(15) NOT NULL COMMENT 'ACL access type. can be Account/Domain', | ||||||
|   UNIQUE (`name`, `account_id`), |   UNIQUE (`name`, `account_id`), | ||||||
|   PRIMARY KEY  (`id`), |   PRIMARY KEY  (`id`), | ||||||
|   CONSTRAINT `fk_affinity_group__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`), |   CONSTRAINT `fk_affinity_group__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`), | ||||||
| @ -417,6 +418,7 @@ CREATE TABLE `cloud`.`affinity_group` ( | |||||||
|   CONSTRAINT `uc_affinity_group__uuid` UNIQUE (`uuid`) |   CONSTRAINT `uc_affinity_group__uuid` UNIQUE (`uuid`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| CREATE TABLE `cloud`.`affinity_group_vm_map` ( | CREATE TABLE `cloud`.`affinity_group_vm_map` ( | ||||||
|   `id` bigint unsigned NOT NULL auto_increment, |   `id` bigint unsigned NOT NULL auto_increment, | ||||||
|   `affinity_group_id` bigint unsigned NOT NULL, |   `affinity_group_id` bigint unsigned NOT NULL, | ||||||
| @ -426,7 +428,15 @@ CREATE TABLE `cloud`.`affinity_group_vm_map` ( | |||||||
|   CONSTRAINT `fk_affinity_group_vm_map___instance_id` FOREIGN KEY(`instance_id`) REFERENCES `user_vm` (`id`) ON DELETE CASCADE |   CONSTRAINT `fk_affinity_group_vm_map___instance_id` FOREIGN KEY(`instance_id`) REFERENCES `user_vm` (`id`) ON DELETE CASCADE | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | ||||||
| 
 | 
 | ||||||
| 
 | CREATE TABLE `cloud`.`affinity_group_domain_map` ( | ||||||
|  |   `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', | ||||||
|  |   `domain_id` bigint unsigned NOT NULL COMMENT 'domain id', | ||||||
|  |   `affinity_group_id` bigint unsigned NOT NULL COMMENT 'affinity group id', | ||||||
|  |   `subdomain_access` int(1) unsigned COMMENT '1 if affinity group can be accessible from the subdomain', | ||||||
|  |   PRIMARY KEY (`id`), | ||||||
|  |   CONSTRAINT `fk_affinity_group_domain_map__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, | ||||||
|  |   CONSTRAINT `fk_affinity_group_domain_map__affinity_group_id` FOREIGN KEY (`affinity_group_id`) REFERENCES `affinity_group`(`id`) ON DELETE CASCADE | ||||||
|  | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | ||||||
| 
 | 
 | ||||||
| CREATE TABLE `cloud`.`dedicated_resources` ( | CREATE TABLE `cloud`.`dedicated_resources` ( | ||||||
|   `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', |   `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', | ||||||
| @ -898,6 +908,7 @@ CREATE VIEW `cloud`.`affinity_group_view` AS | |||||||
|         affinity_group.type type, |         affinity_group.type type, | ||||||
|         affinity_group.description description, |         affinity_group.description description, | ||||||
|         affinity_group.uuid uuid, |         affinity_group.uuid uuid, | ||||||
|  | 		affinity_group.acl_type acl_type, | ||||||
|         account.id account_id, |         account.id account_id, | ||||||
|         account.uuid account_uuid, |         account.uuid account_uuid, | ||||||
|         account.account_name account_name, |         account.account_name account_name, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user