diff --git a/fuel_health/muranomanager.py b/fuel_health/muranomanager.py index 121f2f09..5f1f04da 100644 --- a/fuel_health/muranomanager.py +++ b/fuel_health/muranomanager.py @@ -401,61 +401,65 @@ class MuranoTest(fuel_health.nmanager.PlatformServicesBaseClass): if inst_name in service['instance']['name']: return service['instance']['floatingIpAddress'] - def get_list_packages(self): + def get_list_packages(self, artifacts=False): try: - packages = self.murano_client.packages.list() + if artifacts: + packages_list = self.murano_art_client.packages.list() + packages = [] + for package in packages_list: + packages.append(package) + else: + packages_list = self.murano_client.packages.list() + packages = list(packages_list) except exceptions.ClientException: self.fail("Can not get list of packages") - packages_list = list(packages) - LOG.debug('Packages List: {0}'.format(packages_list)) - self.assertIsInstance(packages_list, list) - return packages_list + LOG.debug('Packages List: {0}'.format(packages)) + self.assertIsInstance(packages, list) + return packages - def generate_fqn_list(self): + def generate_fqn_list(self, artifacts=False): fqn_list = [] - packages = self.get_list_packages() + packages = self.get_list_packages(artifacts) for package in packages: fqn_list.append(package.to_dict()['fully_qualified_name']) LOG.debug('FQN List: {0}'.format(fqn_list)) return fqn_list - def upload_package(self, package_name, body, app): + def upload_package(self, package_name, body, app, artifacts=False): files = {'%s' % package_name: open(app, 'rb')} - package = self.murano_client.packages.create(body, files) + if artifacts: + package = self.murano_art_client.packages.create(body, files) + else: + package = self.murano_client.packages.create(body, files) self.packages.append(package) return package - def package_exists(self, *packages): - fqn_list = self.generate_fqn_list() + def package_exists(self, artifacts=False, *packages): + fqn_list = self.generate_fqn_list(artifacts) LOG.debug("Response for packages is {0}".format(fqn_list)) for package in packages: if package not in fqn_list: return False return True - def get_package(self, package_id): - resp = requests.get(self.endpoint + 'catalog/packages/{0}'. - format(package_id), headers=self.headers, - verify=False) - self.assertEqual(200, resp.status_code) - return resp.json() + def get_package(self, package_id, artifacts=False): + if artifacts: + package = self.murano_art_client.packages.get(package_id) + else: + package = self.murano_client.packages.get(package_id) + return package - def get_package_by_fqdn(self, package_name): - resp = requests.get(self.endpoint + 'catalog/packages', - headers=self.headers, verify=False) - for package in resp.json()["packages"]: - if package["fully_qualified_name"] == package_name: + def get_package_by_fqdn(self, package_name, artifacts=False): + package_list = self.get_list_packages(artifacts) + for package in package_list: + if package.to_dict()["fully_qualified_name"] == package_name: return package - def delete_package(self, package_id): - resp = requests.delete(self.endpoint + 'catalog/packages/{0}'. - format(package_id), headers=self.headers, - verify=False) - try: - self.assertEqual(200, resp.status_code) - except Exception: - self.assertEqual(404, resp.status_code) - LOG.debug("Package not exists.") + def delete_package(self, package_id, artifacts=False): + if artifacts: + self.murano_art_client.packages.delete(package_id) + else: + self.murano_client.packages.delete(package_id) def get_list_categories(self): resp = requests.get(self.endpoint + 'catalog/packages/categories', diff --git a/fuel_health/nmanager.py b/fuel_health/nmanager.py index d8c1a469..e0be3e55 100644 --- a/fuel_health/nmanager.py +++ b/fuel_health/nmanager.py @@ -58,6 +58,11 @@ try: except Exception: LOG.exception() LOG.warning('Ironic client could not be imported') +try: + import muranoclient.glance.client as art_client +except Exception: + LOG.exception() + LOG.warning('Artifacts client could not be imported') import aodhclient.client import cinderclient.client @@ -116,6 +121,8 @@ class OfficialClientManager(fuel_health.manager.Manager): self.glance_client_v1 = self._get_glance_client(version=1) self.ironic_client = self._get_ironic_client() self.aodh_client = self._get_aodh_client() + self.artifacts_client = self._get_artifacts_client() + self.murano_art_client = self._get_murano_client(artifacts=True) self.client_attr_names = [ 'compute_client', 'identity_client', @@ -129,7 +136,9 @@ class OfficialClientManager(fuel_health.manager.Manager): 'ceilometer_client', 'neutron_client', 'ironic_client', - 'aodh_client' + 'aodh_client', + 'artifacts_client', + 'murano_art_client' ] def _get_compute_client(self, username=None, password=None, @@ -264,7 +273,7 @@ class OfficialClientManager(fuel_health.manager.Manager): token=token, insecure=True) - def _get_murano_client(self): + def _get_murano_client(self, artifacts=False): """This method returns Murano API client """ keystone = self._get_identity_client( @@ -283,10 +292,16 @@ class OfficialClientManager(fuel_health.manager.Manager): 'not found. Murano client cannot be initialized.') return - return muranoclient.v1.client.Client( - endpoint, - token=self.token_id, - insecure=True) + if artifacts: + return muranoclient.v1.client.Client( + endpoint, + token=self.token_id, + insecure=True, artifacts_client=self.artifacts_client) + else: + return muranoclient.v1.client.Client( + endpoint, + token=self.token_id, + insecure=True) def _get_sahara_client(self): sahara_api_version = self.config.sahara.api_version @@ -351,6 +366,21 @@ class OfficialClientManager(fuel_health.manager.Manager): os_auth_token=keystone.auth_token, ironic_url=endpoint, insecure=True) + def _get_artifacts_client(self, version='1'): + keystone = self._get_identity_client() + try: + endpoint = keystone.service_catalog.url_for( + service_type='artifact', + endpoint_type='publicURL') + except keystoneclient.exceptions.EndpointNotFound: + LOG.warning('Can not initialize artifacts client') + return None + return art_client.Client(endpoint=endpoint, + type_name='murano', + type_version=version, + token=keystone.auth_token, + insecure=True) + def _get_aodh_client(self, version='2'): username = self.config.identity.admin_username password = self.config.identity.admin_password diff --git a/fuel_health/tests/sanity/test_sanity_murano.py b/fuel_health/tests/sanity/test_sanity_murano.py index 41111f8a..056f4f36 100644 --- a/fuel_health/tests/sanity/test_sanity_murano.py +++ b/fuel_health/tests/sanity/test_sanity_murano.py @@ -33,7 +33,7 @@ class MuranoSanityTests(muranomanager.MuranoTest): Duration: 10 s. - Deployment tags: Murano + Deployment tags: Murano | murano_plugin """ fail_msg = "Can't create environment. Murano API isn't available. " @@ -55,7 +55,7 @@ class MuranoSanityTests(muranomanager.MuranoTest): Duration: 10 s. - Deployment tags: Murano + Deployment tags: Murano | murano_plugin """ fail_msg = "Can't get list of categories. Murano API isn't available. " self.verify(10, self.get_list_categories, 1, fail_msg, @@ -70,8 +70,23 @@ class MuranoSanityTests(muranomanager.MuranoTest): Duration: 10 s. - Deployment tags: Murano + Deployment tags: Murano | murano_plugin, murano_without_glare """ fail_msg = "Can't get list of packages. Murano API isn't available. " self.verify(10, self.get_list_packages, 1, fail_msg, "getting list of packages") + + def test_get_list_artifacts_packages(self): + """Get list of Murano Artifact applications packages + Target component: Murano + + Scenario: + 1. Send request to get list of artifact packages + + Duration: 10 s. + + Deployment tags: Murano | murano_plugin, murano_use_glare + """ + fail_msg = "Can't get list of packages. Murano API isn't available. " + self.verify(10, self.get_list_packages, 1, fail_msg, + "getting list of packages", artifacts=True) diff --git a/fuel_health/tests/tests_platform/test_murano_linux.py b/fuel_health/tests/tests_platform/test_murano_linux.py index 51b62933..3bfe06b6 100644 --- a/fuel_health/tests/tests_platform/test_murano_linux.py +++ b/fuel_health/tests/tests_platform/test_murano_linux.py @@ -43,7 +43,7 @@ class MuranoDeployLinuxServicesTests(muranomanager.MuranoTest): self.dummy_fqdn = 'io.murano.apps.Simple' - # Flavor with 2 vCPU and 40Gb HDD will allow to sucessfully + # Flavor with 2 vCPU and 40Gb HDD will allow to successfully # deploy all Murano applications. self.flavor_name = rand_name("ostf_test_Murano_flavor") flavor = self.compute_client.flavors.create( @@ -70,7 +70,7 @@ class MuranoDeployLinuxServicesTests(muranomanager.MuranoTest): 10. Send request to delete package. Duration: 1200 s. - Deployment tags: Murano, Heat + Deployment tags: Murano | murano_plugin, murano_without_glare Available since release: 2014.2-6.1 """ @@ -163,6 +163,116 @@ class MuranoDeployLinuxServicesTests(muranomanager.MuranoTest): self.verify(5, self.delete_package, 10, fail_msg, "deleting_package", self.package.id) + def test_deploy_dummy_app_with_glare(self): + """Check application deployment in Murano environment with GLARE + Target component: Murano + + Scenario: + 1. Prepare test app. + 2. Upload test app. + 3. Send request to create environment. + 4. Send request to create session for environment. + 5. Send request to create test service. + 6. Send request to deploy session. + 7. Checking environment status. + 8. Checking deployment status. + 9. Send request to delete environment. + 10. Send request to delete package. + + Duration: 1200 s. + Deployment tags: Murano | murano_plugin, murano_use_glare + Available since release: 2014.2-6.1 + """ + artifacts = True + vms_count = self.get_info_about_available_resources( + self.min_required_ram_mb, 40, 2) + if vms_count < 1: + msg = ('This test requires more hardware resources of your ' + 'OpenStack cluster: your cloud should allow to create ' + 'at least 1 VM with {0} MB of RAM, {1} HDD and {2} vCPUs. ' + 'You need to remove some resources or add compute nodes ' + 'to have an ability to run this OSTF test.' + .format(self.min_required_ram_mb, 40, 2)) + LOG.debug(msg) + self.skipTest(msg) + + if self.package_exists(artifacts, self.dummy_fqdn): + package = self.get_package_by_fqdn(self.dummy_fqdn, artifacts) + self.delete_package(package.to_dict()["id"], artifacts) + + fail_msg = ("Package preparation failed. Please refer to " + "OSTF logs for more information") + zip_path = self.verify(10, self.zip_dir, 1, fail_msg, + 'prepare package', + os.path.dirname(__file__), self.dummy_fqdn) + + fail_msg = ("Package uploading failed. " + "Please refer to Openstack and OSTF logs") + self.package = self.verify(10, self.upload_package, 2, fail_msg, + 'uploading package', 'SimpleApp', + {"categories": ["Web"], "tags": ["tag"]}, + zip_path, artifacts) + + fail_msg = "Can't create environment. Murano API is not available. " + self.environment = self.verify(15, self.create_environment, + 3, fail_msg, 'creating environment', + self.env_name) + + fail_msg = "User can't create session for environment. " + session = self.verify(5, self.create_session, + 4, fail_msg, "session creating", + self.environment.id) + + post_body = { + "instance": { + "flavor": self.flavor_name, + "image": "TestVM", + "assignFloatingIp": True, + "?": { + "type": "io.murano.resources.LinuxMuranoInstance", + "id": str(uuid.uuid4()) + }, + "name": rand_name("testMurano") + }, + "name": rand_name("teMurano"), + "?": { + "_{id}".format(id=uuid.uuid4().hex): { + "name": "SimpleApp" + }, + "type": self.dummy_fqdn, + "id": str(uuid.uuid4()) + } + } + + fail_msg = "User can't create service. " + self.verify(5, self.create_service, + 5, fail_msg, "service creating", + self.environment.id, session.id, post_body) + + fail_msg = "User can't deploy session. " + self.verify(5, self.deploy_session, + 6, fail_msg, + "sending session on deployment", + self.environment.id, session.id) + + fail_msg = "Deployment was not completed correctly. " + self.verify(860, self.deploy_check, + 7, fail_msg, 'deployment is going', + self.environment) + + self.verify(5, self.deployments_status_check, 8, fail_msg, + 'Check deployments status', + self.environment.id) + + fail_msg = "Can't delete environment. " + self.verify(60, self.environment_delete_check, + 9, fail_msg, "deleting environment", + self.environment.id) + + fail_msg = "Can't delete package" + self.verify(5, self.delete_package, 10, fail_msg, "deleting_package", + self.package.id, artifacts) + def test_deploy_apache_service(self): """Check that user can deploy Apache service in Murano environment Target component: Murano @@ -178,7 +288,7 @@ class MuranoDeployLinuxServicesTests(muranomanager.MuranoTest): 8. Send request to delete environment. Duration: 2140 s. - Deployment tags: Murano, Heat + Deployment tags: Murano | murano_plugin, murano_without_artifacts Available since release: 2014.2-6.0 """ @@ -287,7 +397,7 @@ class MuranoDeployLinuxServicesTests(muranomanager.MuranoTest): 11. Send request to delete environment. Duration: 2140 s. - Deployment tags: Murano, Heat + Deployment tags: Murano | murano_plugin, murano_without_artifacts Available since release: 2014.2-6.1 """ diff --git a/fuel_plugin/ostf_adapter/mixins.py b/fuel_plugin/ostf_adapter/mixins.py index be480515..a40d327a 100644 --- a/fuel_plugin/ostf_adapter/mixins.py +++ b/fuel_plugin/ostf_adapter/mixins.py @@ -30,10 +30,8 @@ from sqlalchemy.orm import joinedload from fuel_plugin.ostf_adapter.nose_plugin import nose_utils from fuel_plugin.ostf_adapter.storage import models - LOG = logging.getLogger(__name__) - TEST_REPOSITORY = [] # TODO(ikutukov): remove hardcoded Nailgun API urls here and below NAILGUN_VERSION_API_URL = 'http://{0}:{1}/api/v1/version' @@ -242,6 +240,33 @@ def _get_cluster_attrs(cluster_id, token=None): for comp in comp_names: processor(comp) + # TODO(freerunner): Rework murano part after removal murano from the box + murano_settings = response['editable'].get('murano_settings', None) + # NOTE(freerunner): Murano settings appears only if murano enabled + murano_artifacts = None + if murano_settings: + murano_artifacts = (murano_settings + ['murano_glance_artifacts_plugin']['value']) + detach_murano = response['editable'].get('detach-murano', None) + murano_plugin_enabled = None + if detach_murano: + murano_plugin_enabled = detach_murano['metadata'].get('enabled', None) + if murano_plugin_enabled: + additional_depl_tags.add('murano_plugin') + + # TODO(freerunner): Rework GLARE discover mechanism after + # TODO(freerunner): removal murano from the box + if murano_artifacts: + additional_depl_tags.add('murano_use_glare') + # NOTE(freerunner): Murano plugin will always support only one version + elif detach_murano and murano_plugin_enabled and ( + detach_murano['metadata']['versions'][0] + ['murano_glance_artifacts'].get('value', None)): + additional_depl_tags.add('murano_use_glare') + # NOTE(freerunner): Set this tag only if murano is present + elif murano_plugin_enabled or murano_settings: + additional_depl_tags.add('murano_without_glare') + storage_components = response['editable'].get('storage', dict()) storage_comp = ['volumes_ceph', 'images_ceph', 'ephemeral_ceph', diff --git a/fuel_plugin/testing/tests/base.py b/fuel_plugin/testing/tests/base.py index 3f25e3b4..e91aac39 100644 --- a/fuel_plugin/testing/tests/base.py +++ b/fuel_plugin/testing/tests/base.py @@ -236,7 +236,121 @@ CLUSTERS = { 'common': {} } } - } + }, + 9: { + 'cluster_meta': { + 'release_id': 9, + 'mode': 'multinode' + }, + 'release_data': { + 'operating_system': 'ubuntu', + 'version': '2016.1-9.0' + }, + 'cluster_node': { + }, + 'cluster_attributes': { + 'editable': { + 'detach-murano': { + 'metadata': { + 'enabled': True, + 'versions': [ + { + 'murano_glance_artifacts': { + "value": True + } + } + ] + } + }, + 'additional_components': {}, + 'common': {} + } + } + }, + 10: { + 'cluster_meta': { + 'release_id': 10, + 'mode': 'multinode' + }, + 'release_data': { + 'operating_system': 'ubuntu', + 'version': '2016.1-9.0' + }, + 'cluster_node': { + }, + 'cluster_attributes': { + 'editable': { + 'detach-murano': { + 'metadata': { + 'enabled': True, + 'versions': [ + { + 'murano_glance_artifacts': { + "value": False + } + } + ] + } + }, + 'additional_components': {}, + 'common': {} + } + } + }, + 11: { + 'cluster_meta': { + 'release_id': 11, + 'mode': 'multinode' + }, + 'release_data': { + 'operating_system': 'ubuntu', + 'version': '2016.1-9.0' + }, + 'cluster_node': { + }, + 'cluster_attributes': { + 'editable': { + 'additional_components': { + 'murano': { + 'value': True + } + }, + 'murano_settings': { + 'murano_glance_artifacts_plugin': { + 'value': True + } + }, + 'common': {} + } + } + }, + 12: { + 'cluster_meta': { + 'release_id': 12, + 'mode': 'multinode' + }, + 'release_data': { + 'operating_system': 'ubuntu', + 'version': '2016.1-9.0' + }, + 'cluster_node': { + }, + 'cluster_attributes': { + 'editable': { + 'additional_components': { + 'murano': { + 'value': True + } + }, + 'murano_settings': { + 'murano_glance_artifacts_plugin': { + 'value': False + } + }, + 'common': {} + } + } + }, } diff --git a/fuel_plugin/testing/tests/unit/test_support_utilities.py b/fuel_plugin/testing/tests/unit/test_support_utilities.py index 05c68285..697197d9 100644 --- a/fuel_plugin/testing/tests/unit/test_support_utilities.py +++ b/fuel_plugin/testing/tests/unit/test_support_utilities.py @@ -108,3 +108,99 @@ class TestDeplTagsGetter(base.BaseUnitTest): res = mixins._get_cluster_attrs(expected['cluster_id']) self.assertEqual(res, expected['attrs']) + + +class TestDeplMuranoTags(base.BaseUnitTest): + + def setUp(self): + config.init_config([]) + + self.expected = { + 'attrs': { + 'deployment_tags': set( + ['multinode', 'ubuntu', 'additional_components', + 'nova_network', 'public_on_all_nodes', + 'enable_without_ceph', 'computes_without_dpdk']), + 'release_version': '2016.1-9.0' + } + } + + def test_get_murano_plugin_tags_with_artifacts(self): + expected = self.expected + expected['cluster_id'] = 9 + expected['attrs']['deployment_tags'].add('murano_plugin') + expected['attrs']['deployment_tags'].add('murano_use_glare') + + with requests_mock.Mocker() as m: + cluster = base.CLUSTERS[expected['cluster_id']] + m.register_uri('GET', '/api/clusters/9', + json=cluster['cluster_meta']) + m.register_uri('GET', '/api/clusters/9/attributes', + json=cluster['cluster_attributes']) + m.register_uri('GET', '/api/releases/9', + json=cluster['release_data']) + m.register_uri('GET', '/api/nodes?cluster_id=9', + json=cluster['cluster_node']) + res = mixins._get_cluster_attrs(expected['cluster_id']) + + self.assertEqual(res, expected['attrs']) + + def test_get_murano_plugin_tags_without_artifacts(self): + expected = self.expected + expected['cluster_id'] = 10 + expected['attrs']['deployment_tags'].add('murano_plugin') + expected['attrs']['deployment_tags'].add('murano_without_glare') + + with requests_mock.Mocker() as m: + cluster = base.CLUSTERS[expected['cluster_id']] + m.register_uri('GET', '/api/clusters/10', + json=cluster['cluster_meta']) + m.register_uri('GET', '/api/clusters/10/attributes', + json=cluster['cluster_attributes']) + m.register_uri('GET', '/api/releases/10', + json=cluster['release_data']) + m.register_uri('GET', '/api/nodes?cluster_id=10', + json=cluster['cluster_node']) + res = mixins._get_cluster_attrs(expected['cluster_id']) + + self.assertEqual(res, expected['attrs']) + + def test_get_murano_tags_with_artifacts(self): + expected = self.expected + expected['cluster_id'] = 11 + expected['attrs']['deployment_tags'].add('murano') + expected['attrs']['deployment_tags'].add('murano_use_glare') + + with requests_mock.Mocker() as m: + cluster = base.CLUSTERS[expected['cluster_id']] + m.register_uri('GET', '/api/clusters/11', + json=cluster['cluster_meta']) + m.register_uri('GET', '/api/clusters/11/attributes', + json=cluster['cluster_attributes']) + m.register_uri('GET', '/api/releases/11', + json=cluster['release_data']) + m.register_uri('GET', '/api/nodes?cluster_id=11', + json=cluster['cluster_node']) + res = mixins._get_cluster_attrs(expected['cluster_id']) + + self.assertEqual(res, expected['attrs']) + + def test_get_murano_tags_without_artifacts(self): + expected = self.expected + expected['cluster_id'] = 12 + expected['attrs']['deployment_tags'].add('murano') + expected['attrs']['deployment_tags'].add('murano_without_glare') + + with requests_mock.Mocker() as m: + cluster = base.CLUSTERS[expected['cluster_id']] + m.register_uri('GET', '/api/clusters/12', + json=cluster['cluster_meta']) + m.register_uri('GET', '/api/clusters/12/attributes', + json=cluster['cluster_attributes']) + m.register_uri('GET', '/api/releases/12', + json=cluster['release_data']) + m.register_uri('GET', '/api/nodes?cluster_id=12', + json=cluster['cluster_node']) + res = mixins._get_cluster_attrs(expected['cluster_id']) + + self.assertEqual(res, expected['attrs'])