diff --git a/murano_tempest_tests/config.py b/murano_tempest_tests/config.py index fe0d2e239..a88e2af15 100644 --- a/murano_tempest_tests/config.py +++ b/murano_tempest_tests/config.py @@ -49,6 +49,10 @@ ApplicationCatalogGroup = [ "If no such region is found in the service catalog, " "the first found one is used."), + cfg.StrOpt("linux_image", + default="debian-8-m-agent.qcow2", + help="Image for linux services"), + cfg.StrOpt("catalog_type", default="application-catalog", help="Catalog type of Application Catalog."), @@ -70,7 +74,14 @@ ApplicationCatalogGroup = [ cfg.BoolOpt("glare_backend", default=False, help="Tells tempest about murano glare backend " - "configuration.") + "configuration."), + cfg.BoolOpt("cinder_volume_tests", + default=False, + help="Whether or not cinder volumes attachment tests " + "are expected to run"), + cfg.BoolOpt("deployment_tests", + default=False, + help="Whether or not deployment tests are expected to run") ] ServiceBrokerGroup = [ diff --git a/murano_tempest_tests/extras/io.murano.apps.test.ApacheHttpServerCustom/Classes/ApacheHttpServer.yaml b/murano_tempest_tests/extras/io.murano.apps.test.ApacheHttpServerCustom/Classes/ApacheHttpServer.yaml new file mode 100644 index 000000000..cd13ff970 --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.ApacheHttpServerCustom/Classes/ApacheHttpServer.yaml @@ -0,0 +1,81 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +Namespaces: + =: io.murano.apps.test + std: io.murano + res: io.murano.resources + sys: io.murano.system + conf: io.murano.configuration + + +Name: ApacheHttpServerCustom + +Extends: std:Application + +Properties: + name: + Contract: $.string().notNull() + + instance: + Contract: $.class(res:Instance).notNull() + + userName: + Contract: $.string() + +Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $._environment.reporter.report($this, 'Creating VM for Apache Server.') + - $securityGroupIngress: + - ToPort: 80 + FromPort: 80 + IpProtocol: tcp + External: true + - ToPort: 443 + FromPort: 443 + IpProtocol: tcp + External: true + - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) + - $.instance.deploy() + - $._environment.reporter.report($this, 'Instance is created. Deploying Apache') + + - $resources: new(sys:Resources) + - $linux: new(conf:Linux) + + - $linux.runCommand($.instance.agent, 'apt-get -y install apache2') + - $linux.runCommand($.instance.agent, 'iptables -I INPUT 1 -p tcp --dport 443 -j ACCEPT') + - $linux.runCommand($.instance.agent, 'iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT') + - $._environment.reporter.report($this, 'Apache is installed.') + + - If: $.userName != '' + Then: + - $linux.runCommand($.instance.agent, 'service apache2 stop') + - $fileReplacements: + "%USER_NAME%": $.userName + - $fileContent: $resources.string('index.html').replace($fileReplacements) + - $linux.putFile($.instance.agent, $fileContent, '/var/www/html/index.html') + - $linux.runCommand($.instance.agent, 'service apache2 start') + + - If: $.instance.assignFloatingIp + Then: + - $host: $.instance.floatingIpAddress + Else: + - $host: $.instance.ipAddresses[0] + - $._environment.reporter.report($this, format('Apache is available at http://{0}', $host)) + - $.setAttr(deployed, true) diff --git a/murano_tempest_tests/extras/io.murano.apps.test.ApacheHttpServerCustom/Resources/index.html b/murano_tempest_tests/extras/io.murano.apps.test.ApacheHttpServerCustom/Resources/index.html new file mode 100644 index 000000000..68436975b --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.ApacheHttpServerCustom/Resources/index.html @@ -0,0 +1,8 @@ + + + + Hello World + +Hello world. This is my first web page. My name is %USER_NAME%. + + diff --git a/murano_tempest_tests/extras/io.murano.apps.test.ApacheHttpServerCustom/manifest.yaml b/murano_tempest_tests/extras/io.murano.apps.test.ApacheHttpServerCustom/manifest.yaml new file mode 100644 index 000000000..b0ab7c3d7 --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.ApacheHttpServerCustom/manifest.yaml @@ -0,0 +1,28 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +Format: 1.0 +Type: Application +FullName: io.murano.test.apache.ApacheHttpServerCustom +Name: Apache HTTP Server Custom +Description: | + The Apache HTTP Server Project is an effort to develop and maintain an + open-source HTTP server for modern operating systems including UNIX and + Windows NT. The goal of this project is to provide a secure, efficient and + extensible server that provides HTTP services in sync with the current HTTP + standards. + Apache httpd has been the most popular web server on the Internet since + April 1996, and celebrated its 17th birthday as a project this February. +Author: 'Mirantis, Inc' +Tags: [HTTP, Server, WebServer, HTML, Apache] +Classes: + io.murano.apps.test.ApacheHttpServerCustom: ApacheHttpServer.yaml diff --git a/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/Classes/Lighttpd.yaml b/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/Classes/Lighttpd.yaml new file mode 100644 index 000000000..c719f1204 --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/Classes/Lighttpd.yaml @@ -0,0 +1,55 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +Namespaces: + =: io.murano.apps.test + std: io.murano + sys: io.murano.system + +Name: Lighttpd + +Extends: std:Application + +Properties: + updater: + Contract: $.class(UpdateExecutor).notNull() + +Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $securityGroupIngress: + - ToPort: 80 + FromPort: 80 + IpProtocol: tcp + External: true + - ToPort: 443 + FromPort: 443 + IpProtocol: tcp + External: true + - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) + - $._environment.reporter.report($this, 'Ensuring Updater is deployed.') + - $.updater.deploy() + - $resources: new(sys:Resources) + - $template: $resources.yaml('DeployLighttpd.template') + - $.updater.instance.agent.call($template, $resources) + + - If: $.updater.instance.assignFloatingIp + Then: + - $address: $.updater.instance.floatingIpAddress + - $._environment.reporter.report($this, format('Running at http://{0}', $address)) + - $.setAttr(deployed, true) diff --git a/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/Resources/DeployLighttpd.template b/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/Resources/DeployLighttpd.template new file mode 100644 index 000000000..7eba0d5a6 --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/Resources/DeployLighttpd.template @@ -0,0 +1,27 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +FormatVersion: 2.0.0 +Version: 1.0.0 +Name: Deploy Lighttpd + +Body: | + deploy() + +Scripts: + deploy: + Type: Application + Version: 1.0.0 + EntryPoint: deployLighttpd.sh + Options: + captureStdout: true + captureStderr: true diff --git a/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/Resources/scripts/deployLighttpd.sh b/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/Resources/scripts/deployLighttpd.sh new file mode 100644 index 000000000..09dcd46b6 --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/Resources/scripts/deployLighttpd.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +sudo apt-get -y -q install lighttpd diff --git a/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/manifest.yaml b/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/manifest.yaml new file mode 100644 index 000000000..5bdb9029f --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.Lighttpd/manifest.yaml @@ -0,0 +1,24 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +Format: 1.0 +Type: Application +FullName: io.murano.apps.test.Lighttpd +Name: Lighttpd +Description: | + Lighttpd... :) +Author: 'Mirantis, Inc' +Tags: [Web] +Classes: + io.murano.apps.test.Lighttpd: Lighttpd.yaml +Require: + io.murano.apps.test.UpdateExecutor: diff --git a/murano_tempest_tests/extras/io.murano.apps.test.UpdateExecutor/Classes/UpdateExecutor.yaml b/murano_tempest_tests/extras/io.murano.apps.test.UpdateExecutor/Classes/UpdateExecutor.yaml new file mode 100644 index 000000000..fd47e8854 --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.UpdateExecutor/Classes/UpdateExecutor.yaml @@ -0,0 +1,47 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +Namespaces: + =: io.murano.apps.test + std: io.murano + res: io.murano.resources + sys: io.murano.system + conf: io.murano.configuration + + +Name: UpdateExecutor + +Extends: std:Application + +Properties: + name: + Contract: $.string().notNull() + + instance: + Contract: $.class(res:Instance).notNull() + +Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $._environment.reporter.report($this, 'Creating VM.') + - $.instance.deploy() + - $._environment.reporter.report($this, 'Starting packages updating.') + - $file: sys:Resources.string('scripts/update.sh') + - conf:Linux.runCommand($.instance.agent, $file) + - $._environment.reporter.report($this, 'Update completed.') + - $.setAttr(deployed, true) diff --git a/murano_tempest_tests/extras/io.murano.apps.test.UpdateExecutor/Resources/scripts/update.sh b/murano_tempest_tests/extras/io.murano.apps.test.UpdateExecutor/Resources/scripts/update.sh new file mode 100644 index 000000000..28cddd559 --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.UpdateExecutor/Resources/scripts/update.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +sudo apt-get update diff --git a/murano_tempest_tests/extras/io.murano.apps.test.UpdateExecutor/manifest.yaml b/murano_tempest_tests/extras/io.murano.apps.test.UpdateExecutor/manifest.yaml new file mode 100644 index 000000000..49137b479 --- /dev/null +++ b/murano_tempest_tests/extras/io.murano.apps.test.UpdateExecutor/manifest.yaml @@ -0,0 +1,22 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +Format: 1.0 +Type: Application +FullName: io.murano.apps.test.UpdateExecutor +Name: Update Executor +Description: | + Test application, which updates packages on VM +Author: 'Mirantis, Inc' +Tags: [application] +Classes: + io.murano.apps.test.UpdateExecutor: UpdateExecutor.yaml diff --git a/murano_tempest_tests/tests/scenario/__init__.py b/murano_tempest_tests/tests/scenario/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/murano_tempest_tests/tests/scenario/application_catalog/__init__.py b/murano_tempest_tests/tests/scenario/application_catalog/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/murano_tempest_tests/tests/scenario/application_catalog/base.py b/murano_tempest_tests/tests/scenario/application_catalog/base.py new file mode 100644 index 000000000..be10e235a --- /dev/null +++ b/murano_tempest_tests/tests/scenario/application_catalog/base.py @@ -0,0 +1,335 @@ +# Copyright (c) 2016 Mirantis, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import requests +import socket +import time + +from tempest.clients import Manager as services_manager +from tempest.common import credentials_factory as common_creds +from tempest.common import dynamic_creds +from tempest.common import waiters +from tempest import config +from tempest.lib import base +from tempest.lib import exceptions + +from murano_tempest_tests import clients +from murano_tempest_tests import utils + +CONF = config.CONF + + +class BaseApplicationCatalogScenarioTest(base.BaseTestCase): + """Base test class for Murano Application Catalog Scenario tests.""" + + @classmethod + def setUpClass(cls): + super(BaseApplicationCatalogScenarioTest, cls).setUpClass() + cls.resource_setup() + + @classmethod + def tearDownClass(cls): + cls.resource_cleanup() + super(BaseApplicationCatalogScenarioTest, cls).tearDownClass() + + @classmethod + def get_client_with_isolated_creds(cls, type_of_creds="admin"): + + creds = cls.get_configured_isolated_creds(type_of_creds=type_of_creds) + + os = clients.Manager(credentials=creds) + client = os.application_catalog_client + + return client + + @classmethod + def get_configured_isolated_creds(cls, type_of_creds='admin'): + identity_version = CONF.identity.auth_version + if identity_version == 'v3': + cls.admin_role = CONF.identity.admin_role + else: + cls.admin_role = 'admin' + if not hasattr(cls, 'dynamic_cred'): + cls.dynamic_cred = dynamic_creds.DynamicCredentialProvider( + identity_version=CONF.identity.auth_version, + name=cls.__name__, admin_role=cls.admin_role, + admin_creds=common_creds.get_configured_admin_credentials( + 'identity_admin')) + if type_of_creds == 'primary': + creds = cls.dynamic_cred.get_primary_creds() + elif type_of_creds == 'admin': + creds = cls.dynamic_cred.get_admin_creds() + elif type_of_creds == 'alt': + creds = cls.dynamic_cred.get_alt_creds() + else: + creds = cls.dynamic_cred.get_credentials(type_of_creds) + cls.dynamic_cred.type_of_creds = type_of_creds + + return creds.credentials + + @classmethod + def verify_nonempty(cls, *args): + if not all(args): + msg = "Missing API credentials in configuration." + raise cls.skipException(msg) + + @classmethod + def resource_setup(cls): + if not CONF.service_available.murano: + skip_msg = "Murano is disabled" + raise cls.skipException(skip_msg) + if not hasattr(cls, "os"): + creds = cls.get_configured_isolated_creds(type_of_creds='primary') + cls.os = clients.Manager(credentials=creds) + cls.services_manager = services_manager(creds) + cls.linux_image = CONF.application_catalog.linux_image + cls.application_catalog_client = cls.os.application_catalog_client + cls.artifacts_client = cls.os.artifacts_client + cls.servers_client = cls.services_manager.servers_client + cls.orchestration_client = cls.services_manager.orchestration_client + cls.snapshots_client = cls.services_manager.snapshots_client + cls.volumes_client = cls.services_manager.volumes_client + cls.backups_client = cls.services_manager.backups_client + + @classmethod + def resource_cleanup(cls): + cls.clear_isolated_creds() + + @classmethod + def clear_isolated_creds(cls): + if hasattr(cls, "dynamic_cred"): + cls.dynamic_cred.clear_creds() + + def environment_delete(self, environment_id, timeout=180): + self.application_catalog_client.delete_environment(environment_id) + + start_time = time.time() + while time.time() - start_time > timeout: + try: + self.application_catalog_client.get_environment(environment_id) + except exceptions.NotFound: + return + + @classmethod + def purge_stacks(cls): + stacks = cls.orchestration_client.list_stacks()['stacks'] + for stack in stacks: + cls.orchestration_client.delete_stack(stack['id']) + cls.orchestration_client.wait_for_stack_status(stack['id'], + 'DELETE_COMPLETE') + + def get_service(self, environment, session, service_name): + for service in self.application_catalog_client.get_services_list( + environment, session): + if service['name'] == service_name: + return service + + def get_stack_id(self, environment_id): + stacks = self.orchestration_client.list_stacks()['stacks'] + for stack in stacks: + if environment_id in self.orchestration_client.show_stack( + stack['id'])['stack']['description']: + return stack['id'] + + def get_stack_template(self, stack): + return self.orchestration_client.show_template(stack) + + def get_instance_id(self, name): + instance_list = self.servers_client.list_servers()['servers'] + for instance in instance_list: + if name in instance['name']: + return instance['id'] + + def apache_cinder( + self, attributes=None, userName=None, flavor='m1.medium'): + post_body = { + "instance": { + "flavor": flavor, + "image": self.linux_image, + "assignFloatingIp": True, + "availabilityZone": "nova", + "volumes": attributes, + "?": { + "type": "io.murano.resources.LinuxMuranoInstance", + "id": utils.generate_uuid() + }, + "name": utils.generate_name("testMurano") + }, + "name": utils.generate_name("ApacheHTTPServer"), + "userName": userName, + "?": { + "_{id}".format(id=utils.generate_uuid()): { + "name": "ApacheHTTPServer" + }, + "type": "io.murano.apps.test.ApacheHttpServerCustom", + "id": utils.generate_uuid() + } + } + return post_body + + def update_executor(self, flavor='m1.medium'): + post_body = { + "instance": { + "flavor": flavor, + "image": self.linux_image, + "assignFloatingIp": True, + "?": { + "type": "io.murano.resources.LinuxMuranoInstance", + "id": utils.generate_uuid() + }, + "name": utils.generate_name('testMurano') + }, + "name": utils.generate_name('dummy'), + "?": { + "type": "io.murano.apps.test.UpdateExecutor", + "id": utils.generate_uuid() + } + } + return post_body + + def deploy_environment(self, environment, session): + self.application_catalog_client.deploy_session(environment['id'], + session['id']) + return self.wait_for_environment_deploy(environment) + + def wait_for_environment_deploy(self, environment): + start_time = time.time() + status = self.application_catalog_client.\ + get_environment(environment['id'])['status'] + while status != 'ready': + status = self.application_catalog_client.\ + get_environment(environment['id'])['status'] + if time.time() - start_time > 1800: + time.sleep(60) + self.fail( + 'Environment deployment is not finished in 1200 seconds') + elif status == 'deploy failure': + time.sleep(60) + self.fail('Environment has incorrect status {0}'. + format(status)) + time.sleep(5) + return self.application_catalog_client.\ + get_environment(environment['id']) + + def status_check(self, environment_id, configurations): + for configuration in configurations: + inst_name = configuration[0] + ports = configuration[1:] + ip = self.get_ip_by_instance_name(environment_id, inst_name) + if ip and ports: + for port in ports: + self.check_port_access(ip, port) + else: + self.fail('Instance does not have floating IP') + + def check_path(self, environment_id, path, inst_name=None): + environment = self.application_catalog_client.\ + get_environment(environment_id) + if inst_name: + ip = self.get_ip_by_instance_name(environment_id, inst_name) + else: + ip = environment.services[0]['instance']['floatingIpAddress'] + resp = requests.get('http://{0}/{1}'.format(ip, path)) + if resp.status_code == 200: + return resp + else: + self.fail("Service path unavailable") + + def get_ip_by_instance_name(self, environment_id, inst_name): + for service in self.application_catalog_client.\ + get_services_list(environment_id): + if inst_name in service['instance']['name']: + return service['instance']['floatingIpAddress'] + + def check_port_access(self, ip, port): + result = 1 + start_time = time.time() + while time.time() - start_time < 600: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + result = sock.connect_ex((str(ip), port)) + sock.close() + if result == 0: + break + time.sleep(5) + self.assertEqual(0, result, '%s port is closed on instance' % port) + + @classmethod + def create_volume(cls): + volume = cls.volumes_client.create_volume()['volume'] + waiters.wait_for_volume_status(cls.volumes_client, + volume['id'], 'available') + return volume['id'] + + @classmethod + def delete_volume(cls, volume_id): + cls.volumes_client.delete_volume(volume_id) + is_volume_deleted = False + while not is_volume_deleted: + is_volume_deleted = cls.volumes_client.\ + is_resource_deleted(volume_id) + time.sleep(1) + + def create_snapshot(self, volume_id): + snapshot = self.snapshots_client.\ + create_snapshot(volume_id=volume_id)['snapshot'] + waiters.wait_for_snapshot_status(self.snapshots_client, + snapshot['id'], 'available') + return snapshot['id'] + + def delete_snapshot(self, snapshot_id): + self.snapshots_client.delete_snapshot(snapshot_id) + is_snapshot_deleted = False + while not is_snapshot_deleted: + is_snapshot_deleted = self.snapshots_client.\ + is_resource_deleted(snapshot_id) + time.sleep(1) + + def create_backup(self, volume_id): + backup = self.backups_client.create_backup( + volume_id=volume_id, + force=True)['backup'] + self.backups_client.wait_for_backup_status(backup['id'], 'available') + return backup['id'] + + def delete_backup(self, backup_id): + self.backups_client.delete_backup(backup_id) + return self.backups_client.wait_for_backup_deletion(backup_id) + + def get_volume(self, environment_id): + stack = self.get_stack_id(environment_id) + stack_outputs = self.orchestration_client.\ + show_stack(stack)['stack']['outputs'] + for output in stack_outputs: + if 'vol' in output['output_key']: + volume_id = output['output_value'] + return self.volumes_client.show_volume(volume_id)['volume'] + + def check_volume_attached(self, name, volume_id): + instance_id = self.get_instance_id(name) + attached_volumes = self.servers_client.\ + list_volume_attachments(instance_id)['volumeAttachments'] + assert attached_volumes[0]['id'] == volume_id + + +class BaseApplicationCatalogScenarioIsolatedAdminTest( + BaseApplicationCatalogScenarioTest): + + @classmethod + def resource_setup(cls): + creds = cls.get_configured_isolated_creds(type_of_creds='admin') + cls.os = clients.Manager(credentials=creds) + cls.services_manager = services_manager(creds) + super(BaseApplicationCatalogScenarioIsolatedAdminTest, cls).\ + resource_setup() diff --git a/murano_tempest_tests/utils.py b/murano_tempest_tests/utils.py index c29dee364..837e53384 100644 --- a/murano_tempest_tests/utils.py +++ b/murano_tempest_tests/utils.py @@ -34,7 +34,8 @@ MANIFEST = {'Format': 'MuranoPL/1.0', def compose_package(app_name, manifest, package_dir, - require=None, archive_dir=None, add_class_name=False): + require=None, archive_dir=None, add_class_name=False, + manifest_required=True): """Composes a murano package Composes package `app_name` with `manifest` file as a template for the @@ -43,15 +44,16 @@ def compose_package(app_name, manifest, package_dir, Puts the resulting .zip file into `acrhive_dir` if present or in the `package_dir`. """ - with open(manifest, 'w') as f: - fqn = 'io.murano.apps.' + app_name - mfest_copy = MANIFEST.copy() - mfest_copy['FullName'] = fqn - mfest_copy['Name'] = app_name - mfest_copy['Classes'] = {fqn: 'mock_muranopl.yaml'} - if require: - mfest_copy['Require'] = require - f.write(yaml.dump(mfest_copy, default_flow_style=False)) + if manifest_required: + with open(manifest, 'w') as f: + fqn = 'io.murano.apps.' + app_name + mfest_copy = MANIFEST.copy() + mfest_copy['FullName'] = fqn + mfest_copy['Name'] = app_name + mfest_copy['Classes'] = {fqn: 'mock_muranopl.yaml'} + if require: + mfest_copy['Require'] = require + f.write(yaml.dump(mfest_copy, default_flow_style=False)) if add_class_name: class_file = os.path.join(package_dir, 'Classes', 'mock_muranopl.yaml') @@ -82,7 +84,8 @@ def compose_package(app_name, manifest, package_dir, return archive_path, name -def prepare_package(name, require=None, add_class_name=False): +def prepare_package(name, require=None, add_class_name=False, + app='MockApp', manifest_required=True): """Prepare package. :param name: Package name to compose @@ -90,13 +93,13 @@ def prepare_package(name, require=None, add_class_name=False): :param add_class_name: Option to write class name to class file :return: Path to archive, directory with archive, filename of archive """ - app_dir = acquire_package_directory() - target_arc_path = app_dir.rsplit('MockApp', 1)[0] + app_dir = acquire_package_directory(app=app) + target_arc_path = app_dir.rsplit(app, 1)[0] arc_path, filename = compose_package( name, os.path.join(app_dir, 'manifest.yaml'), app_dir, require=require, archive_dir=target_arc_path, - add_class_name=add_class_name) + add_class_name=add_class_name, manifest_required=manifest_required) return arc_path, target_arc_path, filename @@ -111,7 +114,7 @@ def generate_name(prefix): return '{0}_{1}'.format(prefix, suffix) -def acquire_package_directory(): +def acquire_package_directory(app='MockApp'): """Obtain absolutely directory with package files. Should be called inside tests dir. @@ -119,7 +122,7 @@ def acquire_package_directory(): """ top_plugin_dir = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) - expected_package_dir = '/extras/MockApp' + expected_package_dir = '/extras/' + app app_dir = top_plugin_dir + expected_package_dir return app_dir