Introduce refactored tests

- Move all functions excluding specific to common/utils.py
- Add requests to test-requirements.txt
- Modify config.py to give more flexibility to tests

Change-Id: I1d86d500da8aab67d90da7314ccb6c376c8f59d4
Partially-Implements: blueprint new-ci-tests-preparation
This commit is contained in:
Victor Ryzhenkin 2015-07-06 22:14:05 +03:00
parent d3717666db
commit 2d3dd9c6da
9 changed files with 637 additions and 318 deletions

View File

@ -13,8 +13,12 @@
# under the License.
import contextlib
import json
import logging
import os
import random
import socket
import telnetlib
import time
import zipfile
@ -23,6 +27,7 @@ from keystoneclient import exceptions as ks_exceptions
from keystoneclient.v2_0 import client as ksclient
from muranoclient import client as mclient
import muranoclient.common.exceptions as exceptions
import yaml
from murano.services import states
import murano.tests.functional.engine.config as cfg
@ -81,6 +86,7 @@ class ZipUtilsMixin(object):
class DeployTestMixin(ZipUtilsMixin):
cfg.load_config()
# -----------------------------Clients methods---------------------------------
@staticmethod
@memoize
def keystone_client():
@ -98,17 +104,6 @@ class DeployTestMixin(ZipUtilsMixin):
endpoint=heat_url,
token=cls.keystone_client().auth_token)
@classmethod
def get_murano_url(cls):
try:
url = cls.keystone_client().service_catalog.url_for(
service_type='application_catalog', endpoint_type='publicURL')
except ks_exceptions.EndpointNotFound:
url = CONF.murano.murano_url
LOG.warning("Murano endpoint not found in Keystone. Using CONF.")
return url if 'v1' not in url else "/".join(
url.split('/')[:url.split('/').index('v1')])
@classmethod
@memoize
def murano_client(cls):
@ -117,36 +112,16 @@ class DeployTestMixin(ZipUtilsMixin):
endpoint=murano_url,
token=cls.keystone_client().auth_token)
@classmethod
def init_list(cls, list_name):
if not hasattr(cls, list_name):
setattr(cls, list_name, [])
@classmethod
def upload_package(cls, package_name, body, app):
files = {'%s' % package_name: open(app, 'rb')}
package = cls.murano_client().packages.create(body, files)
cls.init_list("_packages")
cls._packages.append(package)
return package
@classmethod
def environment_delete(cls, environment_id, timeout=180):
cls.murano_client().environments.delete(environment_id)
start_time = time.time()
while time.time() - start_time < timeout:
try:
cls.murano_client().environments.get(environment_id)
except exceptions.HTTPNotFound:
return
err_msg = ('Environment {0} was not deleted in {1} seconds'.
format(environment_id, timeout))
LOG.error(err_msg)
raise RuntimeError(err_msg)
# --------------------------Specific test methods------------------------------
@classmethod
def deploy_apps(cls, name, *apps):
"""Create and deploy environment.
:param name: Murano environment name
:param apps: App(s), described in JSON format
:return: Murano environment
"""
environment = cls.murano_client().environments.create({'name': name})
cls.init_list("_environments")
cls._environments.append(environment)
@ -162,6 +137,12 @@ class DeployTestMixin(ZipUtilsMixin):
@staticmethod
def wait_for_final_status(environment, timeout=300):
"""Function for wait final status of environment.
:param environment: Murano environment.
:param timeout: Timeout for waiting environment to get any status
excluding DEPLOYING state
"""
start_time = time.time()
status = environment.manager.get(environment.id).status
while states.SessionState.DEPLOYING == status:
@ -177,18 +158,223 @@ class DeployTestMixin(ZipUtilsMixin):
dep[0].id)
return status, ", ".join([r.text for r in reports])
# -----------------------------Reports methods---------------------------------
@classmethod
def purge_environments(cls):
cls.init_list("_environments")
def get_last_deployment(cls, environment):
"""Gets last deployment of Murano environment.
:param environment: Murano environment
:return:
"""
deployments = cls.murano_client().deployments.list(environment.id)
return deployments[0]
@classmethod
def get_deployment_report(cls, environment, deployment):
"""Gets reports for environment with specific deployment.
:param environment: Murano environment.
:param deployment: Murano deployment for certain environment
:return:
"""
history = ''
report = cls.murano_client().deployments.reports(
environment.id, deployment.id)
for status in report:
history += '\t{0} - {1}\n'.format(status.created, status.text)
return history
@classmethod
def _log_report(cls, environment):
"""Used for logging reports on failures.
:param environment: Murano environment.
"""
deployment = cls.get_last_deployment(environment)
try:
for env in cls._environments:
with ignored(Exception):
cls.environment_delete(env.id)
finally:
cls._environments = []
details = deployment.result['result']['details']
LOG.warning('Details:\n {0}'.format(details))
except Exception as e:
LOG.error(e)
report = cls.get_deployment_report(environment, deployment)
LOG.debug('Report:\n {0}\n'.format(report))
# -----------------------------Service methods---------------------------------
@classmethod
def add_service(cls, environment, data, session, to_dict=False):
"""This function adds a specific service to environment.
:param environment: Murano environment
:param data: JSON with specific servive to add into
:param session: Session that is open for environment
:param to_dict: If True - returns a JSON object with service
If False - returns a specific class <Service>
"""
LOG.debug('Added service:\n {0}'.format(data))
service = cls.murano_client().services.post(environment.id,
path='/', data=data,
session_id=session.id)
if to_dict:
return cls._convert_service(service)
else:
return service
@classmethod
def get_service_as_json(cls, environment, service_name):
"""Get a service with specific name from environment in JSON format.
:param environment: Murano environment
:param service_name: Service name
:return:
"""
for service in cls.murano_client().services.list(environment.id):
if service.name == service_name:
return cls._convert_service(service)
@classmethod
def _convert_service(cls, service):
"""Converts a <Service> to JSON object.
:param service: <Service> object
:return: JSON object
"""
component = service.to_dict()
component = json.dumps(component)
return yaml.load(component)
# -----------------------------Packages methods--------------------------------
@classmethod
def upload_package(cls, package_name, body, app):
"""Uploads a .zip package with parameters to Murano.
:param package_name: Package name in Murano repository
:param body: Categories, tags, etc.
e.g. {
"categories": ["Application Servers"],
"tags": ["tag"]
}
:param app: Correct .zip archive with the application
:return: Package
"""
files = {'{0}'.format(package_name): open(app, 'rb')}
package = cls.murano_client().packages.create(body, files)
cls.init_list("_packages")
cls._packages.append(package)
return package
# ------------------------------Common methods---------------------------------
@classmethod
def rand_name(cls, name='murano'):
"""Generates random string.
:param name: Basic name
:return:
"""
return name + str(random.randint(1, 0x7fffffff))
@classmethod
def init_list(cls, list_name):
if not hasattr(cls, list_name):
setattr(cls, list_name, [])
@classmethod
def get_murano_url(cls):
try:
url = cls.keystone_client().service_catalog.url_for(
service_type='application_catalog', endpoint_type='publicURL')
except ks_exceptions.EndpointNotFound:
url = CONF.murano.murano_url
LOG.warning("Murano endpoint not found in Keystone. Using CONF.")
return url if 'v1' not in url else "/".join(
url.split('/')[:url.split('/').index('v1')])
@classmethod
def verify_connection(cls, ip, port):
"""Try to connect to specific ip:port with telnet.
:param ip: Ip that you want to check
:param port: Port that you want to check
:return: :raise RuntimeError:
"""
tn = telnetlib.Telnet(ip, port)
tn.write('GET / HTTP/1.0\n\n')
try:
buf = tn.read_all()
LOG.debug('Data:\n {0}'.format(buf))
if len(buf) != 0:
tn.sock.sendall(telnetlib.IAC + telnetlib.NOP)
return
else:
raise RuntimeError('Resource at {0}:{1} not exist'.
format(ip, port))
except socket.error as e:
LOG.debug('Socket Error: {0}'.format(e))
@classmethod
def get_ip_by_appname(cls, environment, appname):
"""Returns ip of instance with a deployed application using app name.
:param environment: Murano environment
:param appname: Application name or substring of application name
:return:
"""
for service in environment.services:
if appname in service['name']:
return service['instance']['floatingIpAddress']
@classmethod
def get_ip_by_instance_name(cls, environment, inst_name):
"""Returns ip of instance using instance name.
:param environment: Murano environment
:param name: String, which is substring of name of instance or name of
instance
:return:
"""
for service in environment.services:
if inst_name in service['instance']['name']:
return service['instance']['floatingIpAddress']
@classmethod
def get_k8s_ip_by_instance_name(cls, environment, inst_name, service_name):
"""Returns ip of specific kubernetes node (gateway, master, minion).
Search depends on service name of kubernetes and names of spawned
instances
:param environment: Murano environment
:param inst_name: Name of instance or substring of instance name
:param service_name: Name of Kube Cluster application in Murano
environment
:return: Ip of Kubernetes instances
"""
for service in environment.services:
if service_name in service['name']:
if "gateway" in inst_name:
for gateway in service['gatewayNodes']:
if inst_name in gateway['instance']['name']:
LOG.debug(gateway['instance']['floatingIpAddress'])
return gateway['instance']['floatingIpAddress']
elif "master" in inst_name:
LOG.debug(service['masterNode']['instance'][
'floatingIpAddress'])
return service['masterNode']['instance'][
'floatingIpAddress']
elif "minion" in inst_name:
for minion in service['minionNodes']:
if inst_name in minion['instance']['name']:
LOG.debug(minion['instance']['floatingIpAddress'])
return minion['instance']['floatingIpAddress']
# -----------------------------Cleanup methods---------------------------------
@classmethod
def purge_uploaded_packages(cls):
"""Cleanup for uploaded packages."""
cls.init_list("_packages")
try:
for pkg in cls._packages:
@ -202,3 +388,78 @@ class DeployTestMixin(ZipUtilsMixin):
os.remove(pkg_file)
finally:
cls._package_files = []
@classmethod
def purge_environments(cls):
"""Cleanup for created environments."""
cls.init_list("_environments")
try:
for env in cls._environments:
with ignored(Exception):
cls.environment_delete(env.id)
finally:
cls._environments = []
# -----------------------Methods for environment CRUD--------------------------
@classmethod
def create_environment(cls):
"""Creates Murano environment with random name.
:return: Murano environment
"""
name = cls.rand_name('MuranoTe')
environment = cls.murano_client().environments.create({'name': name})
cls._environments.append(environment)
return environment
@classmethod
def get_environment(cls, environment):
"""Refresh <Environment> variable.
:param environment: Murano environment.
:return: Murano environment.
"""
return cls.murano_client().environments.get(environment.id)
@classmethod
def environment_delete(cls, environment_id, timeout=180):
"""Remove Murano environment.
:param environment_id: ID of Murano environment
:param timeout: Timeout to environment get deleted
:return: :raise RuntimeError:
"""
cls.murano_client().environments.delete(environment_id)
start_time = time.time()
while time.time() - start_time < timeout:
try:
cls.murano_client().environments.get(environment_id)
except exceptions.HTTPNotFound:
return
err_msg = ('Environment {0} was not deleted in {1} seconds'.
format(environment_id, timeout))
LOG.error(err_msg)
raise RuntimeError(err_msg)
# -----------------------Methods for session actions---------------------------
@classmethod
def create_session(cls, environment):
return cls.murano_client().sessions.configure(environment.id)
@classmethod
def delete_session(cls, environment, session):
return cls.murano_client().sessions.delete(environment.id, session.id)
# -------------------------------Heat methods----------------------------------
@classmethod
def _get_stack(cls, environment_id):
for stack in cls.heat_client().stacks.list():
if environment_id in stack.description:
return stack

