diff --git a/.travis.yml b/.travis.yml index d8e6f96ea15..ee52d9e5ab0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,8 @@ env: global: - PATH=$HOME/.local/bin:$PATH matrix: - - TESTS="smoke/test_affinity_groups + - TESTS="smoke/test_accounts + smoke/test_affinity_groups smoke/test_affinity_groups_projects smoke/test_certauthority_root smoke/test_deploy_vgpu_enabled_vm @@ -47,6 +48,8 @@ env: - TESTS="smoke/test_hostha_kvm smoke/test_hostha_simulator + smoke/test_host_annotations + smoke/test_host_maintenance smoke/test_hosts smoke/test_internal_lb smoke/test_iso @@ -66,12 +69,14 @@ env: smoke/test_over_provisioning smoke/test_password_server smoke/test_portable_publicip - smoke/test_primary_storage - smoke/test_privategw_acl - smoke/test_public_ip_range - smoke/test_pvlan" + smoke/test_portforwardingrules" - - TESTS="smoke/test_regions + - TESTS="smoke/test_primary_storage + smoke/test_privategw_acl + smoke/test_projects + smoke/test_public_ip_range + smoke/test_pvlan + smoke/test_regions smoke/test_reset_vm_on_reboot smoke/test_resource_detail smoke/test_router_dhcphosts @@ -86,6 +91,7 @@ env: smoke/test_ssvm smoke/test_staticroles smoke/test_templates + smoke/test_usage smoke/test_usage_events smoke/test_vm_life_cycle smoke/test_vm_snapshots @@ -99,16 +105,21 @@ env: smoke/misc/test_vm_sync" - TESTS="component/find_hosts_for_migration - smoke/test_accounts component/test_acl_isolatednetwork_delete component/test_acl_listsnapshot component/test_acl_listvm component/test_acl_listvolume component/test_acl_sharednetwork - component/test_acl_sharednetwork_deployVM-impersonation" + component/test_affinity_groups_projects" + + - TESTS="component/test_allocation_states + component/test_acl_sharednetwork_deployVM-impersonation + component/test_affinity_groups_projects + component/test_cpu_domain_limits component/test_cpu_limits" - TESTS="component/test_cpu_max_limits + component/test_acl_isolatednetwork component/test_cpu_project_limits component/test_deploy_vm_userdata_multi_nic component/test_egress_fw_rules @@ -124,38 +135,28 @@ env: component/test_non_contiguous_vlan component/test_persistent_networks" - - TESTS="smoke/test_projects - component/test_project_configs + - TESTS="component/test_project_configs + component/test_project_limits component/test_project_usage - smoke/test_regions + component/test_project_resources component/test_regions_accounts component/test_routers - component/test_snapshots" + component/test_snapshots + component/test_stopped_vm" - - TESTS="component/test_project_limits - component/test_resource_limits - smoke/test_host_maintenance" - - - TESTS="component/test_stopped_vm + - TESTS="component/test_resource_limits component/test_tags - component/test_templates - component/test_update_vm - smoke/test_usage" + component/test_templates" - - TESTS="component/test_volumes + - TESTS="component/test_update_vm + component/test_volumes + component/test_vpc component/test_vpc_network component/test_vpc_offerings component/test_vpn_users" - # FIXME: fix following tests and include them in Travis -# - TESTS="component/test_affinity_groups_projects" -# - TESTS="component/test_allocation_states" -# - TESTS="component/test_vpc" -# - TESTS="component/test_project_resources" -# - TESTS="component/test_cpu_domain_limits" -# - TESTS="component/test_acl_isolatednetwork" -# - TESTS="component/test_organization_states" +# - TESTS="component/test_organization_states" Please add when CLOUDSTACK-7735 is fixed before_install: travis_wait 30 ./tools/travis/before_install.sh install: ./tools/travis/install.sh diff --git a/test/integration/component/test_affinity_groups_projects.py b/test/integration/component/test_affinity_groups_projects.py index dbcc92b849b..4be673a6a7b 100644 --- a/test/integration/component/test_affinity_groups_projects.py +++ b/test/integration/component/test_affinity_groups_projects.py @@ -914,21 +914,22 @@ class TestDeployVMAffinityGroups(cloudstackTestCase): cls.services["virtual_machine"]["zoneid"] = cls.zone.id cls.services["template"] = cls.template.id cls.services["zoneid"] = cls.zone.id - + cls._cleanup = [] cls.domain_admin_account = Account.create( cls.api_client, cls.services["domain_admin_account"], domainid=cls.domain.id, admin=True ) - + cls._cleanup.append(cls.domain_admin_account) cls.domain_api_client = cls.testClient.getUserApiClient(cls.domain_admin_account.name, cls.domain.name, 2) cls.account = Account.create( cls.api_client, cls.services["account"], domainid=cls.domain.id - ) + ) + cls._cleanup.append(cls.account) cls.account_api_client = cls.testClient.getUserApiClient(cls.account.name, cls.domain.name, 0) @@ -937,22 +938,24 @@ class TestDeployVMAffinityGroups(cloudstackTestCase): cls.services["account_not_in_project"], domainid=cls.domain.id ) + cls._cleanup.append(cls.account_not_in_project) cls.account_not_in_project_api_client = cls.testClient.getUserApiClient(cls.account_not_in_project.name, cls.domain.name, 0) - + cls._proj_toclean = [] cls.project = Project.create( cls.api_client, cls.services["project"], account=cls.domain_admin_account.name, domainid=cls.domain_admin_account.domainid ) - + cls._proj_toclean.append(cls.project) cls.project2 = Project.create( cls.api_client, cls.services["project2"], account=cls.domain_admin_account.name, domainid=cls.domain_admin_account.domainid ) + cls._proj_toclean.append(cls.project2) cls.debug("Created project with ID: %s" % cls.project.id) cls.debug("Created project2 with ID: %s" % cls.project2.id) @@ -969,7 +972,6 @@ class TestDeployVMAffinityGroups(cloudstackTestCase): domainid=cls.account.domainid ) - cls._cleanup = [] return def setUp(self): @@ -988,8 +990,9 @@ class TestDeployVMAffinityGroups(cloudstackTestCase): @classmethod def tearDownClass(cls): try: - cls.domain.delete(cls.api_client, cleanup=True) + cleanup_resources(cls.api_client, cls._proj_toclean) cleanup_resources(cls.api_client, cls._cleanup) + cls.domain.delete(cls.api_client, cleanup=True) except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) @@ -1076,6 +1079,7 @@ class TestDeployVMAffinityGroups(cloudstackTestCase): if vm_failed: vm_failed.expunge(self.api_client) + wait_for_cleanup(self.api_client, ["expunge.delay", "expunge.interval"]) self.cleanup.append(aff_grp) diff --git a/test/integration/component/test_allocation_states.py b/test/integration/component/test_allocation_states.py index 6aad63db08d..78c720356a0 100644 --- a/test/integration/component/test_allocation_states.py +++ b/test/integration/component/test_allocation_states.py @@ -93,6 +93,7 @@ class TestAllocationState(cloudstackTestCase): # Get Zone, Domain and templates cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + cls.hypervisor = cls.testClient.getHypervisorInfo() cls.template = get_template( cls.api_client, cls.zone.id, @@ -268,12 +269,19 @@ class TestAllocationState(cloudstackTestCase): # 1. List secondary storage # 2. Check state is "Up" or not + if self.hypervisor.lower() == 'simulator': + self.skipTest("Hypervisor is simulator skipping") + sec_storages = Host.list( self.apiclient, zoneid=self.zone.id, type='SecondaryStorageVM', - listall=True + listall=True ) + + if sec_storages is None: + self.skipTest("SSVM is not provisioned yet, skipping") + self.assertEqual( isinstance(sec_storages, list), True, diff --git a/test/integration/component/test_cpu_domain_limits.py b/test/integration/component/test_cpu_domain_limits.py index f3dc5642364..4d7c165bfcc 100644 --- a/test/integration/component/test_cpu_domain_limits.py +++ b/test/integration/component/test_cpu_domain_limits.py @@ -19,6 +19,7 @@ """ # Import Local Modules from nose.plugins.attrib import attr +from marvin.cloudstackAPI import createServiceOffering from marvin.cloudstackTestCase import cloudstackTestCase from marvin.lib.base import ( Account, @@ -36,80 +37,36 @@ from marvin.lib.common import (get_domain, from marvin.lib.utils import cleanup_resources from marvin.codes import ERROR_NO_HOST_FOR_MIGRATION -class Services: - """Test resource limit services - """ - - def __init__(self): - self.services = { - "account": { - "email": "test@test.com", - "firstname": "Test", - "lastname": "User", - "username": "resource", - # Random characters are appended for unique - # username - "password": "password", - }, - "service_offering": { - "name": "Tiny Instance", - "displaytext": "Tiny Instance", - "cpunumber": 4, - "cpuspeed": 100, # in MHz - "memory": 128, # In MBs - }, - "virtual_machine": { - "displayname": "TestVM", - "username": "root", - "password": "password", - "ssh_port": 22, - "hypervisor": 'KVM', - "privateport": 22, - "publicport": 22, - "protocol": 'TCP', - }, - "network": { - "name": "Test Network", - "displaytext": "Test Network", - "netmask": '255.255.255.0' - }, - "project": { - "name": "Project", - "displaytext": "Test project", - }, - "domain": { - "name": "Domain", - }, - "ostype": 'CentOS 5.3 (64-bit)', - "sleep": 60, - "timeout": 10, - "mode": 'advanced', - # Networking mode: Advanced, Basic - } - class TestDomainCPULimitsUpdateResources(cloudstackTestCase): @classmethod def setUpClass(cls): cls.testClient = super(TestDomainCPULimitsUpdateResources, cls).getClsTestClient() cls.api_client = cls.testClient.getApiClient() + cls.testdata = cls.testClient.getParsedTestDataConfig() - cls.services = Services().services + #cls.services = Services().services # Get Zone, Domain and templates cls.domain = get_domain(cls.api_client) cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) - cls.services["mode"] = cls.zone.networktype - cls.template = get_template( - cls.api_client, - cls.zone.id, - cls.services["ostype"] - ) + cls.testdata["mode"] = cls.zone.networktype - cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.testdata["ostype"] + ) + + # Create service, disk offerings etc + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.testdata["service_offering_multiple_cores"] + ) + cls.testdata["virtual_machine"]["zoneid"] = cls.zone.id cls.service_offering = ServiceOffering.create( cls.api_client, - cls.services["service_offering"] + cls.testdata["service_offering_multiple_cores"] ) cls._cleanup = [cls.service_offering, ] @@ -149,7 +106,7 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): try: vm = VirtualMachine.create( api_client, - self.services["virtual_machine"], + self.testdata["virtual_machine"], templateid=self.template.id, accountid=self.account.name, domainid=self.account.domainid, @@ -171,12 +128,12 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): self.child_domain = Domain.create( self.apiclient, - services=self.services["domain"], + services=self.testdata["domain"], parentdomainid=self.domain.id ) self.child_do_admin = Account.create( self.apiclient, - self.services["account"], + self.testdata["account"], admin=True, domainid=self.child_domain.id ) @@ -194,13 +151,13 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): self.domain = Domain.create( self.apiclient, - services=self.services["domain"], + services=self.testdata["domain"], parentdomainid=self.domain.id ) self.admin = Account.create( self.apiclient, - self.services["account"], + self.testdata["account"], admin=True, domainid=self.domain.id ) @@ -253,7 +210,7 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): ) resource_count = account_list[0].cputotal - expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + expected_resource_count = int(self.service_offering.cpunumber) self.assertEqual(resource_count, expected_resource_count, "Initial resource count should match with the expected resource count") @@ -329,7 +286,7 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): ) resource_count = account_list[0].cputotal - expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + expected_resource_count = int(self.service_offering.cpunumber) self.assertEqual(resource_count, expected_resource_count, "Initial resource count should match with the expected resource count") @@ -390,7 +347,7 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): ) resource_count = account_list[0].cputotal - expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + expected_resource_count = int(self.service_offering.cpunumber) self.assertEqual(resource_count, expected_resource_count, "Initial resource count should with the expected resource count") @@ -421,11 +378,11 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): # 2. Deploy multiple VMs within domain with this service offering # 3. Update Resource count for the domain # 4. CPU usage should list properly - + self.hypervisor = self.testClient.getHypervisorInfo() self.debug("Creating service offering with 4 CPU cores") self.service_offering = ServiceOffering.create( self.apiclient, - self.services["service_offering"] + self.testdata["service_offering_multiple_cores"] ) # Adding to cleanup list after execution self.cleanup.append(self.service_offering) @@ -451,8 +408,17 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): self.createInstance(service_off=self.service_offering, api_client=api_client) self.debug("Deploying instance - CPU capacity is fully utilized") + cmd = self.testdata["service_offering_multiple_cores"] + cmd['cpunumber'] = '20' + + self.so = ServiceOffering.create( + self.api_client, + cmd + ) + self.cleanup.append(self.so) + with self.assertRaises(Exception): - self.createInstance(service_off=self.service_offering, api_client=api_client) + self.createInstance(service_off=self.so, api_client=api_client) account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, @@ -461,7 +427,7 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): ) resource_count = account_list[0].cputotal - expected_resource_count = int(self.services["service_offering"]["cpunumber"]) * 4 #Total 4 VMs + expected_resource_count = int(self.service_offering.cpunumber) * 4 #Total 4 VMs self.assertEqual(resource_count, expected_resource_count, "Initial resource count should be 4") @@ -479,7 +445,7 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): ) resource_count_after_delete = account_list[0].cputotal - expected_resource_count -= int(self.services["service_offering"]["cpunumber"]) + expected_resource_count -= int(self.service_offering.cpunumber) self.assertEqual(resource_count_after_delete, expected_resource_count, "Resource count should match with the expected count") @@ -513,20 +479,31 @@ class TestMultipleChildDomains(cloudstackTestCase): def setUpClass(cls): cls.testClient = super(TestMultipleChildDomains, cls).getClsTestClient() cls.api_client = cls.testClient.getApiClient() + cls.testdata = cls.testClient.getParsedTestDataConfig() - cls.services = Services().services + #cls.services = Services().services # Get Zone, Domain and templates cls.domain = get_domain(cls.api_client) cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) - cls.services["mode"] = cls.zone.networktype + cls.testdata["mode"] = cls.zone.networktype + cls.template = get_template( - cls.api_client, - cls.zone.id, - cls.services["ostype"] - ) + cls.api_client, + cls.zone.id, + cls.testdata["ostype"] + ) - cls.services["virtual_machine"]["zoneid"] = cls.zone.id + # Create service, disk offerings etc + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.testdata["service_offering_multiple_cores"] + ) + cls.testdata["virtual_machine"]["zoneid"] = cls.zone.id + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.testdata["service_offering_multiple_cores"] + ) cls._cleanup = [] return @@ -564,7 +541,7 @@ class TestMultipleChildDomains(cloudstackTestCase): try: vm = VirtualMachine.create( api_client, - self.services["virtual_machine"], + self.testdata["virtual_machine"], templateid=self.template.id, accountid=account.name, domainid=account.domainid, @@ -585,11 +562,11 @@ class TestMultipleChildDomains(cloudstackTestCase): self.debug("Creating a domain under: %s" % self.domain.name) self.parent_domain = Domain.create(self.apiclient, - services=self.services["domain"], + services=self.testdata["domain"], parentdomainid=self.domain.id) self.parentd_admin = Account.create( self.apiclient, - self.services["account"], + self.testdata["account"], admin=True, domainid=self.domain.id ) @@ -603,17 +580,17 @@ class TestMultipleChildDomains(cloudstackTestCase): account=self.parentd_admin.name) self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) self.cdomain_1 = Domain.create(self.apiclient, - services=self.services["domain"], + services=self.testdata["domain"], parentdomainid=self.parent_domain.id) self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) self.cdomain_2 = Domain.create(self.apiclient, - services=self.services["domain"], + services=self.testdata["domain"], parentdomainid=self.parent_domain.id) self.cadmin_1 = Account.create( self.apiclient, - self.services["account"], + self.testdata["account"], admin=True, domainid=self.cdomain_1.id ) @@ -635,7 +612,7 @@ class TestMultipleChildDomains(cloudstackTestCase): self.cadmin_2 = Account.create( self.apiclient, - self.services["account"], + self.testdata["account"], admin=True, domainid=self.cdomain_2.id ) @@ -685,10 +662,10 @@ class TestMultipleChildDomains(cloudstackTestCase): # domain resource updates self.debug("Creating service offering with 2 CPU cores") - self.services["service_offering"]["cpunumber"] = 2 + self.testdata["service_offering"]["cpunumber"] = 2 self.service_offering = ServiceOffering.create( self.apiclient, - self.services["service_offering"] + self.testdata["service_offering"] ) # Adding to cleanup list after execution self.cleanup.append(self.service_offering) diff --git a/test/integration/component/test_organization_states.py b/test/integration/component/test_organization_states.py index ffdd08d2e6f..03b938cfab6 100644 --- a/test/integration/component/test_organization_states.py +++ b/test/integration/component/test_organization_states.py @@ -263,12 +263,12 @@ class TestOrganizationStates(cloudstackTestCase): @attr("disruptive","simulator_only",tags=["advanced"],required_hardware="false") def test_21_disablePod(self): """ - Disable Pod + Disable Pod Validate that listPods() returns the allocationstate as "Disabled" """ self.debug("Pod to be disabled: " + self.zone.id) - podupdResp = self.pod.update(self.apiclient,allocationstate="Disabled") + podupdResp = self.pod.update(self.apiclient,allocationstate="Disabled",id=self.pod.id) self.assertEqual(podupdResp.allocationstate, "Disabled", @@ -363,7 +363,7 @@ class TestOrganizationStates(cloudstackTestCase): """ self.debug("Pod to be enabled: " + self.zone.id) - podupdResp = self.pod.update(self.apiclient,allocationstate="Enabled") + podupdResp = self.pod.update(self.apiclient,allocationstate="Enabled",id=self.pod.id) self.assertEqual(podupdResp.allocationstate, "Enabled", @@ -386,7 +386,7 @@ class TestOrganizationStates(cloudstackTestCase): """ self.debug("Cluster to be disabled: " + self.cluster.id) - clusterupdResp = self.cluster.update(self.apiclient,allocationstate="Disabled") + clusterupdResp = self.cluster.update(self.apiclient,allocationstate="Disabled",id=self.cluster.id) self.assertEqual(clusterupdResp.allocationstate, "Disabled", @@ -481,7 +481,7 @@ class TestOrganizationStates(cloudstackTestCase): """ self.debug("Cluster to be enabled: " + self.cluster.id) - clusterupdResp = self.cluster.update(self.apiclient,allocationstate="Enabled") + clusterupdResp = self.cluster.update(self.apiclient,allocationstate="Enabled",id=self.cluster.id) self.assertEqual(clusterupdResp.allocationstate, "Enabled", @@ -501,17 +501,17 @@ class TestOrganizationStates(cloudstackTestCase): Disable Host Validate that listHosts() returns the allocationstate as "Disabled" """ - self.debug("Host to be disabled: " + self.host.id) + self.debug("Host to be disabled: " + self.host.id) - hostupdResp = Host.update(self.apiclient,id=self.host.id,allocationstate="Disable") + hostupdResp = Host.update(self.apiclient,id=self.host.id,allocationstate="Disable") - self.assertEqual(hostupdResp.resourcestate, + self.assertEqual(hostupdResp.resourcestate, "Disabled", "Disabling Host did not set the alloctionstate to Disabled") - hostlistResp = Host.list(self.apiclient,id=self.host.id) + hostlistResp = Host.list(self.apiclient,id=self.host.id) - self.assertEqual(hostlistResp[0].resourcestate, + self.assertEqual(hostlistResp[0].resourcestate, "Disabled", "Disabling Host did not set the alloctionstate to Disabled") @@ -540,13 +540,16 @@ class TestOrganizationStates(cloudstackTestCase): """ Validate that admin is allowed to deploy VM in a disabled host without passing hostId parameter """ - vm = VirtualMachine.create( + try: + vm = VirtualMachine.create( self.admin_apiclient, {}, zoneid=self.zone.id, serviceofferingid=self.serviceOffering.id, templateid=self.template.id ) + except Exception: + raise self.fail("Failed to deploy VM, this issue was hit: https://issues.apache.org/jira/browse/CLOUDSTACK-7735") self.assertEqual(vm.state, "Running", @@ -564,8 +567,10 @@ class TestOrganizationStates(cloudstackTestCase): self.assertEqual(listResp[0].state, VirtualMachine.STOPPED, "Admin is not able to Stop Vm in a disabled Host! ") - - self.vm_admin.start(self.admin_apiclient) + try: + self.vm_admin.start(self.admin_apiclient) + except Exception: + raise self.fail("Failed to deploy VM, this issue was hit: https://issues.apache.org/jira/browse/CLOUDSTACK-7735") listResp = VirtualMachine.list(self.apiclient,id=self.vm_admin.id) self.assertEqual(listResp[0].state, @@ -601,8 +606,11 @@ class TestOrganizationStates(cloudstackTestCase): self.assertEqual(listResp[0].state, VirtualMachine.STOPPED, "Regular user is not able to Stop Vm in a disabled Host! ") + try: + self.vm_user.start(self.user_apiclient) + except Exception: + raise self.fail("Failed to deploy VM, this issue was hit: https://issues.apache.org/jira/browse/CLOUDSTACK-7735") - self.vm_user.start(self.user_apiclient) listResp = VirtualMachine.list(self.user_apiclient,id=self.vm_user.id) self.assertEqual(listResp[0].state, @@ -629,4 +637,3 @@ class TestOrganizationStates(cloudstackTestCase): "Enabled", "Enabling Host did not set the alloctionstate to Enabled") - diff --git a/test/integration/component/test_project_resources.py b/test/integration/component/test_project_resources.py index 7302476e0f7..607c684f8ce 100644 --- a/test/integration/component/test_project_resources.py +++ b/test/integration/component/test_project_resources.py @@ -1151,13 +1151,13 @@ class TestPublicIpAddress(cloudstackTestCase): ) self.assertEqual( fw_rules[0].startport, - str(self.services["fw_rule"]["startport"]), + self.services["fw_rule"]["startport"], "Check start port of firewall rule" ) self.assertEqual( fw_rules[0].endport, - str(self.services["fw_rule"]["endport"]), + self.services["fw_rule"]["endport"], "Check end port of firewall rule" ) diff --git a/tools/marvin/marvin/config/test_data.py b/tools/marvin/marvin/config/test_data.py index 7d8b6266766..da74cb52daa 100644 --- a/tools/marvin/marvin/config/test_data.py +++ b/tools/marvin/marvin/config/test_data.py @@ -88,6 +88,13 @@ test_data = { "cpuspeed": 256, # in MHz "memory": 256, # In MBs }, + "service_offering_multiple_cores": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 4, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, "service_offerings": { "tiny": { "name": "Tiny Instance",