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:
parent
d3717666db
commit
2d3dd9c6da
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
@ -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
|
||||
|
71
murano/tests/functional/engine/integration_base.py
Normal file
71
murano/tests/functional/engine/integration_base.py
Normal 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())
|
||||
}
|
||||
}
|
203
murano/tests/functional/engine/manager.py
Normal file
203
murano/tests/functional/engine/manager.py
Normal 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())
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user