View File

@ -16,7 +16,7 @@ import os
import time
import uuid
import murano.tests.functional.engine.muranomanager as core
import murano.tests.functional.engine.manager as core
CONF = core.CONF
@ -95,9 +95,9 @@ class MuranoBase(core.MuranoTestsCore):
environment_name = 'Telnetenv' + uuid.uuid4().hex[:5]
env = self._quick_deploy(environment_name, post_body)
self.deployment_success_check(env, 23)
environment = self.deploy_apps(environment_name, post_body)
self.wait_for_environment_deploy(environment)
self.deployment_success_check(environment, 23)
def test_deploy_apache(self):
post_body = {
@ -120,9 +120,9 @@ class MuranoBase(core.MuranoTestsCore):
environment_name = 'Apacheenv' + uuid.uuid4().hex[:5]
env = self._quick_deploy(environment_name, post_body)
self.deployment_success_check(env, 80)
environment = self.deploy_apps(environment_name, post_body)
self.wait_for_environment_deploy(environment)
self.deployment_success_check(environment, 80)
def test_deploy_postgresql(self):
post_body = {
@ -148,9 +148,9 @@ class MuranoBase(core.MuranoTestsCore):
environment_name = 'Postgreenv' + uuid.uuid4().hex[:5]
env = self._quick_deploy(environment_name, post_body)
self.deployment_success_check(env, 5432)
environment = self.deploy_apps(environment_name, post_body)
self.wait_for_environment_deploy(environment)
self.deployment_success_check(environment, 5432)
def test_deploy_tomcat(self):
post_body = {
@ -173,9 +173,9 @@ class MuranoBase(core.MuranoTestsCore):
environment_name = 'Tomcatenv' + uuid.uuid4().hex[:5]
env = self._quick_deploy(environment_name, post_body)
self.deployment_success_check(env, 8080)
environment = self.deploy_apps(environment_name, post_body)
self.wait_for_environment_deploy(environment)
self.deployment_success_check(environment, 8080)
def test_instance_refs_are_removed_after_application_is_removed(self):
# FIXME(sergmelikyan): Revise this as part of proper fix for #1417136
@ -188,7 +188,7 @@ class MuranoBase(core.MuranoTestsCore):
application_id = application1['?']['id']
instance_name = application1['instance']['name']
apps = [application1, application2]
environment = self._quick_deploy(name, *apps)
environment = self.deploy_apps(name, *apps)
# delete telnet application
session = self.murano.sessions.configure(environment.id)
@ -211,12 +211,12 @@ class MuranoBase(core.MuranoTestsCore):
name = 'e' + uuid.uuid4().hex
application = self._get_telnet_app()
environment = self._quick_deploy(name, application)
environment = self.deploy_apps(name, application)
self.wait_for_environment_deploy(environment)
stack = self._get_stack(environment.id)
self.assertIsNotNone(stack)
self.murano.environments.delete(environment.id)
self.murano_client().environments.delete(environment.id)
start_time = time.time()
while stack is not None:

View File

@ -1,27 +1,39 @@
[murano]
# keystone url
#auth_url = http://127.0.0.1:5000/v2.0/
# auth_url = http://127.0.0.1:5000/v2.0/
# keystone user
#user = admin
# user = admin
# password for keystone user
#password = pass
# password = admin
# keystone tenant
#tenant = admin
# tenant = admin
# keyname - used for debugging murano-agent
# keyname = keyname
# murano url
#murano_url = http://localhost:8082
# murano_url = http://127.0.0.1:8082
# Flavor for simple checks
#standard_flavor = m1.medium
# Flavor for sanity checks
# standard_flavor = m1.medium
# Flavor for advanced checks
#advanced_flavor = m1.large
# advanced_flavor = m1.medium
# image for linux services
#linux_image = default_linux
# linux_image = default_linux
# image for docker applications
# docker_image = debian-8-docker.qcow2
# image for kubernetes applications
# kubernetes_image = ubuntu14.04-x64-kubernetes.qcow2
# image for windows services
#windows_image = default_windows
# windows_image = default_windows
# image for hdp sandbox
# hdp_image = hdp-sandbox

View File

@ -32,6 +32,9 @@ MuranoGroup = [
cfg.StrOpt('tenant',
default='admin',
help='keystone tenant'),
cfg.StrOpt('keyname',
default='',
help='name of keypair for debugging'),
cfg.StrOpt('murano_url',
default='http://127.0.0.1:8082/v1/',
help="murano url"),
@ -44,9 +47,18 @@ MuranoGroup = [
cfg.StrOpt('linux_image',
default='default_linux',
help="image for linux services"),
cfg.StrOpt('docker_image',
default='ubuntu14.04-x64-docker',
help="image for docker applications"),
cfg.StrOpt('windows_image',
default='default_windows',
help="image for windows services")
help="image for windows services"),
cfg.StrOpt('hdp_image',
default="hdp-sandbox",
help="image for hdp-sandbox"),
cfg.StrOpt('kubernetes_image',
default="ubuntu14.04-x64-kubernetes",
help="image for kubernetes")
]
CONF = cfg.CONF

View File

@ -0,0 +1,71 @@
# Copyright (c) 2015 Openstack Foundation, Inc.
#
# 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 os
import uuid
import mistralclient.api.client as mistralclient
import testresources
import testtools
import murano.tests.functional.common.tempest_utils as tempest_utils
import murano.tests.functional.common.utils as utils
class MistralIntegration(testtools.TestCase, testtools.testcase.WithAttributes,
testresources.ResourcedTestCase,
tempest_utils.TempestDeployTestMixin):
@classmethod
@utils.memoize
def mistral_client(cls):
keystone_client = cls.keystone_client()
endpoint_type = 'publicURL'
service_type = 'workflowv2'
mistral_url = keystone_client.service_catalog.url_for(
service_type=service_type,
endpoint_type=endpoint_type)
auth_token = keystone_client.auth_token
return mistralclient.client(mistral_url=mistral_url,
auth_url=keystone_client.auth_url,
project_id=keystone_client.tenant_id,
endpoint_type=endpoint_type,
service_type=service_type,
auth_token=auth_token,
user_id=keystone_client.user_id)
@classmethod
def upload_mistral_showcase_app(cls):
app_dir = 'io.murano.apps.test.MistralShowcaseApp'
zip_file_path = cls.zip_dir(os.path.dirname(__file__), app_dir)
cls.init_list("_package_files")
cls._package_files.append(zip_file_path)
return cls.upload_package(
'MistralShowcaseApp',
{"categories": ["Web"], "tags": ["tag"]},
zip_file_path)
@staticmethod
def _create_env_body():
return {
"name": "Mistral_environment",
"?": {
"type": "io.murano.apps.test.MistralShowcaseApp",
"id": str(uuid.uuid4())
}
}

View File

@ -0,0 +1,203 @@
# Copyright (c) 2015 Mirantis, Inc.
#
# 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 logging
import socket
import time
import uuid
import requests
import testresources
import testtools
import murano.tests.functional.common.utils as utils
import murano.tests.functional.engine.config as cfg
CONF = cfg.cfg.CONF
LOG = logging.getLogger(__name__)
class MuranoTestsCore(testtools.TestCase, testtools.testcase.WithAttributes,
testresources.ResourcedTestCase, utils.DeployTestMixin):
"""This manager provides access to Murano-api service."""
@classmethod
def setUpClass(cls):
super(MuranoTestsCore, cls).setUpClass()
cfg.load_config()
cls._environments = []
def setUp(self):
super(MuranoTestsCore, self).setUp()
def tearDown(self):
super(MuranoTestsCore, self).tearDown()
self.purge_environments()
# --------------------------Specific test methods------------------------------
def wait_for_environment_deploy(self, environment):
"""Wait for successful deployment of Murano environment.
Logging deployments and reports of failure.
:param environment: Murano environment
:return: Murano environment
"""
start_time = time.time()
status = environment.manager.get(environment.id).status
while status != 'ready':
status = environment.manager.get(environment.id).status
if time.time() - start_time > 1800:
time.sleep(60)
self._log_report(environment)
self.fail(
'Environment deployment is not finished in 1200 seconds')
elif status == 'deploy failure':
self._log_report(environment)
time.sleep(60)
self.fail('Environment has incorrect status {0}'.
format(status))
time.sleep(5)
LOG.debug('Environment {0} is ready'.format(environment.name))
return environment.manager.get(environment.id)
def status_check(self, environment, configurations, kubernetes=False):
"""Function which gives opportunity to check any count of instances.
:param environment: Murano environment
:param configurations: Array of configurations.
:param kubernetes: Used for parsing multiple instances in one service
False by default.
Example: [[instance_name, *ports], [instance_name, *ports]] ...
Example k8s: [[cluster['name'], instance_name, *ports], [...], ...]
"""
for configuration in configurations:
if kubernetes:
service_name = configuration[0]
LOG.debug('Service: {0}'.format(service_name))
inst_name = configuration[1]
LOG.debug('Instance: {0}'.format(inst_name))
ports = configuration[2:]
LOG.debug('Acquired ports: {0}'.format(ports))
ip = self.get_k8s_ip_by_instance_name(environment, inst_name,
service_name)
if ip and ports:
for port in ports:
self.check_port_access(ip, port)
self.check_k8s_deployment(ip, port)
else:
self.fail('Instance does not have floating IP')
else:
inst_name = configuration[0]
ports = configuration[1:]
ip = self.get_ip_by_instance_name(environment, 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 deployment_success_check(self, environment, *ports):
"""Old style deployment check.
Checks that environment deployment successfully. Only one instance in
environment for this function is permitted for using this function.
:param environment: Murano environment
:param ports:
"""
deployment = self.murano_client().deployments.list(environment.id)[-1]
self.assertEqual('success', deployment.state,
'Deployment status is {0}'.format(deployment.state))
ip = environment.services[0]['instance']['floatingIpAddress']
if ip:
for port in ports:
self.check_port_access(ip, port)
else:
self.fail('Instance does not have floating IP')
def check_port_access(self, ip, port):
"""Check that ports are opened on specific instances.
:param ip: Instance's ip address
:param port: Port that you want to check
"""
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)
def check_k8s_deployment(self, ip, port):
start_time = time.time()
while time.time() - start_time < 600:
try:
LOG.debug('Checking: {0}:{1}'.format(ip, port))
self.verify_connection(ip, port)
return
except RuntimeError as e:
time.sleep(10)
LOG.debug(e)
self.fail('Containers are not ready')
def check_path(self, env, path, inst_name=None):
"""Check path of deployed application using requests method 'GET'.
:param env: Murano environment.
:param path: Path to check
Example: wordpress. e.g. function will check http://<ip>/wordpress
:param inst_name: If defined, function will search through environment
for instance ip and after check path.
"""
environment = env.manager.get(env.id)
if inst_name:
ip = self.get_ip_by_instance_name(environment, inst_name)
else:
ip = environment.services[0]['instance']['floatingIpAddress']
resp = requests.get('http://{0}/{1}'.format(ip, path))
if resp.status_code == 200:
pass
else:
self.fail("Service path unavailable")
def deploy_environment(self, environment, session):
self.murano_client().sessions.deploy(environment.id, session.id)
return self.wait_for_environment_deploy(environment)
def _get_telnet_app(self):
return {
"instance": {
"?": {
"type": "io.murano.resources.LinuxMuranoInstance",
"id": str(uuid.uuid4())
},
"flavor": self.flavor,
"image": self.linux,
"name": "instance{0}".format(uuid.uuid4().hex[:5]),
},
"name": "app{0}".format(uuid.uuid4().hex[:5]),
"?": {
"type": "io.murano.apps.linux.Telnet",
"id": str(uuid.uuid4())
}
}

View File

@ -1,197 +0,0 @@
# Copyright (c) 2015 Mirantis, Inc.
#
# 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 json
import random
import socket
import time
import uuid
import requests
import testresources
import testtools
import yaml
import murano.tests.functional.common.utils as utils
import murano.tests.functional.engine.config as cfg
CONF = cfg.cfg.CONF
class MuranoTestsCore(testtools.TestCase, testtools.testcase.WithAttributes,
testresources.ResourcedTestCase, utils.DeployTestMixin):
"""This manager provides access to Murano-api service."""
@classmethod
def setUpClass(cls):
super(MuranoTestsCore, cls).setUpClass()
cfg.load_config()
cls.keystone = cls.keystone_client()
cls.murano_url = cls.get_murano_url()
cls.heat_url = cls.heat_client()
cls.murano_endpoint = cls.murano_url + '/v1/'
cls.murano = cls.murano_client()
cls._environments = []
def setUp(self):
super(MuranoTestsCore, self).setUp()
self.keystone = self.keystone_client()
self.heat = self.heat_client()
self.murano = self.murano_client()
self.headers = {'X-Auth-Token': self.murano.auth_token,
'content-type': 'application/json'}
def tearDown(self):
super(MuranoTestsCore, self).tearDown()
self.purge_environments()
def rand_name(self, name='murano_env'):
return name + str(random.randint(1, 0x7fffffff))
def wait_for_environment_deploy(self, environment):
start_time = time.time()
status = environment.manager.get(environment.id).status
while status != 'ready':
status = environment.manager.get(environment.id).status
if time.time() - start_time > 3000:
self.fail(
'Environment deployment is not finished in 1200 seconds')
elif status == 'deploy failure':
self.fail('Environment has incorrect status '
'{0}'.format(status))
time.sleep(5)
return environment.manager.get(environment.id)
def check_port_access(self, ip, port):
result = 1
start_time = time.time()
while time.time() - start_time < 300:
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)
def deployment_success_check(self, environment, *ports):
deployment = self.murano.deployments.list(environment.id)[-1]
self.assertEqual('success', deployment.state,
'Deployment status is {0}'.format(deployment.state))
ip = environment.services[0]['instance']['floatingIpAddress']
if ip:
for port in ports:
self.check_port_access(ip, port)
else:
self.fail('Instance does not have floating IP')
def create_env(self):
name = self.rand_name('MuranoTe')
environment = self.murano.environments.create({'name': name})
self._environments.append(environment)
return environment
def create_session(self, environment):
return self.murano.sessions.configure(environment.id)
def delete_session(self, environment, session):
return self.murano.sessions.delete(environment.id, session.id)
def add_service(self, environment, data, session):
"""This function adding a specific service to environment
Returns specific class <Service>
:param environment:
:param data:
:param session:
:return:
"""
return self.murano.services.post(environment.id,
path='/', data=data,
session_id=session.id)
def create_service(self, environment, session, json_data):
"""This function adding a specific service to environment
Returns a JSON object with a service
:param environment:
:param session:
:param json_data:
:return:
"""
headers = self.headers.copy()
headers.update({'x-configuration-session': session.id})
endpoint = '{0}environments/{1}/services'.format(self.murano_endpoint,
environment.id)
return requests.post(endpoint, data=json.dumps(json_data),
headers=headers).json()
def deploy_environment(self, environment, session):
self.murano.sessions.deploy(environment.id, session.id)
return self.wait_for_environment_deploy(environment)
def get_environment(self, environment):
return self.murano.environments.get(environment.id)
def get_service_as_json(self, environment):
service = self.murano.services.list(environment.id)[0]
service = service.to_dict()
service = json.dumps(service)
return yaml.load(service)
def _quick_deploy(self, name, *apps):
environment = self.murano.environments.create({'name': name})
self._environments.append(environment)
session = self.murano.sessions.configure(environment.id)
for app in apps:
self.murano.services.post(environment.id,
path='/',
data=app,
session_id=session.id)
self.murano.sessions.deploy(environment.id, session.id)
return self.wait_for_environment_deploy(environment)
def _get_stack(self, environment_id):
for stack in self.heat.stacks.list():
if environment_id in stack.description:
return stack
def _get_telnet_app(self):
return {
"instance": {
"?": {
"type": "io.murano.resources.LinuxMuranoInstance",
"id": str(uuid.uuid4())
},
"flavor": self.flavor,
"image": self.linux,
"name": "instance{0}".format(uuid.uuid4().hex[:5]),
},
"name": "app{0}".format(uuid.uuid4().hex[:5]),
"?": {
"type": "io.murano.apps.linux.Telnet",
"id": str(uuid.uuid4())
}
}

View File

@ -12,38 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
import uuid
import mistralclient.api.client as mistralclient
import testtools
import murano.tests.functional.common.tempest_utils as tempest_utils
import murano.tests.functional.common.utils as common_utils
import murano.tests.functional.engine.integration_base as core
class MistralTest(testtools.TestCase, tempest_utils.TempestDeployTestMixin):
@classmethod
def mistral_client(cls):
keystone_client = cls.keystone_client()
endpoint_type = 'publicURL'
service_type = 'workflowv2'
mistral_url = keystone_client.service_catalog.url_for(
service_type=service_type,
endpoint_type=endpoint_type)
auth_token = keystone_client.auth_token
return mistralclient.client(mistral_url=mistral_url,
auth_url=keystone_client.auth_url,
project_id=keystone_client.tenant_id,
endpoint_type=endpoint_type,
service_type=service_type,
auth_token=auth_token,
user_id=keystone_client.user_id)
class MistralTest(core.MistralIntegration):
@classmethod
def setUpClass(cls):
@ -59,30 +34,11 @@ class MistralTest(testtools.TestCase, tempest_utils.TempestDeployTestMixin):
@classmethod
def tearDownClass(cls):
with common_utils.ignored(Exception):
cls.purge_environments()
with common_utils.ignored(Exception):
cls.purge_uploaded_packages()
@classmethod
def upload_mistral_showcase_app(cls):
app_dir = 'io.murano.apps.test.MistralShowcaseApp'
zip_file_path = cls.zip_dir(os.path.dirname(__file__), app_dir)
cls.init_list("_package_files")
cls._package_files.append(zip_file_path)
return cls.upload_package(
'MistralShowcaseApp',
{"categories": ["Web"], "tags": ["tag"]},
zip_file_path)
@staticmethod
def _create_env_body():
return {
"name": "Mistral_environment",
"?": {
"type": "io.murano.apps.test.MistralShowcaseApp",
"id": str(uuid.uuid4())
}
}
def test_deploy_package_success(self):
# Test expects successful deployment and one output: input_1_value.

View File

@ -18,6 +18,7 @@ testscenarios>=0.4
testtools>=1.4.0
unittest2
pylint==1.4.1 # GNU GPL v2
requests>=2.5.2
# Some of the tests use real MySQL and Postgres databases
PyMySQL>=0.6.2 # MIT License