diff --git a/api/src/com/cloud/server/ResourceTag.java b/api/src/com/cloud/server/ResourceTag.java index 067cb973e2f..0bd5d734e30 100644 --- a/api/src/com/cloud/server/ResourceTag.java +++ b/api/src/com/cloud/server/ResourceTag.java @@ -38,6 +38,7 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit SecurityGroupRule(true, false), PublicIpAddress(true, true), Project(true, false), + Account(true, false), Vpc(true, true), NetworkACL(true, true), StaticRoute(true, false), diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 412ca5bead7..1632da95f95 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -3804,6 +3804,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @DB public boolean releasePublicIpRange(final long vlanDbId, final long userId, final Account caller) { VlanVO vlan = _vlanDao.findById(vlanDbId); + if(vlan == null) { + s_logger.warn("VLAN information for Account '" + caller + "', User '" + userId + "' VLAN '" + vlanDbId + "' is null. This is NPE situation."); + } // Verify range is dedicated boolean isAccountSpecific = false; diff --git a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java index 9373a08fcf8..9803ce753a3 100644 --- a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java +++ b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java @@ -16,28 +16,13 @@ // under the License. package com.cloud.tags; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import org.apache.cloudstack.api.Identity; -import org.apache.cloudstack.api.InternalIdentity; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Logger; - -import com.cloud.api.query.dao.ResourceTagJoinDao; import com.cloud.dc.DataCenterVO; import com.cloud.domain.PartOf; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; +import com.cloud.offerings.NetworkOfferingVO; import com.cloud.network.LBHealthCheckPolicyVO; import com.cloud.network.as.AutoScaleVmGroupVO; import com.cloud.network.as.AutoScaleVmProfileVO; @@ -58,7 +43,6 @@ import com.cloud.network.vpc.NetworkACLVO; import com.cloud.network.vpc.StaticRouteVO; import com.cloud.network.vpc.VpcOfferingVO; import com.cloud.network.vpc.VpcVO; -import com.cloud.offerings.NetworkOfferingVO; import com.cloud.projects.ProjectVO; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; @@ -72,6 +56,7 @@ import com.cloud.storage.VolumeVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; import com.cloud.user.DomainManager; import com.cloud.user.OwnedBy; import com.cloud.user.UserVO; @@ -89,11 +74,28 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.snapshot.VMSnapshotVO; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +import org.apache.commons.collections.MapUtils; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; public class TaggedResourceManagerImpl extends ManagerBase implements TaggedResourceService { public static final Logger s_logger = Logger.getLogger(TaggedResourceManagerImpl.class); - private static final Map> s_typeMap = new HashMap>(); + private static final Map> s_typeMap = new HashMap<>(); static { s_typeMap.put(ResourceObjectType.UserVm, UserVmVO.class); s_typeMap.put(ResourceObjectType.Volume, VolumeVO.class); @@ -108,6 +110,7 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso s_typeMap.put(ResourceObjectType.SecurityGroupRule, SecurityGroupRuleVO.class); s_typeMap.put(ResourceObjectType.PublicIpAddress, IPAddressVO.class); s_typeMap.put(ResourceObjectType.Project, ProjectVO.class); + s_typeMap.put(ResourceObjectType.Account, AccountVO.class); s_typeMap.put(ResourceObjectType.Vpc, VpcVO.class); s_typeMap.put(ResourceObjectType.Nic, NicVO.class); s_typeMap.put(ResourceObjectType.NetworkACL, NetworkACLItemVO.class); @@ -140,8 +143,6 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso @Inject ResourceTagDao _resourceTagDao; @Inject - ResourceTagJoinDao _resourceTagJoinDao; - @Inject DomainManager _domainMgr; @Inject AccountDao _accountDao; @@ -194,6 +195,12 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso domainId = ((SecurityGroupVO)SecurityGroup).getDomainId(); } + if (resourceType == ResourceObjectType.Account) { + AccountVO account = (AccountVO)entity; + accountId = account.getId(); + domainId = account.getDomainId(); + } + // if the resource type is network acl, get the accountId and domainId from VPC following: NetworkACLItem -> NetworkACL -> VPC if (resourceType == ResourceObjectType.NetworkACL) { NetworkACLItemVO aclItem = (NetworkACLItemVO)entity; @@ -223,7 +230,23 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso if ((domainId == null) || ((accountId != null) && (domainId.longValue() == -1))) { domainId = _accountDao.getDomainIdForGivenAccountId(accountId); } - return new Pair(accountId, domainId); + return new Pair<>(accountId, domainId); + } + + private void checkResourceAccessible(Long accountId, Long domainId, String exceptionMessage) { + Account caller = CallContext.current().getCallingAccount(); + if (Objects.equals(domainId, -1)) + { + throw new CloudRuntimeException("Invalid DomainId: -1"); + } + if (accountId != null) { + _accountMgr.checkAccess(caller, null, false, _accountMgr.getAccount(accountId)); + } else if (domainId != null && !_accountMgr.isNormalUser(caller.getId())) { + //check permissions; + _accountMgr.checkAccess(caller, _domainMgr.getDomain(domainId)); + } else { + throw new PermissionDeniedException(exceptionMessage); + } } @Override @@ -237,59 +260,6 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso throw new InvalidParameterValueException("Invalid resource type " + resourceTypeStr); } - @Override - @DB - @ActionEvent(eventType = EventTypes.EVENT_TAGS_CREATE, eventDescription = "creating resource tags") - public List createTags(final List resourceIds, final ResourceObjectType resourceType, final Map tags, final String customer) { - final Account caller = CallContext.current().getCallingAccount(); - - final List resourceTags = new ArrayList(tags.size()); - - Transaction.execute(new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - for (String key : tags.keySet()) { - for (String resourceId : resourceIds) { - if (!resourceType.resourceTagsSupport()) { - throw new InvalidParameterValueException("The resource type " + resourceType + " doesn't support resource tags"); - } - - long id = getResourceId(resourceId, resourceType); - String resourceUuid = getUuid(resourceId, resourceType); - - Pair accountDomainPair = getAccountDomain(id, resourceType); - Long domainId = accountDomainPair.second(); - Long accountId = accountDomainPair.first(); - - if ((domainId != null) && (domainId == -1)) { - throw new CloudRuntimeException("Invalid DomainId : -1"); - } - if (accountId != null) { - _accountMgr.checkAccess(caller, null, false, _accountMgr.getAccount(accountId)); - } else if (domainId != null && !_accountMgr.isNormalUser(caller.getId())) { - //check permissions; - _accountMgr.checkAccess(caller, _domainMgr.getDomain(domainId)); - } else { - throw new PermissionDeniedException("Account " + caller + " doesn't have permissions to create tags" + " for resource " + key); - } - - String value = tags.get(key); - - if (value == null || value.isEmpty()) { - throw new InvalidParameterValueException("Value for the key " + key + " is either null or empty"); - } - - ResourceTagVO resourceTag = new ResourceTagVO(key, value, accountDomainPair.first(), accountDomainPair.second(), id, resourceType, customer, resourceUuid); - resourceTag = _resourceTagDao.persist(resourceTag); - resourceTags.add(resourceTag); - } - } - } - }); - - return resourceTags; - } - @Override public String getUuid(String resourceId, ResourceObjectType resourceType) { if (!StringUtils.isNumeric(resourceId)) { @@ -308,65 +278,119 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso @Override @DB - @ActionEvent(eventType = EventTypes.EVENT_TAGS_DELETE, eventDescription = "deleting resource tags") - public boolean deleteTags(List resourceIds, ResourceObjectType resourceType, Map tags) { - Account caller = CallContext.current().getCallingAccount(); + @ActionEvent(eventType = EventTypes.EVENT_TAGS_CREATE, eventDescription = "creating resource tags") + public List createTags(final List resourceIds, final ResourceObjectType resourceType, final Map tags, final String customer) { + final Account caller = CallContext.current().getCallingAccount(); + final List resourceTags = new ArrayList<>(tags.size()); + + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + for (String key : tags.keySet()) { + for (String resourceId : resourceIds) { + if (!resourceType.resourceTagsSupport()) { + throw new InvalidParameterValueException("The resource type " + resourceType + " doesn't support resource tags"); + } + + long id = getResourceId(resourceId, resourceType); + String resourceUuid = getUuid(resourceId, resourceType); + + Pair accountDomainPair = getAccountDomain(id, resourceType); + Long domainId = accountDomainPair.second(); + Long accountId = accountDomainPair.first(); + + checkResourceAccessible(accountId, domainId, "Account '" + caller + + "' doesn't have permissions to create tags" + " for resource '" + id + "(" + key + ")'."); + + String value = tags.get(key); + + if (value == null || value.isEmpty()) { + throw new InvalidParameterValueException("Value for the key " + key + " is either null or empty"); + } + + ResourceTagVO resourceTag = new ResourceTagVO(key, value, accountDomainPair.first(), accountDomainPair.second(), id, resourceType, customer, resourceUuid); + resourceTag = _resourceTagDao.persist(resourceTag); + resourceTags.add(resourceTag); + } + } + } + }); + + return resourceTags; + } + + private List searchResourceTags(List resourceIds, ResourceObjectType resourceType) { + List resourceUuids = resourceIds.stream().map(resourceId -> getUuid(resourceId, resourceType)).collect(Collectors.toList()); SearchBuilder sb = _resourceTagDao.createSearchBuilder(); - sb.and().op("resourceId", sb.entity().getResourceId(), SearchCriteria.Op.IN); - sb.or("resourceUuid", sb.entity().getResourceUuid(), SearchCriteria.Op.IN); - sb.cp(); + sb.and("resourceUuid", sb.entity().getResourceUuid(), SearchCriteria.Op.IN); sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ); SearchCriteria sc = sb.create(); - sc.setParameters("resourceId", resourceIds.toArray()); - sc.setParameters("resourceUuid", resourceIds.toArray()); + sc.setParameters("resourceUuid", resourceUuids.toArray()); sc.setParameters("resourceType", resourceType); + return _resourceTagDao.search(sc, null); + } - List resourceTags = _resourceTagDao.search(sc, null); - ; - final List tagsToRemove = new ArrayList(); + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_TAGS_DELETE, eventDescription = "deleting resource tags") + public boolean deleteTags(List resourceIds, ResourceObjectType resourceType, Map tags) { + Account caller = CallContext.current().getCallingAccount(); + if(s_logger.isDebugEnabled()) { + s_logger.debug("ResourceIds to Find " + String.join(", ", resourceIds)); + } + List resourceTags = searchResourceTags(resourceIds, resourceType); + final List tagsToDelete = new ArrayList<>(); // Finalize which tags should be removed for (ResourceTag resourceTag : resourceTags) { //1) validate the permissions + if(s_logger.isDebugEnabled()) { + s_logger.debug("Resource Tag Id: " + resourceTag.getResourceId()); + s_logger.debug("Resource Tag AccountId: " + resourceTag.getAccountId()); + } Account owner = _accountMgr.getAccount(resourceTag.getAccountId()); + if(s_logger.isDebugEnabled()) { + s_logger.debug("Resource Owner: " + owner); + } _accountMgr.checkAccess(caller, null, false, owner); //2) Only remove tag if it matches key value pairs - if (tags != null && !tags.isEmpty()) { + if (MapUtils.isEmpty(tags)) { + tagsToDelete.add(resourceTag); + } else { for (String key : tags.keySet()) { - boolean canBeRemoved = false; + boolean deleteTag = false; if (resourceTag.getKey().equalsIgnoreCase(key)) { String value = tags.get(key); if (value != null) { if (resourceTag.getValue().equalsIgnoreCase(value)) { - canBeRemoved = true; + deleteTag = true; } } else { - canBeRemoved = true; + deleteTag = true; } - if (canBeRemoved) { - tagsToRemove.add(resourceTag); + if (deleteTag) { + tagsToDelete.add(resourceTag); break; } } } - } else { - tagsToRemove.add(resourceTag); } } - if (tagsToRemove.isEmpty()) { - throw new InvalidParameterValueException("Unable to find tags by parameters specified"); + if (tagsToDelete.isEmpty()) { + throw new InvalidParameterValueException("Unable to find any tags which conform to specified delete parameters."); } //Remove the tags Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { - for (ResourceTag tagToRemove : tagsToRemove) { + for (ResourceTag tagToRemove : tagsToDelete) { _resourceTagDao.remove(tagToRemove.getId()); - s_logger.debug("Removed the tag " + tagToRemove); + s_logger.debug("Removed the tag '" + tagToRemove + "' for resources (" + + String.join(", ", resourceIds) + ")"); } } }); @@ -375,7 +399,7 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso } @Override - public List listByResourceTypeAndId(ResourceObjectType type, long resourceId) { - return _resourceTagDao.listBy(resourceId, type); + public List listByResourceTypeAndId(ResourceObjectType resourceType, long resourceId) { + return _resourceTagDao.listBy(resourceId, resourceType); } } diff --git a/setup/dev/advanced.cfg b/setup/dev/advanced.cfg index d5762c39bce..d458b015e75 100644 --- a/setup/dev/advanced.cfg +++ b/setup/dev/advanced.cfg @@ -154,7 +154,7 @@ }, "logger": { - "LogFolderPath": "/tmp/" + "LogFolderPath": "/tmp" }, "globalConfig": [ { diff --git a/setup/dev/advancedsg.cfg b/setup/dev/advancedsg.cfg index 01f7fcc4662..5fbd02e6b14 100644 --- a/setup/dev/advancedsg.cfg +++ b/setup/dev/advancedsg.cfg @@ -109,7 +109,7 @@ }, "logger": { - "LogFolderPath": "/tmp/" + "LogFolderPath": "/tmp" }, "globalConfig": [ { diff --git a/setup/dev/basic.cfg b/setup/dev/basic.cfg index db8c59b09b4..b7f1486361a 100644 --- a/setup/dev/basic.cfg +++ b/setup/dev/basic.cfg @@ -110,7 +110,7 @@ }, "logger": { - "LogFolderPath": "/tmp/" + "LogFolderPath": "/tmp" }, "globalConfig": [ { diff --git a/setup/dev/local.cfg b/setup/dev/local.cfg index 89d56a56664..954d75fc331 100644 --- a/setup/dev/local.cfg +++ b/setup/dev/local.cfg @@ -25,7 +25,7 @@ }, "logger": { - "LogFolderPath": "/tmp/" + "LogFolderPath": "/tmp" }, "mgtSvr": [ { diff --git a/setup/dev/s3.cfg b/setup/dev/s3.cfg index a865e0dbc82..de28e5b2698 100644 --- a/setup/dev/s3.cfg +++ b/setup/dev/s3.cfg @@ -115,7 +115,7 @@ ], "logger": { - "LogFolderPath": "/tmp/" + "LogFolderPath": "/tmp" }, "mgtSvr": [ { diff --git a/test/integration/component/test_tags.py b/test/integration/component/test_tags.py index f9b0655016d..ed1aee7a0ee 100644 --- a/test/integration/component/test_tags.py +++ b/test/integration/component/test_tags.py @@ -185,9 +185,9 @@ class Services: "cidrlist": '0.0.0.0/0', }, # Cent OS 5.3 (64 bit) - "sleep": 60, + "sleep": 5, "ostype": 'CentOS 5.3 (64-bit)', - "timeout": 10, + "timeout": 5, "mode": 'advanced', } @@ -287,10 +287,12 @@ class TestResourceTags(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) for tag in self.rm_tags: - tag['tag_obj'].delete(self.apiclient, tag['resid'], - tag['restype'], - {tag['key']: tag['value']}) - + for concrete_tag in tag['tags']: + Tag.delete(self.apiclient, + tag['resid'], + tag['restype'], + {concrete_tag['key']: concrete_tag['value']}) + return @attr(tags=["advanced"], required_hardware="false") @@ -404,7 +406,7 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=lb_rule.id, resourceType='LoadBalancer', @@ -541,7 +543,7 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=nat_rule.id, resourceType='portForwardingRule', @@ -683,7 +685,7 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=fw_rule.id, resourceType='FirewallRule', @@ -835,7 +837,7 @@ class TestResourceTags(cloudstackTestCase): ) self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=vpn.id, resourceType='VPN', @@ -876,12 +878,15 @@ class TestResourceTags(cloudstackTestCase): # 1. Create a tag on VM using createTags API # 2. Delete above created tag using deleteTags API + tag_key = 'scope' + tag_value = 'test_05_vm_tag' + self.debug("Creating a tag for user VM") tag = Tag.create( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -891,8 +896,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -901,15 +906,15 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, "The tag value should match with the original value" ) vms = VirtualMachine.list( self.apiclient, listall=True, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( @@ -919,11 +924,11 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -935,8 +940,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( tags, @@ -1031,7 +1036,7 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.user_api_client, resourceIds=template.id, resourceType='Template', @@ -1124,7 +1129,7 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=iso.id, resourceType='ISO', @@ -1152,12 +1157,15 @@ class TestResourceTags(cloudstackTestCase): @attr(tags=["advanced", "basic"], required_hardware="false") def test_08_volume_tag(self): - """ Test creation, listing and deletion tagson volume + """ Test creation, listing and deletion tags on volume """ # Validate the following # 1. Create a tag on volume using createTags API # 2. Delete above created tag using deleteTags API + tag_key = 'scope' + tag_value = 'test_08_volume_tag' + if self.hypervisor.lower() == 'lxc': if not find_storage_pool_type(self.apiclient, storagetype='rbd'): self.skipTest("RBD storage type is required for data volumes for LXC") @@ -1181,7 +1189,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=volume.id, resourceType='volume', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -1191,8 +1199,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='volume', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -1201,14 +1209,14 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, 'The tag should have original value' ) vols = Volume.list(self.apiclient, listall=True, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(vols, list), @@ -1218,11 +1226,11 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=volume.id, resourceType='volume', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -1234,7 +1242,7 @@ class TestResourceTags(cloudstackTestCase): resourceType='volume', account=self.account.name, domainid=self.account.domainid, - key='region' + key=tag_key ) self.assertEqual( tags, @@ -1245,7 +1253,7 @@ class TestResourceTags(cloudstackTestCase): @attr(tags=["advanced", "basic"], required_hardware="false") def test_09_snapshot_tag(self): - """ Test creation, listing and deletion tag son snapshot + """ Test creation, listing and deletion tags on snapshot """ # Validate the following # 1. Create a tag on snapshot using createTags API @@ -1321,7 +1329,7 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=snapshot.id, resourceType='snapshot', @@ -1350,12 +1358,15 @@ class TestResourceTags(cloudstackTestCase): @attr(tags=["advanced"], required_hardware="false") def test_10_network_tag(self): - """ Testcreation, listing and deletion tags on guest network + """ Test creation, listing and deletion tags on guest network """ # Validate the following # 1. Create a tag on Network using createTags API # 2. Delete above created tag using deleteTags API + tag_key = 'scope' + tag_value = 'test_10_network_tag' + self.debug("Fetching the network details for account: %s" % self.account.name) networks = Network.list( @@ -1378,7 +1389,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=network.id, resourceType='Network', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -1388,8 +1399,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='Network', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -1398,7 +1409,7 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, 'The tag should have original value' ) @@ -1407,8 +1418,8 @@ class TestResourceTags(cloudstackTestCase): account=self.account.name, domainid=self.account.domainid, listall=True, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(networks, list), @@ -1418,11 +1429,11 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=network.id, resourceType='Network', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -1434,8 +1445,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='Network', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( tags, @@ -1452,6 +1463,9 @@ class TestResourceTags(cloudstackTestCase): # 1. Create a tag on VM using createTags API # 2. Delete above created tag using deleteTags API + tag_key = 'scope' + tag_value = 'test_11_migrate_tagged_vm_del' + if self.hypervisor.lower() in ['lxc']: self.skipTest("vm migrate feature is not supported on %s" % self.hypervisor.lower()) @@ -1496,7 +1510,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -1506,8 +1520,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -1517,7 +1531,7 @@ class TestResourceTags(cloudstackTestCase): self.assertEqual( tags[0].value, - 'India', + tag_value, 'The tag should have original value' ) @@ -1527,11 +1541,11 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -1543,8 +1557,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( tags, @@ -1562,12 +1576,15 @@ class TestResourceTags(cloudstackTestCase): # 2. Add same tag in upper case. # 3. Verify that tag creation failed. + tag_key = 'scope' + tag_value = 'test_13_tag_case_insensitive' + self.debug("Creating a tag for user VM") tag_1 = Tag.create( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag_1.__dict__) @@ -1577,8 +1594,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -1588,25 +1605,25 @@ class TestResourceTags(cloudstackTestCase): self.assertEqual( tags[0].value, - 'India', + tag_value, 'The tag should have original value' ) try: Tag.create(self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'REGION': 'INDIA'}) + tags={tag_key.upper(): tag_value.uppper()}) except Exception as e: pass else: assert("Creating same tag in upper case succeeded") try: - tag_1.delete( + Tag.delete( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -1618,8 +1635,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( tags, @@ -1637,13 +1654,16 @@ class TestResourceTags(cloudstackTestCase): # 1. Create more than 10 tags to VM using createTags API # 2. Create a tag with special characters on VM using createTags API + tag_key = 'scope' + tag_value = 'test_14_special_char_mutiple_tags' + self.debug("Creating a tag for user VM") tag = Tag.create( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', tags={ - 'region': 'India', + tag_key: tag_value, 'offering': 'high', 'type': 'webserver', 'priority': 'critical', @@ -1664,8 +1684,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -1674,16 +1694,16 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, 'The tag should have original value' ) # Cleanup - tag.delete( + Tag.delete( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', tags={ - 'region': 'India', + tag_key: tag_value, 'offering': 'high', 'type': 'webserver', 'priority': 'critical', @@ -1707,6 +1727,9 @@ class TestResourceTags(cloudstackTestCase): # 2. Create a tag on projects using createTags API # 3. Delete the tag. + tag_key = 'scope' + tag_value = 'test_15_project_tag' + # Create project as a domain admin project = Project.create( self.apiclient, @@ -1724,7 +1747,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=project.id, resourceType='project', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -1733,7 +1756,7 @@ class TestResourceTags(cloudstackTestCase): listall=True, resourceType='project', resourceIds=project.id, - key='region', + key=tag_key, ) self.debug("tags = %s" % tags) @@ -1744,15 +1767,15 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, 'The tag should have original value' ) projects = Project.list( self.apiclient, listall=True, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( @@ -1763,11 +1786,11 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=project.id, resourceType='project', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -1779,8 +1802,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='project', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( tags, @@ -1799,8 +1822,11 @@ class TestResourceTags(cloudstackTestCase): # 3. Login with other account and query the tags using # listTags API + tag_key = 'scope' + tag_value = 'test_16_query_tags_other_account' + self.debug("Creating user accounts..") - + user_account = Account.create( self.apiclient, self.services["user"], @@ -1836,7 +1862,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=iso.id, resourceType='ISO', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -1846,7 +1872,7 @@ class TestResourceTags(cloudstackTestCase): resourceType='ISO', account=user_account.name, domainid=user_account.domainid, - key='region', + key=tag_key, ) self.assertEqual( isinstance(tags, list), @@ -1855,7 +1881,7 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, "The tag value should match with the original value" ) @@ -1866,7 +1892,7 @@ class TestResourceTags(cloudstackTestCase): resourceType='ISO', account=other_user_account.name, domainid=other_user_account.domainid, - key='region', + key=tag_key, ) self.assertEqual( @@ -1875,6 +1901,16 @@ class TestResourceTags(cloudstackTestCase): "List tags should return empty response" ) + try: + Tag.delete( + self.apiclient, + resourceIds=iso.id, + resourceType='ISO', + tags={tag_key: tag_value} + ) + except Exception as e: + self.fail("Failed to delete the tag - %s" % e) + return @attr(tags=["advanced", "basic"], required_hardware="false") @@ -1887,6 +1923,9 @@ class TestResourceTags(cloudstackTestCase): # 3. Login with admin account and query the tags using # listTags API + tag_key = 'scope' + tag_value = 'test_17_query_tags_admin_account' + self.debug("Creating user accounts..") user_account = Account.create( @@ -1913,7 +1952,7 @@ class TestResourceTags(cloudstackTestCase): Tag.create(self.apiclient, resourceIds=iso.id, resourceType='ISO', - tags={'region': 'India'}) + tags={tag_key: tag_value}) tags = Tag.list( self.apiclient, @@ -1921,7 +1960,7 @@ class TestResourceTags(cloudstackTestCase): resourceType='ISO', account=user_account.name, domainid=user_account.domainid, - key='region', + key=tag_key, ) self.assertEqual( isinstance(tags, list), @@ -1930,7 +1969,7 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, "The tag value should match with the original value" ) @@ -1939,7 +1978,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, listall=True, resourceType='ISO', - key='region', + key=tag_key, ) self.assertEqual( isinstance(tags, list), @@ -1948,10 +1987,20 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, 'The tag should have original value' ) + try: + Tag.delete( + self.apiclient, + resourceIds=iso.id, + resourceType='ISO', + tags={tag_key: tag_value} + ) + except Exception as e: + self.fail("Failed to delete the tag - %s" % e) + return @attr(tags=["advanced", "basic", "simulator"], required_hardware="false") @@ -1962,12 +2011,15 @@ class TestResourceTags(cloudstackTestCase): # 1. Create a tag on supported resource type(ex:vms) # 2. Run the list API commands with passing invalid key parameter + tag_key = 'scope' + tag_value = 'test_18_invalid_list_parameters' + self.debug("Creating a tag for user VM") tag = Tag.create( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -1979,14 +2031,15 @@ class TestResourceTags(cloudstackTestCase): self.rm_tags.append({'tag_obj': tag, 'restype': 'userVM', 'resid': self.vm_1.id, - 'key': 'region', - 'value': 'India'}) + 'tags': [ + {'key': tag_key, 'value': tag_value} + ]}) self.debug("Passing invalid key parameter to the listAPI for vms") vms = VirtualMachine.list(self.apiclient, - **{'tags[0].key': 'region111', - 'tags[0].value': 'India', + **{'tags[0].key': tag_key + '1', + 'tags[0].value': tag_value, 'listall': 'True'} ) self.assertEqual( @@ -2006,12 +2059,15 @@ class TestResourceTags(cloudstackTestCase): # 1. Deletion of a tag without any errors. # 2. Add same tag. + tag_key = 'scope' + tag_value = 'test_19_delete_add_same_tag' + self.debug("Creating a tag for user VM") tag = Tag.create( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -2021,8 +2077,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -2032,17 +2088,17 @@ class TestResourceTags(cloudstackTestCase): self.assertEqual( tags[0].value, - "India", + tag_value, "Tag created with incorrect value" ) self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -2054,8 +2110,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( tags, @@ -2067,7 +2123,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -2077,8 +2133,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -2087,17 +2143,17 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual(tags[0].value, - "India", + tag_value, "Tag created with incorrect value" ) self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -2107,8 +2163,11 @@ class TestResourceTags(cloudstackTestCase): def test_20_create_tags_multiple_resources(self): "Test creation of same tag on multiple resources" - self.debug("Creating volume for account: %s " % - self.account.name) + tag_key = 'scope' + tag_value = 'test_20_create_tags_multiple_resources' + + self.debug("Creating volume for account: %s " % self.account.name) + volume = Volume.create( self.apiclient, self.services["volume"], @@ -2126,7 +2185,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=volume.id, resourceType='volume', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -2136,7 +2195,7 @@ class TestResourceTags(cloudstackTestCase): resourceType='volume', account=self.account.name, domainid=self.account.domainid, - key='region', + key=tag_key, ) self.assertEqual( isinstance(tags, list), @@ -2145,7 +2204,7 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, 'The tag should have original value' ) @@ -2154,7 +2213,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -2164,8 +2223,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -2175,17 +2234,17 @@ class TestResourceTags(cloudstackTestCase): self.assertEqual( tags[0].value, - "India", - "Tag created with incorrect value" + tag_value, + "Expected tag value is incorrect" ) self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -2197,8 +2256,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( tags, @@ -2212,6 +2271,9 @@ class TestResourceTags(cloudstackTestCase): def test_21_create_tag_stopped_vm(self): "Test creation of tag on stopped vm." + tag_key = 'scope' + tag_value = 'test_21_create_tag_stopped_vm' + try: self.debug("Stopping the virtual machine: %s" % self.vm_1.name) # Stop virtual machine @@ -2222,7 +2284,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -2232,8 +2294,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -2243,16 +2305,16 @@ class TestResourceTags(cloudstackTestCase): self.assertEqual( tags[0].value, - "India", + tag_value, "Tag created with incorrect value" ) self.debug("Deleting the created tag..") - tag.delete( + Tag.delete( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Exception occured - %s" % e) @@ -2262,6 +2324,9 @@ class TestResourceTags(cloudstackTestCase): def test_22_create_tag_destroyed_vm(self): "Test creation of tag on stopped vm." + tag_key = 'scope' + tag_value = 'test_22_create_tag_destroyed_vm' + self.debug("Destroying instance: %s" % self.vm_1.name) self.vm_1.delete(self.apiclient, expunge=False) @@ -2270,7 +2335,7 @@ class TestResourceTags(cloudstackTestCase): self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -2280,8 +2345,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='userVM', account=self.account.name, domainid=self.account.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -2291,17 +2356,17 @@ class TestResourceTags(cloudstackTestCase): self.assertEqual( tags[0].value, - "India", + tag_value, "Tag created with incorrect value" ) self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.apiclient, resourceIds=self.vm_1.id, resourceType='userVM', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -2405,8 +2470,8 @@ class TestResourceTags(cloudstackTestCase): return @attr(tags=["advanced"], required_hardware="false") - def test_24_public_IP_tag(self): - """ Testcreation, adding and removing tag on public IP address + def test_24_public_ip_tag(self): + """ Test creation, adding and removing tag on public IP address """ # Validate the following # 1. Create a domain and admin account under the new domain @@ -2414,6 +2479,9 @@ class TestResourceTags(cloudstackTestCase): # 3. Delete above created tag using deleteTags API # 4. Perform steps 2&3 using domain-admin + tag_key = 'scope' + tag_value = 'test_24_public_ip_tag' + self.debug("Creating a sub-domain under: %s" % self.domain.name) self.child_domain = Domain.create( self.apiclient, @@ -2479,7 +2547,7 @@ class TestResourceTags(cloudstackTestCase): self.dom_admin_api_client, resourceIds=public_ip.ipaddress.id, resourceType='PublicIpAddress', - tags={'region': 'India'} + tags={tag_key: tag_value} ) self.debug("Tag created: %s" % tag.__dict__) @@ -2489,8 +2557,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='PublicIpAddress', account=self.child_do_admin.name, domainid=self.child_do_admin.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(tags, list), @@ -2499,7 +2567,7 @@ class TestResourceTags(cloudstackTestCase): ) self.assertEqual( tags[0].value, - 'India', + tag_value, 'The tag should have original value' ) publicIps = PublicIPAddress.list( @@ -2507,8 +2575,8 @@ class TestResourceTags(cloudstackTestCase): account=self.child_do_admin.name, domainid=self.child_do_admin.domainid, listall=True, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( isinstance(publicIps, list), @@ -2518,11 +2586,11 @@ class TestResourceTags(cloudstackTestCase): self.debug("Deleting the created tag..") try: - tag.delete( + Tag.delete( self.dom_admin_api_client, resourceIds=public_ip.ipaddress.id, resourceType='PublicIpAddress', - tags={'region': 'India'} + tags={tag_key: tag_value} ) except Exception as e: self.fail("Failed to delete the tag - %s" % e) @@ -2534,8 +2602,8 @@ class TestResourceTags(cloudstackTestCase): resourceType='PublicIpAddress', account=self.child_do_admin.name, domainid=self.child_do_admin.domainid, - key='region', - value='India' + key=tag_key, + value=tag_value ) self.assertEqual( tags, @@ -2543,3 +2611,366 @@ class TestResourceTags(cloudstackTestCase): "List tags should return empty response" ) return + + def __test_account_tags(self, apiclient, account, listall = False): + set_tags = {'primary-contact-name': 'John Doe', + 'primary-contact-phone': '1-022-333-444'} + + Tag.create( + apiclient, + resourceIds=account.id, + resourceType='Account', + tags=set_tags) + + received_tags = Tag.list( + apiclient, + resourceId=account.id, + listAll=listall, + resourceType='Account') + + self.assertEqual( + isinstance(received_tags, list), + True, + "List tags should return list response." + ) + + received_tag_map = {} + for t in received_tags: + received_tag_map[t.key] = t.value + + self.assertEqual( + set_tags, + received_tag_map, + "Tags saved and received differ." + ) + + try: + Tag.delete( + apiclient, + resourceIds=account.id, + resourceType='Account', + tags=set_tags) + except Exception as e: + self.fail("Failed to delete the tag - %s" % e) + + received_tags_removed = Tag.list( + apiclient, + resourceId=account.id, + listAll=listall, + resourceType='Account') + + self.assertEqual( + received_tags_removed, + None, + "List tags should return empty list response when tags are removed." + ) + return + + @attr(tags=["advanced","basic"], required_hardware="false") + def test_25_admin_account_tags(self): + '''Test create, list, delete tag for admin account from admin account''' + self.debug("Creating a tag for Admin account") + admin_account = Account.list(self.apiclient, name='admin') + self.__test_account_tags(self.apiclient, admin_account[0]) + return + + @attr(tags=["advanced","basic"], required_hardware="false") + def test_26_domain_admin_account_tags(self): + child_domain = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) + child_domain_admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=child_domain.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(child_domain_admin) + self.cleanup.append(child_domain) + domain_admin_api_client = self.testClient.getUserApiClient( + UserName=child_domain_admin.name, + DomainName=child_domain_admin.domain + ) + self.__test_account_tags(domain_admin_api_client, child_domain_admin) + return + + @attr(tags=["advanced","basic"], required_hardware="false") + def test_27_regular_user_account_tags(self): + regular_account = Account.create( + self.apiclient, + self.services["account"], + admin=False, + domainid=self.domain.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(regular_account) + regular_account_api_client = self.testClient.getUserApiClient(UserName=regular_account.name, DomainName=self.domain.name) + self.__test_account_tags(regular_account_api_client, regular_account) + return + + @attr(tags=["advanced","basic"], required_hardware="false") + def test_28_admin_access_domain_admin_account_tags(self): + '''Test create, list, delete tag for domain admin account from admin account''' + child_domain = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) + child_domain_admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=child_domain.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(child_domain_admin) + self.cleanup.append(child_domain) + self.__test_account_tags(self.apiclient, child_domain_admin, listall = True) + return + + @attr(tags=["advanced","basic"], required_hardware="false") + def test_29_admin_access_user_account_tags(self): + '''Test create, list, delete tag for user account from admin account''' + regular_account = Account.create( + self.apiclient, + self.services["account"], + admin=False + ) + # Cleanup the resources created at end of test + self.cleanup.append(regular_account) + self.__test_account_tags(self.apiclient, regular_account, listall = True) + return + + @attr(tags=["advanced","basic"], required_hardware="false") + def test_30_domain_admin_access_user_account_same_domain_tags(self): + '''Test create, list, delete tag for user account from admin account''' + + child_domain = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) + child_domain_admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=child_domain.id + ) + + regular_account = Account.create( + self.apiclient, + self.services["account"], + admin=False, + domainid=child_domain.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(regular_account) + + # Cleanup the resources created at end of test + self.cleanup.append(child_domain_admin) + self.cleanup.append(child_domain) + + domain_admin_api_client = self.testClient.getUserApiClient( + UserName=child_domain_admin.name, + DomainName=child_domain.name + ) + self.__test_account_tags(domain_admin_api_client, regular_account, listall = True) + return + + @attr(tags=["advanced","basic"], required_hardware="false") + def test_31_user_cant_remove_update_admin_tags(self): + '''Tests that an user is unable to remove, modify tags created by admin but should access''' + + tag_key_user = 'scope_user' + tag_value_user = 'test_31_user_cant_remove_update_admin_tags' + + tag_key_admin = 'scope_admin' + tag_value_admin = 'test_31_user_cant_remove_update_admin_tags' + + regular_account = Account.create( + self.apiclient, + self.services["account"], + admin=False + ) + self.cleanup.append(regular_account) + + regular_account_api_client = self.testClient.getUserApiClient(UserName=regular_account.name, DomainName=self.domain.name) + + def create_admin_tag(): + return Tag.create( + self.apiclient, + resourceIds=regular_account.id, + resourceType='Account', + tags={ tag_key_admin: tag_value_admin}) + + + def create_user_tag(): + return Tag.create( + regular_account_api_client, + resourceIds=regular_account.id, + resourceType='Account', + tags={ tag_key_user: tag_value_user}) + + create_admin_tag() + create_user_tag() + + # + # List test expressions + # + def list_tags(apiclient, listAll): + return Tag.list( + apiclient, + resourceId=regular_account.id, + listAll=listAll, + resourceType='Account') + + def tags_to_map(tags): + m = {} + for t in tags: + m[t.key] = t.value + return m + + # admin requests user account tags and gets None (without listall) + received_tags_admin = list_tags(self.apiclient, False) + self.assertEqual( + received_tags_admin, + None, + "List tags should return empty list response when tags are not set on self-owned account." + ) + + # admin requests user account tags and gets all (with listall) + received_tags_admin_listall = list_tags(self.apiclient, True) + self.assertEqual( + tags_to_map(received_tags_admin_listall), + {tag_key_admin: tag_value_admin, tag_key_user: tag_value_user}, + "List (with listAll=true) tags should return information for admin tags and user tags" + ) + + # user requests own account tags and receives all (without listall) + received_tags_user = list_tags(regular_account_api_client, False) + self.assertEqual( + tags_to_map(received_tags_user), + {tag_key_admin: tag_value_admin, tag_key_user: tag_value_user}, + "List (with listAll=false) tags should return information for user tags" + ) + + # user requests own account tags and receives all (with listall) + received_tags_user_listall = list_tags(regular_account_api_client, True) + self.assertEqual( + tags_to_map(received_tags_user_listall), + {tag_key_admin: tag_value_admin, tag_key_user: tag_value_user}, + "List (with listAll=false) tags should return information for user tags" + ) + + # + # Delete test expressions + # + + def delete_tags(apiclient, tags): + Tag.delete( + apiclient, + resourceIds=regular_account.id, + resourceType='Account', + tags=tags) + + # user tries to delete admin tag on own account and succeeds + try: + delete_tags(regular_account_api_client, {tag_key_admin: tag_value_admin}) + except Exception as e: + self.fail("Regular user is not able to delete administrator tag on own account - %s" % e) + + # user tries to delete user tag and succeeds + try: + delete_tags(regular_account_api_client, {tag_key_user: tag_value_user}) + except Exception as e: + self.fail("Regular user is not able to delete own tag - %s" % e) + + # recover tag to run admin tests + create_user_tag() + create_admin_tag() + + # admin tries to delete tags and succeeds + try: + delete_tags(self.apiclient, {tag_key_admin: tag_value_admin, tag_key_user: tag_value_user}) + except Exception as e: + self.fail("Administrator is not able to delete a tag - %s" % e) + + return + + @attr(tags=["advanced","basic"], required_hardware="false") + def test_32_user_a_doesnt_have_access_to_user_b_tags(self): + '''Test resource security between regular accounts A and B''' + + tag_key_user1 = 'scope_user1' + tag_value_user1 = 'test_32_user_a_doesnt_have_access_to_user_b_tags-user1' + + tag_key_user2 = 'scope_user2' + tag_value_user2 = 'test_32_user_a_doesnt_have_access_to_user_b_tags-user2' + + regular_account1 = Account.create( + self.apiclient, + self.services["account"], + admin=False + ) + self.cleanup.append(regular_account1) + + regular_account_api_client1 = self.testClient.getUserApiClient(UserName=regular_account1.name, DomainName=self.domain.name) + + regular_account2 = Account.create( + self.apiclient, + self.services["account"], + admin=False + ) + self.cleanup.append(regular_account2) + + regular_account_api_client2 = self.testClient.getUserApiClient(UserName=regular_account2.name, DomainName=self.domain.name) + + Tag.create( + regular_account_api_client1, + resourceIds=regular_account1.id, + resourceType='Account', + tags={tag_key_user1: tag_value_user1}) + + Tag.create( + regular_account_api_client2, + resourceIds=regular_account2.id, + resourceType='Account', + tags={tag_key_user2: tag_value_user2}) + + try: + Tag.list( + regular_account_api_client1, + resourceId=regular_account2.id, + listAll=listAll, + resourceType='Account') + except Exception as e: + pass + else: + self.fail("User1 has access to list tags of User2.") + + try: + Tag.delete( + regular_account_api_client1, + resourceIds=regular_account2.id, + resourceType='Account', + tags={tag_key_user2: tag_value_user2}) + except Exception as e: + pass + else: + self.fail("User1 has access to delete tags of User2.") + + try: + Tag.create( + regular_account_api_client1, + resourceIds=regular_account2.id, + resourceType='Account', + tags={tag_key_user1: tag_value_user1}) + except Exception as e: + pass + else: + self.fail("User1 has access to create tags for User2.") + + return diff --git a/tools/marvin/marvin/cloudstackConnection.py b/tools/marvin/marvin/cloudstackConnection.py index d044fdd0eb4..826069742fe 100644 --- a/tools/marvin/marvin/cloudstackConnection.py +++ b/tools/marvin/marvin/cloudstackConnection.py @@ -103,8 +103,8 @@ class CSConnection(object): elif job_status == JOB_FAILED: raise Exception("Job failed: %s"\ % async_response) - time.sleep(5) - timeout -= 5 + time.sleep(1) + timeout -= 1 self.logger.debug("=== JobId:%s is Still Processing, " "Will TimeOut in:%s ====" % (str(jobid), str(timeout))) diff --git a/tools/marvin/marvin/deployDataCenter.py b/tools/marvin/marvin/deployDataCenter.py index 8d553c872e1..0ef3f3c3ea9 100644 --- a/tools/marvin/marvin/deployDataCenter.py +++ b/tools/marvin/marvin/deployDataCenter.py @@ -35,6 +35,7 @@ from marvin.lib.utils import (random_gen) from marvin.config.test_data import test_data from sys import exit import os +import errno import pickle from time import sleep, strftime, localtime from optparse import OptionParser @@ -65,18 +66,16 @@ class DeployDataCenters(object): def __persistDcConfig(self): try: if self.__logFolderPath: - dc_file_path = self.__logFolderPath + "/dc_entries.obj" + dc_file_path = os.path.join(self.__logFolderPath, "dc_entries.obj") else: ts = strftime("%b_%d_%Y_%H_%M_%S", localtime()) dc_file_path = "dc_entries_" + str(ts) + ".obj" + file_to_write = open(dc_file_path, 'w') if file_to_write: pickle.dump(self.__cleanUp, file_to_write) - print "\n=== Data Center Settings are dumped to %s===" % \ - dc_file_path - self.__tcRunLogger.debug( - "\n=== Data Center Settings are dumped to %s===" % - dc_file_path) + print "\n=== Data Center Settings are dumped to %s===" % dc_file_path + self.__tcRunLogger.debug("\n=== Data Center Settings are dumped to %s===" % dc_file_path) except Exception as e: print "Exception Occurred while persisting DC Settings: %s" % \ GetDetailExceptionInfo(e) @@ -1110,38 +1109,46 @@ class DeleteDataCenters: finally: return ret +def mkdirpath(path): + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise if __name__ == "__main__": ''' @Desc : This module facilitates the following: 1. Deploying DataCenter by using the input provided configuration. - EX: python deployDataCenter.py -i + EX: python deployDataCenter.py -i [-l ] 2. Removes a created DataCenter by providing the input configuration file and data center settings file EX: python deployDataCenter.py -i - -r + -r [-l ] ''' parser = OptionParser() - parser.add_option("-i", "--input", action="store", - default=None, dest="input", - help="the path \ - where the json config file generated") + parser.add_option("-i", "--input", action="store", default=None, dest="input", + help="The path where the json zones config file is located.") + + parser.add_option("-r", "--remove", action="store", default=None, dest="remove", + help="The path to file where the created dc entries are kept.") + + parser.add_option("-l", "--logdir", action="store", default=None, dest="logdir", + help="The directory where result logs of running the script are stored:" + "[dc_entries.obj, failed_plus_exceptions.txt, runinfo.txt]." + + "Created automatically if doesn't exists.") - parser.add_option("-r", "--remove", action="store", - default=None, dest="remove", - help="path to file\ - where the created dc entries are kept") (options, args) = parser.parse_args() ''' Verify the input validity ''' if options.input is None and options.remove is None: - print "\n==== For DeployDataCenter: Please Specify a " \ - "Valid Input Configuration File====" - print "\n==== For DeleteDataCenters: Please Specify a " \ - "Valid Input Configuration File and DC Settings====" + print "\n==== For DeployDataCenter: Please Specify a valid Input Configuration File====" + print "\n==== For DeleteDataCenters: Please Specify a valid Input Configuration File and DC Settings====" exit(1) ''' @@ -1161,8 +1168,10 @@ if __name__ == "__main__": cfg = configGenerator.getSetupConfig(options.input) log = cfg.logger - ret = log_obj.createLogs("DeployDataCenter", - log) + if options.logdir != None: + mkdirpath(options.logdir) + + ret = log_obj.createLogs("DeployDataCenter", log, options.logdir, options.logdir == None) if ret != FAILED: log_folder_path = log_obj.getLogFolderPath() tc_run_logger = log_obj.getLogger() diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 02ee138aa1f..b7f25ace9bc 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -4304,7 +4304,8 @@ class Tag: }) return Tag(apiclient.createTags(cmd).__dict__) - def delete(self, apiclient, resourceIds, resourceType, tags): + @classmethod + def delete(cls, apiclient, resourceIds, resourceType, tags): """Delete tags""" cmd = deleteTags.deleteTagsCmd() diff --git a/tools/marvin/marvin/marvinLog.py b/tools/marvin/marvin/marvinLog.py index ea8eaee7dff..582d2e14457 100644 --- a/tools/marvin/marvin/marvinLog.py +++ b/tools/marvin/marvin/marvinLog.py @@ -128,7 +128,7 @@ class MarvinLog: def createLogs(self, test_module_name=None, log_cfg=None, - user_provided_logpath=None): + user_provided_logpath=None, use_temp_path=True): ''' @Name : createLogs @Desc : Gets the Logger with file paths initialized and created @@ -140,29 +140,34 @@ class MarvinLog: If user provided log path is available, then one in cfg will not be picked up. + use_temp_path: Boolean value which specifies either logs will + be prepended by random path or not. @Output : SUCCESS\FAILED ''' try: - temp_ts = time.strftime("%b_%d_%Y_%H_%M_%S", - time.localtime()) + temp_ts = time.strftime("%b_%d_%Y_%H_%M_%S", time.localtime()) + if test_module_name is None: temp_path = temp_ts + "_" + random_gen() else: - temp_path = str(test_module_name) + \ - "__" + str(temp_ts) + "_" + random_gen() + temp_path = str(test_module_name) + "__" + str(temp_ts) + "_" + random_gen() if user_provided_logpath: - temp_dir = user_provided_logpath + "/MarvinLogs" + temp_dir = os.path.join(user_provided_logpath, "MarvinLogs") elif ((log_cfg is not None) and ('LogFolderPath' in log_cfg.__dict__.keys()) and (log_cfg.__dict__.get('LogFolderPath') is not None)): - temp_dir = \ - log_cfg.__dict__.get('LogFolderPath') + "/MarvinLogs" + temp_dir = os.path.join(log_cfg.__dict__.get('LogFolderPath'), "MarvinLogs") - self.__logFolderDir = temp_dir + "//" + temp_path - print "\n==== Log Folder Path: %s. " \ - "All logs will be available here ====" \ - % str(self.__logFolderDir) + if use_temp_path == True: + self.__logFolderDir = os.path.join(temp_dir, temp_path) + else: + if test_module_name == None: + self.__logFolderDir = temp_dir + else: + self.__logFolderDir = os.path.join(temp_dir, str(test_module_name)) + + print "\n==== Log Folder Path: %s. All logs will be available here ====" % str(self.__logFolderDir) os.makedirs(self.__logFolderDir) ''' @@ -171,9 +176,10 @@ class MarvinLog: 2. RunLog contains the complete Run Information for Test Run 3. ResultFile contains the TC result information for Test Run ''' - tc_failed_exception_log = \ - self.__logFolderDir + "/failed_plus_exceptions.txt" - tc_run_log = self.__logFolderDir + "/runinfo.txt" + + tc_failed_exception_log = os.path.join(self.__logFolderDir, "failed_plus_exceptions.txt") + tc_run_log = os.path.join(self.__logFolderDir, "runinfo.txt") + if self.__setLogHandler(tc_run_log, log_level=logging.DEBUG) != FAILED: self.__setLogHandler(tc_failed_exception_log,