Merge "Refactor to improve modularity, scalability, OOP"

This commit is contained in:
Zuul 2018-04-16 19:19:38 +00:00 committed by Gerrit Code Review
commit 91a9bf7f1b
41 changed files with 2493 additions and 1881 deletions

View File

@ -1,279 +0,0 @@
#!/usr/bin/env python
# Copyright 2013 Red Hat, 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 logging
import re
import requests
import urllib3
import urlparse
LOG = logging.getLogger(__name__)
MULTIPLE_SLASH = re.compile(r'/+')
class ServiceError(Exception):
pass
class Service(object):
def __init__(self, name, service_url, token, disable_ssl_validation):
self.name = name
self.service_url = service_url
self.headers = {'Accept': 'application/json', 'X-Auth-Token': token}
self.disable_ssl_validation = disable_ssl_validation
def do_get(self, url, top_level=False, top_level_path=""):
parts = list(urlparse.urlparse(url))
# 2 is the path offset
if top_level:
parts[2] = '/' + top_level_path
parts[2] = MULTIPLE_SLASH.sub('/', parts[2])
url = urlparse.urlunparse(parts)
try:
if self.disable_ssl_validation:
urllib3.disable_warnings()
http = urllib3.PoolManager(cert_reqs='CERT_NONE')
else:
http = urllib3.PoolManager()
r = http.request('GET', url, headers=self.headers)
except Exception as e:
LOG.error("Request on service '%s' with url '%s' failed",
(self.name, url))
raise e
if r.status >= 400:
raise ServiceError("Request on service '%s' with url '%s' failed"
" with code %d" % (self.name, url, r.status))
return r.data
def get_extensions(self):
return []
def get_versions(self):
return []
class VersionedService(Service):
def get_versions(self, top_level=True):
body = self.do_get(self.service_url, top_level=top_level)
body = json.loads(body)
return self.deserialize_versions(body)
def deserialize_versions(self, body):
return map(lambda x: x['id'], body['versions'])
def no_port_cut_url(self):
# if there is no port defined, cut the url from version to the end
u = urllib3.util.parse_url(self.service_url)
url = self.service_url
if u.port is None:
found = re.findall(r'v\d', url)
if len(found) > 0:
index = url.index(found[0])
url = self.service_url[:index]
return (url, u.port is not None)
class ComputeService(VersionedService):
def get_extensions(self):
body = self.do_get(self.service_url + '/extensions')
body = json.loads(body)
return map(lambda x: x['alias'], body['extensions'])
def get_versions(self):
url, top_level = self.no_port_cut_url()
body = self.do_get(url, top_level=top_level)
body = json.loads(body)
return self.deserialize_versions(body)
class ImageService(VersionedService):
def get_versions(self):
return super(ImageService, self).get_versions(top_level=False)
class NetworkService(VersionedService):
def get_extensions(self):
body = self.do_get(self.service_url + '/v2.0/extensions.json')
body = json.loads(body)
return map(lambda x: x['alias'], body['extensions'])
class VolumeService(VersionedService):
def get_extensions(self):
body = self.do_get(self.service_url + '/extensions')
body = json.loads(body)
return map(lambda x: x['alias'], body['extensions'])
def get_versions(self):
url, top_level = self.no_port_cut_url()
body = self.do_get(url, top_level=top_level)
body = json.loads(body)
return self.deserialize_versions(body)
class IdentityService(VersionedService):
def __init__(self, name, service_url, token, disable_ssl_validation):
super(VersionedService, self).__init__(
name, service_url, token, disable_ssl_validation)
version = ''
if 'v2' in self.service_url:
version = '/v2.0'
url_parse = urlparse.urlparse(self.service_url)
self.service_url = '{}://{}{}'.format(url_parse.scheme,
url_parse.netloc, version)
def get_extensions(self):
if 'v2' in self.service_url:
body = self.do_get(self.service_url + '/extensions')
body = json.loads(body)
return map(lambda x: x['alias'], body['extensions']['values'])
# Keystone api changed in v3, the concept of extensions change. Right
# now, all the existin extensions are part of keystone core api, so,
# there's no longer the /extensions endpoint. The extensions that are
# stable, are enabled by default, the ones marked as experimental are
# disabled by default. Checking the tempest source, there's no test
# pointing to extensions endpoint, so I am very confident that this
# will not be an issue. If so, we need to list all the /OS-XYZ
# extensions to identify what is enabled or not. This would be a manual
# check every time keystone change, add or delete an extension, so I
# rather prefer to return empty here for now.
return []
def deserialize_versions(self, body):
try:
versions = []
for v in body['versions']['values']:
# TripleO is in transition to v3 only, so the environment
# still returns v2 versions even though they're deprecated.
# Therefor pick only versions with stable status.
if v['status'] == 'stable':
versions.append(v['id'])
return versions
except KeyError:
return [body['version']['id']]
def get_versions(self):
return super(IdentityService, self).get_versions(top_level=False)
class ObjectStorageService(Service):
def get_extensions(self):
body = self.do_get(self.service_url, top_level=True,
top_level_path="info")
body = json.loads(body)
# Remove Swift general information from extensions list
body.pop('swift')
return body.keys()
service_dict = {'compute': ComputeService,
'image': ImageService,
'network': NetworkService,
'object-store': ObjectStorageService,
'volumev3': VolumeService,
'identity': IdentityService}
def get_service_class(service_name):
return service_dict.get(service_name, Service)
def get_identity_v3_extensions(keystone_v3_url):
"""Returns discovered identity v3 extensions
As keystone V3 uses a JSON Home to store the extensions,
this method is kept here just for the sake of functionality, but it
implements a different discovery method.
:param keystone_v3_url: Keystone V3 auth url
:return: A list with the discovered extensions
"""
try:
r = requests.get(keystone_v3_url,
verify=False,
headers={'Accept': 'application/json-home'})
except requests.exceptions.RequestException as re:
LOG.error("Request on service '%s' with url '%s' failed",
'identity', keystone_v3_url)
raise re
ext_h = 'http://docs.openstack.org/api/openstack-identity/3/ext/'
res = [x for x in json.loads(r.content)['resources'].keys()]
ext = [ex for ex in res if 'ext' in ex]
return list(set([str(e).replace(ext_h, '').split('/')[0] for e in ext]))
def discover(auth_provider, region, object_store_discovery=True,
api_version=2, disable_ssl_certificate_validation=True):
"""Returns a dict with discovered apis.
:param auth_provider: An AuthProvider to obtain service urls.
:param region: A specific region to use. If the catalog has only one region
then that region will be used.
:return: A dict with an entry for the type of each discovered service.
Each entry has keys for 'extensions' and 'versions'.
"""
token, auth_data = auth_provider.get_auth()
services = {}
service_catalog = 'serviceCatalog'
public_url = 'publicURL'
identity_port = urlparse.urlparse(auth_provider.auth_url).port
if identity_port is None:
identity_port = ""
else:
identity_port = ":" + str(identity_port)
identity_version = urlparse.urlparse(auth_provider.auth_url).path
if api_version == 3:
service_catalog = 'catalog'
public_url = 'url'
# FIXME(chandankumar): It is a workaround to filter services whose
# endpoints does not exist. Once it is merged. Let's rewrite the whole
# stuff.
auth_data[service_catalog] = [data for data in auth_data[service_catalog]
if data['endpoints']]
for entry in auth_data[service_catalog]:
name = entry['type']
services[name] = dict()
for _ep in entry['endpoints']:
if api_version == 3:
if _ep['region'] == region and _ep['interface'] == 'public':
ep = _ep
break
else:
if _ep['region'] == region:
ep = _ep
break
else:
ep = entry['endpoints'][0]
if 'identity' in ep[public_url]:
services[name]['url'] = ep[public_url].replace(
"/identity", "{0}{1}".format(
identity_port, identity_version))
else:
services[name]['url'] = ep[public_url]
service_class = get_service_class(name)
service = service_class(name, services[name]['url'], token,
disable_ssl_certificate_validation)
if name == 'object-store' and not object_store_discovery:
services[name]['extensions'] = []
elif 'v3' not in ep[public_url]: # is not v3 url
services[name]['extensions'] = service.get_extensions()
services[name]['versions'] = service.get_versions()
return services

View File

@ -1,4 +1,4 @@
# Copyright 2018 Red Hat, Inc. # Copyright 2016, 2018 Red Hat, Inc.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -41,8 +41,10 @@ class ProjectsClient(object):
def __init__(self, auth, catalog_type, identity_region, endpoint_type, def __init__(self, auth, catalog_type, identity_region, endpoint_type,
identity_version, **default_params): identity_version, **default_params):
self.identity_version = identity_version self.identity_version = identity_version
self.project_class = tenants_client.TenantsClient if \ if self.identity_version == "v2":
self.identity_version == "v2" else projects_client.ProjectsClient self.project_class = tenants_client.TenantsClient
else:
self.project_class = projects_client.ProjectsClient
self.client = self.project_class(auth, catalog_type, identity_region, self.client = self.project_class(auth, catalog_type, identity_region,
endpoint_type, **default_params) endpoint_type, **default_params)
@ -76,24 +78,19 @@ class ClientManager(object):
:param conf: TempestConf object :param conf: TempestConf object
:param creds: Credentials object :param creds: Credentials object
""" """
self.identity_region = creds.identity_region
self.identity_region = conf.get_defaulted('identity', 'region')
self.auth_provider = creds.get_auth_provider() self.auth_provider = creds.get_auth_provider()
default_params = { default_params = self._get_default_params(conf)
'disable_ssl_certificate_validation': compute_params = self._get_compute_params(conf)
conf.get_defaulted('identity',
'disable_ssl_certificate_validation'),
'ca_certs': conf.get_defaulted('identity', 'ca_certificates_file')
}
compute_params = {
'service': conf.get_defaulted('compute', 'catalog_type'),
'region': self.identity_region,
'endpoint_type': conf.get_defaulted('compute', 'endpoint_type')
}
compute_params.update(default_params) compute_params.update(default_params)
self.identity = self.get_identity_client(conf, default_params) catalog_type = conf.get_defaulted('identity', 'catalog_type')
self.identity = self.get_identity_client(
creds.identity_version,
catalog_type,
default_params)
self.tenants = ProjectsClient( self.tenants = ProjectsClient(
self.auth_provider, self.auth_provider,
@ -105,15 +102,15 @@ class ClientManager(object):
self.set_roles_client( self.set_roles_client(
auth=self.auth_provider, auth=self.auth_provider,
creds=creds, identity_version=creds.identity_version,
conf=conf, catalog_type=catalog_type,
endpoint_type='publicURL', endpoint_type='publicURL',
default_params=default_params) default_params=default_params)
self.set_users_client( self.set_users_client(
auth=self.auth_provider, auth=self.auth_provider,
creds=creds, identity_version=creds.identity_version,
conf=conf, catalog_type=catalog_type,
endpoint_type='publicURL', endpoint_type='publicURL',
default_params=default_params) default_params=default_params)
@ -134,7 +131,7 @@ class ClientManager(object):
self.identity_region, self.identity_region,
**default_params) **default_params)
self.volume_service = services_client.ServicesClient( self.volume_client = services_client.ServicesClient(
self.auth_provider, self.auth_provider,
conf.get_defaulted('volume', 'catalog_type'), conf.get_defaulted('volume', 'catalog_type'),
self.identity_region, self.identity_region,
@ -167,65 +164,99 @@ class ClientManager(object):
tenant = self.tenants.get_project_by_name(creds.tenant_name) tenant = self.tenants.get_project_by_name(creds.tenant_name)
conf.set('identity', 'admin_tenant_id', tenant['id']) conf.set('identity', 'admin_tenant_id', tenant['id'])
def get_identity_client(self, conf, default_params): def _get_default_params(self, conf):
default_params = {
'disable_ssl_certificate_validation':
conf.get_defaulted('identity',
'disable_ssl_certificate_validation'),
'ca_certs': conf.get_defaulted('identity', 'ca_certificates_file')
}
return default_params
def _get_compute_params(self, conf):
compute_params = {
'service': conf.get_defaulted('compute', 'catalog_type'),
'region': self.identity_region,
'endpoint_type': conf.get_defaulted('compute', 'endpoint_type')
}
return compute_params
def get_identity_client(self, identity_version, catalog_type,
default_params):
"""Obtain identity client. """Obtain identity client.
:type conf: TempestConf object :type identity_version: string
:type default_params: dict :type default_params: dict
""" """
if "v2.0" in conf.get("identity", "uri"): if "v3" in identity_version:
return identity_client.IdentityClient( return identity_v3_client.IdentityClient(
self.auth_provider, self.auth_provider,
conf.get_defaulted('identity', 'catalog_type'), catalog_type,
self.identity_region, endpoint_type='publicURL', self.identity_region, endpoint_type='publicURL',
**default_params) **default_params)
else: else:
return identity_v3_client.IdentityClient( return identity_client.IdentityClient(
self.auth_provider, self.auth_provider,
conf.get_defaulted('identity', 'catalog_type'), catalog_type,
self.identity_region, endpoint_type='publicURL', self.identity_region, endpoint_type='publicURL',
**default_params) **default_params)
def set_users_client(self, auth, creds, conf, endpoint_type, def get_service_client(self, service_name):
default_params): """Returns name of the service's client.
:type service_name: string
:rtype: client object or None when the client doesn't exist
"""
if service_name == "image":
return self.images
elif service_name == "network":
# return whole ClientManager object because NetworkService
# currently needs to have an access to get_neutron/nova_client
# methods which are chosen according to neutron presence
return self
else:
return None
def set_users_client(self, auth, identity_version, catalog_type,
endpoint_type, default_params):
"""Sets users client. """Sets users client.
:param auth: auth provider :param auth: auth provider
:type auth: auth.KeystoneV2AuthProvider (or V3) :type auth: auth.KeystoneV2AuthProvider (or V3)
:type creds: Credentials object :type identity_version: string
:type conf: TempestConf object :type catalog_type: string
:type endpoint_type: string :type endpoint_type: string
:type default_params: dict :type default_params: dict
""" """
users_class = users_client.UsersClient users_class = users_client.UsersClient
if "v3" in creds.identity_version: if "v3" in identity_version:
users_class = users_v3_client.UsersClient users_class = users_v3_client.UsersClient
self.users = users_class( self.users = users_class(
auth, auth,
conf.get_defaulted('identity', 'catalog_type'), catalog_type,
self.identity_region, self.identity_region,
endpoint_type=endpoint_type, endpoint_type=endpoint_type,
**default_params) **default_params)
def set_roles_client(self, auth, creds, conf, endpoint_type, def set_roles_client(self, auth, identity_version, catalog_type,
default_params): endpoint_type, default_params):
"""Sets roles client. """Sets roles client.
:param auth: auth provider :param auth: auth provider
:type auth: auth.KeystoneV2AuthProvider (or V3) :type auth: auth.KeystoneV2AuthProvider (or V3)
:type creds: Credentials object :type identity_version: string
:type conf: TempestConf object :type catalog_type: string
:type endpoint_type: string :type endpoint_type: string
:type default_params: dict :type default_params: dict
""" """
roles_class = roles_client.RolesClient roles_class = roles_client.RolesClient
if "v3" in creds.identity_version: if "v3" in identity_version:
roles_class = roles_v3_client.RolesClient roles_class = roles_v3_client.RolesClient
self.roles = roles_class( self.roles = roles_class(
auth, auth,
conf.get_defaulted('identity', 'catalog_type'), catalog_type,
self.identity_region, self.identity_region,
endpoint_type=endpoint_type, endpoint_type=endpoint_type,
**default_params) **default_params)

View File

@ -0,0 +1,67 @@
# Copyright 2013, 2016, 2018 Red Hat, 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 logging
import os
LOG = logging.getLogger(__name__)
# Get the current tempest workspace path
TEMPEST_WORKSPACE = os.getcwd()
DEFAULTS_FILE = os.path.join(TEMPEST_WORKSPACE, "etc",
"default-overrides.conf")
DEFAULT_IMAGE = ("http://download.cirros-cloud.net/0.3.5/"
"cirros-0.3.5-x86_64-disk.img")
DEFAULT_IMAGE_FORMAT = 'qcow2'
# services and their codenames
SERVICE_NAMES = {
'baremetal': 'ironic',
'compute': 'nova',
'database': 'trove',
'data-processing': 'sahara',
'image': 'glance',
'network': 'neutron',
'object-store': 'swift',
'orchestration': 'heat',
'share': 'manila',
'telemetry': 'ceilometer',
'volume': 'cinder',
'messaging': 'zaqar',
'metric': 'gnocchi',
'event': 'panko',
}
# what API versions could the service have and should be enabled/disabled
# depending on whether they get discovered as supported. Services with only one
# version don't need to be here, neither do service versions that are not
# configurable in tempest.conf
SERVICE_VERSIONS = {
'image': {'supported_versions': ['v1', 'v2'], 'catalog': 'image'},
'identity': {'supported_versions': ['v2', 'v3'], 'catalog': 'identity'},
'volume': {'supported_versions': ['v2', 'v3'], 'catalog': 'volumev3'}
}
# Keep track of where the extensions are saved for that service.
# This is necessary because the configuration file is inconsistent - it uses
# different option names for service extension depending on the service.
SERVICE_EXTENSION_KEY = {
'compute': 'api_extensions',
'object-store': 'discoverable_apis',
'network': 'api_extensions',
'volume': 'api_extensions',
'identity': 'api_extensions'
}

View File

@ -1,4 +1,4 @@
# Copyright 2018 Red Hat, Inc. # Copyright 2016, 2018 Red Hat, Inc.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -35,6 +35,8 @@ class Credentials(object):
self.password = self.get_credential('password') self.password = self.get_credential('password')
self.tenant_name = self.get_credential('tenant_name') self.tenant_name = self.get_credential('tenant_name')
self.identity_version = self._get_identity_version() self.identity_version = self._get_identity_version()
self.api_version = 3 if self.identity_version == "v3" else 2
self.identity_region = self._conf.get_defaulted('identity', 'region')
self.disable_ssl_certificate_validation = self._conf.get_defaulted( self.disable_ssl_certificate_validation = self._conf.get_defaulted(
'identity', 'identity',
'disable_ssl_certificate_validation' 'disable_ssl_certificate_validation'

91
config_tempest/flavors.py Normal file
View File

@ -0,0 +1,91 @@
# Copyright 2016 Red Hat, 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.
from constants import LOG
class Flavors(object):
def __init__(self, client, allow_creation, conf):
"""Init.
:type client: FlavorsClient object from tempest lib
:type allow_creation: boolean
:type conf: TempestConf object
"""
self.client = client
self.allow_creation = allow_creation
self._conf = conf
def create_tempest_flavors(self):
"""Find or create flavors and set them in conf.
If 'flavor_ref' and 'flavor_ref_alt' are specified in conf, it will
first try to find those - otherwise it will try finding or creating
'm1.nano' and 'm1.micro' and overwrite those options in conf.
"""
# m1.nano flavor
flavor_id = None
if self._conf.has_option('compute', 'flavor_ref'):
flavor_id = self._conf.get('compute', 'flavor_ref')
flavor_id = self.find_or_create_flavor(flavor_id, 'm1.nano', ram=64)
self._conf.set('compute', 'flavor_ref', flavor_id)
# m1.micro flavor
alt_flavor_id = None
if self._conf.has_option('compute', 'flavor_ref_alt'):
alt_flavor_id = self._conf.get('compute', 'flavor_ref_alt')
alt_flavor_id = self.find_or_create_flavor(alt_flavor_id, 'm1.micro',
ram=128)
self._conf.set('compute', 'flavor_ref_alt', alt_flavor_id)
def find_or_create_flavor(self, flavor_id, flavor_name,
ram=64, vcpus=1, disk=0):
"""Try finding flavor by ID or name, create if not found.
:param flavor_id: first try finding the flavor by this
:param flavor_name: find by this if it was not found by ID, create new
flavor with this name if not found at allCLIENT_MOCK
:param ram: memory of created flavor in MB
:param vcpus: number of VCPUs for the flavor
:param disk: size of disk for flavor in GB
"""
flavor = None
flavors = self.client.list_flavors()['flavors']
# try finding it by the ID first
if flavor_id:
found = [f for f in flavors if f['id'] == flavor_id]
if found:
flavor = found[0]
# if not found, try finding it by name
if flavor_name and not flavor:
found = [f for f in flavors if f['name'] == flavor_name]
if found:
flavor = found[0]
if not flavor and not self.allow_creation:
raise Exception("Flavor '%s' not found, but resource creation"
" isn't allowed. Either use '--create' or provide"
" an existing flavor" % flavor_name)
if not flavor:
LOG.info("Creating flavor '%s'", flavor_name)
flavor = self.client.create_flavor(name=flavor_name,
ram=ram, vcpus=vcpus,
disk=disk, id=None)
return flavor['flavor']['id']
else:
LOG.info("(no change) Found flavor '%s'", flavor['name'])
return flavor['id']

View File

@ -36,71 +36,26 @@ https://docs.openstack.org/developer/os-client-config/
obtained by querying the cloud. obtained by querying the cloud.
""" """
import api_discovery
import argparse import argparse
import ConfigParser import ConfigParser
import logging import logging
import os import os
import shutil
import sys import sys
import urllib2
from clients import ClientManager from clients import ClientManager
import constants as C
from constants import LOG
from credentials import Credentials from credentials import Credentials
from flavors import Flavors
import os_client_config import os_client_config
from oslo_config import cfg from oslo_config import cfg
from tempest.lib import exceptions from services import boto
from services import ceilometer
from services.horizon import configure_horizon
from services.services import Services
from services import volume
import tempest_conf import tempest_conf
from users import Users
LOG = logging.getLogger(__name__)
# Get the current tempest workspace path
TEMPEST_WORKSPACE = os.getcwd()
DEFAULTS_FILE = os.path.join(TEMPEST_WORKSPACE, "etc",
"default-overrides.conf")
DEFAULT_IMAGE = ("http://download.cirros-cloud.net/0.3.5/"
"cirros-0.3.5-x86_64-disk.img")
DEFAULT_IMAGE_FORMAT = 'qcow2'
# services and their codenames
SERVICE_NAMES = {
'baremetal': 'ironic',
'compute': 'nova',
'database': 'trove',
'data-processing': 'sahara',
'image': 'glance',
'network': 'neutron',
'object-store': 'swift',
'orchestration': 'heat',
'share': 'manila',
'telemetry': 'ceilometer',
'volume': 'cinder',
'messaging': 'zaqar',
'metric': 'gnocchi',
'event': 'panko',
}
# what API versions could the service have and should be enabled/disabled
# depending on whether they get discovered as supported. Services with only one
# version don't need to be here, neither do service versions that are not
# configurable in tempest.conf
SERVICE_VERSIONS = {
'image': {'supported_versions': ['v1', 'v2'], 'catalog': 'image'},
'identity': {'supported_versions': ['v2', 'v3'], 'catalog': 'identity'},
'volume': {'supported_versions': ['v2', 'v3'], 'catalog': 'volumev3'}
}
# Keep track of where the extensions are saved for that service.
# This is necessary because the configuration file is inconsistent - it uses
# different option names for service extension depending on the service.
SERVICE_EXTENSION_KEY = {
'compute': 'api_extensions',
'object-store': 'discoverable_apis',
'network': 'api_extensions',
'volume': 'api_extensions',
'identity': 'api_extensions'
}
def set_logging(debug, verbose): def set_logging(debug, verbose):
@ -154,9 +109,9 @@ def set_options(conf, deployer_input, non_admin, overrides=[],
:param cloud_creds: Cloud credentials from client's config :param cloud_creds: Cloud credentials from client's config
:type cloud_creds: dict :type cloud_creds: dict
""" """
if os.path.isfile(DEFAULTS_FILE): if os.path.isfile(C.DEFAULTS_FILE):
LOG.info("Reading defaults from file '%s'", DEFAULTS_FILE) LOG.info("Reading defaults from file '%s'", C.DEFAULTS_FILE)
conf.read(DEFAULTS_FILE) conf.read(C.DEFAULTS_FILE)
if deployer_input and os.path.isfile(deployer_input): if deployer_input and os.path.isfile(deployer_input):
read_deployer_input(deployer_input, conf) read_deployer_input(deployer_input, conf)
@ -192,6 +147,14 @@ def set_options(conf, deployer_input, non_admin, overrides=[],
for section, key, value in overrides: for section, key, value in overrides:
conf.set(section, key, value, priority=True) conf.set(section, key, value, priority=True)
uri = conf.get("identity", "uri")
if "v3" in uri:
conf.set("identity", "auth_version", "v3")
conf.set("identity", "uri_v3", uri)
else:
# TODO(arxcruz) make a check if v3 is enabled
conf.set("identity", "uri_v3", uri.replace("v2.0", "v3"))
def parse_arguments(): def parse_arguments():
cloud_config = os_client_config.OpenStackConfig() cloud_config = os_client_config.OpenStackConfig()
@ -223,14 +186,14 @@ def parse_arguments():
help='Run without admin creds') help='Run without admin creds')
parser.add_argument('--test-accounts', default=None, metavar='PATH', parser.add_argument('--test-accounts', default=None, metavar='PATH',
help='Use accounts from accounts.yaml') help='Use accounts from accounts.yaml')
parser.add_argument('--image-disk-format', default=DEFAULT_IMAGE_FORMAT, parser.add_argument('--image-disk-format', default=C.DEFAULT_IMAGE_FORMAT,
help="""a format of an image to be uploaded to glance. help="""a format of an image to be uploaded to glance.
Default is '%s'""" % DEFAULT_IMAGE_FORMAT) Default is '%s'""" % C.DEFAULT_IMAGE_FORMAT)
parser.add_argument('--image', default=DEFAULT_IMAGE, parser.add_argument('--image', default=C.DEFAULT_IMAGE,
help="""an image to be uploaded to glance. The name of help="""an image to be uploaded to glance. The name of
the image is the leaf name of the path which the image is the leaf name of the path which
can be either a filename or url. Default is can be either a filename or url. Default is
'%s'""" % DEFAULT_IMAGE) '%s'""" % C.DEFAULT_IMAGE)
parser.add_argument('--network-id', parser.add_argument('--network-id',
help="""The ID of an existing network in our openstack help="""The ID of an existing network in our openstack
instance with external connectivity""") instance with external connectivity""")
@ -247,7 +210,6 @@ def parse_arguments():
" together, since creating" " resources requires" " together, since creating" " resources requires"
" admin rights") " admin rights")
args.overrides = parse_overrides(args.overrides) args.overrides = parse_overrides(args.overrides)
args.remove = parse_values_to_remove(args.remove)
cloud = cloud_config.get_one_cloud(argparse=args) cloud = cloud_config.get_one_cloud(argparse=args)
return cloud return cloud
@ -333,451 +295,9 @@ def set_cloud_config_values(non_admin, cloud_creds, conf):
'Could not load some identity options from cloud config file') 'Could not load some identity options from cloud config file')
def create_tempest_users(tenants_client, roles_client, users_client, conf,
services):
"""Create users necessary for Tempest if they don't exist already."""
create_user_with_tenant(tenants_client, users_client,
conf.get('identity', 'username'),
conf.get('identity', 'password'),
conf.get('identity', 'tenant_name'))
username = conf.get_defaulted('auth', 'admin_username')
if username is None:
username = conf.get_defaulted('identity', 'admin_username')
give_role_to_user(tenants_client, roles_client, users_client,
username,
conf.get('identity', 'tenant_name'), role_name='admin')
# Prior to juno, and with earlier juno defaults, users needed to have
# the heat_stack_owner role to use heat stack apis. We assign that role
# to the user if the role is present.
if 'orchestration' in services:
give_role_to_user(tenants_client, roles_client, users_client,
conf.get('identity', 'username'),
conf.get('identity', 'tenant_name'),
role_name='heat_stack_owner',
role_required=False)
create_user_with_tenant(tenants_client, users_client,
conf.get('identity', 'alt_username'),
conf.get('identity', 'alt_password'),
conf.get('identity', 'alt_tenant_name'))
def give_role_to_user(tenants_client, roles_client, users_client, username,
tenant_name, role_name, role_required=True):
"""Give the user a role in the project (tenant).""",
tenant_id = tenants_client.get_project_by_name(tenant_name)['id']
users = users_client.list_users()
user_ids = [u['id'] for u in users['users'] if u['name'] == username]
user_id = user_ids[0]
roles = roles_client.list_roles()
role_ids = [r['id'] for r in roles['roles'] if r['name'] == role_name]
if not role_ids:
if role_required:
raise Exception("required role %s not found" % role_name)
LOG.debug("%s role not required", role_name)
return
role_id = role_ids[0]
try:
roles_client.create_user_role_on_project(tenant_id, user_id, role_id)
LOG.debug("User '%s' was given the '%s' role in project '%s'",
username, role_name, tenant_name)
except exceptions.Conflict:
LOG.debug("(no change) User '%s' already has the '%s' role in"
" project '%s'", username, role_name, tenant_name)
def create_user_with_tenant(tenants_client, users_client, username,
password, tenant_name):
"""Create a user and a tenant if it doesn't exist."""
LOG.info("Creating user '%s' with tenant '%s' and password '%s'",
username, tenant_name, password)
tenant_description = "Tenant for Tempest %s user" % username
email = "%s@test.com" % username
# create a tenant
try:
tenants_client.create_project(name=tenant_name,
description=tenant_description)
except exceptions.Conflict:
LOG.info("(no change) Tenant '%s' already exists", tenant_name)
tenant_id = tenants_client.get_project_by_name(tenant_name)['id']
# create a user
try:
users_client.create_user(**{'name': username, 'password': password,
'tenantId': tenant_id, 'email': email})
except exceptions.Conflict:
LOG.info("User '%s' already exists.", username)
def create_tempest_flavors(client, conf, allow_creation):
"""Find or create flavors 'm1.nano' and 'm1.micro' and set them in conf.
If 'flavor_ref' and 'flavor_ref_alt' are specified in conf, it will first
try to find those - otherwise it will try finding or creating 'm1.nano' and
'm1.micro' and overwrite those options in conf.
:param allow_creation: if False, fail if flavors were not found
"""
# m1.nano flavor
flavor_id = None
if conf.has_option('compute', 'flavor_ref'):
flavor_id = conf.get('compute', 'flavor_ref')
flavor_id = find_or_create_flavor(client,
flavor_id, 'm1.nano',
allow_creation, ram=64)
conf.set('compute', 'flavor_ref', flavor_id)
# m1.micro flavor
alt_flavor_id = None
if conf.has_option('compute', 'flavor_ref_alt'):
alt_flavor_id = conf.get('compute', 'flavor_ref_alt')
alt_flavor_id = find_or_create_flavor(client,
alt_flavor_id, 'm1.micro',
allow_creation, ram=128)
conf.set('compute', 'flavor_ref_alt', alt_flavor_id)
def find_or_create_flavor(client, flavor_id, flavor_name,
allow_creation, ram=64, vcpus=1, disk=0):
"""Try finding flavor by ID or name, create if not found.
:param flavor_id: first try finding the flavor by this
:param flavor_name: find by this if it was not found by ID, create new
flavor with this name if not found at all
:param allow_creation: if False, fail if flavors were not found
:param ram: memory of created flavor in MB
:param vcpus: number of VCPUs for the flavor
:param disk: size of disk for flavor in GB
"""
flavor = None
flavors = client.list_flavors()['flavors']
# try finding it by the ID first
if flavor_id:
found = [f for f in flavors if f['id'] == flavor_id]
if found:
flavor = found[0]
# if not found previously, try finding it by name
if flavor_name and not flavor:
found = [f for f in flavors if f['name'] == flavor_name]
if found:
flavor = found[0]
if not flavor and not allow_creation:
raise Exception("Flavor '%s' not found, but resource creation"
" isn't allowed. Either use '--create' or provide"
" an existing flavor" % flavor_name)
if not flavor:
LOG.info("Creating flavor '%s'", flavor_name)
flavor = client.create_flavor(name=flavor_name,
ram=ram, vcpus=vcpus,
disk=disk, id=None)
return flavor['flavor']['id']
else:
LOG.info("(no change) Found flavor '%s'", flavor['name'])
return flavor['id']
def create_tempest_images(client, conf, image_path, allow_creation,
disk_format):
img_path = os.path.join(conf.get("scenario", "img_dir"),
os.path.basename(image_path))
name = image_path[image_path.rfind('/') + 1:]
conf.set('scenario', 'img_file', name)
alt_name = name + "_alt"
image_id = None
if conf.has_option('compute', 'image_ref'):
image_id = conf.get('compute', 'image_ref')
image_id = find_or_upload_image(client,
image_id, name, allow_creation,
image_source=image_path,
image_dest=img_path,
disk_format=disk_format)
alt_image_id = None
if conf.has_option('compute', 'image_ref_alt'):
alt_image_id = conf.get('compute', 'image_ref_alt')
alt_image_id = find_or_upload_image(client,
alt_image_id, alt_name, allow_creation,
image_source=image_path,
image_dest=img_path,
disk_format=disk_format)
conf.set('compute', 'image_ref', image_id)
conf.set('compute', 'image_ref_alt', alt_image_id)
def check_ceilometer_service(client, conf, services):
try:
services = client.list_services(**{'type': 'metering'})
except exceptions.Forbidden:
LOG.warning("User has no permissions to list services - "
"metering service can't be discovered.")
return
if services and len(services['services']):
metering = services['services'][0]
if 'ceilometer' in metering['name'] and metering['enabled']:
conf.set('service_available', 'ceilometer', 'True')
def check_volume_backup_service(client, conf, services):
"""Verify if the cinder backup service is enabled"""
if 'volumev3' not in services:
LOG.info("No volume service found, skipping backup service check")
return
try:
params = {'binary': 'cinder-backup'}
backup_service = client.list_services(**params)
except exceptions.Forbidden:
LOG.warning("User has no permissions to list services - "
"cinder-backup service can't be discovered.")
return
if backup_service:
# We only set backup to false if the service isn't running otherwise we
# keep the default value
service = backup_service['services']
if not service or service[0]['state'] == 'down':
conf.set('volume-feature-enabled', 'backup', 'False')
def find_or_upload_image(client, image_id, image_name, allow_creation,
image_source='', image_dest='', disk_format=''):
image = _find_image(client, image_id, image_name)
if not image and not allow_creation:
raise Exception("Image '%s' not found, but resource creation"
" isn't allowed. Either use '--create' or provide"
" an existing image_ref" % image_name)
if image:
LOG.info("(no change) Found image '%s'", image['name'])
path = os.path.abspath(image_dest)
if not os.path.isfile(path):
_download_image(client, image['id'], path)
else:
LOG.info("Creating image '%s'", image_name)
if image_source.startswith("http:") or \
image_source.startswith("https:"):
_download_file(image_source, image_dest)
else:
shutil.copyfile(image_source, image_dest)
image = _upload_image(client, image_name, image_dest, disk_format)
return image['id']
def create_tempest_networks(clients, conf, has_neutron, public_network_id):
label = None
public_network_name = None
# TODO(tkammer): separate logic to different func of Nova network
# vs Neutron
if has_neutron:
client = clients.get_neutron_client()
# if user supplied the network we should use
if public_network_id:
LOG.info("Looking for existing network id: {0}"
"".format(public_network_id))
# check if network exists
network_list = client.list_networks()
for network in network_list['networks']:
if network['id'] == public_network_id:
public_network_name = network['name']
break
else:
raise ValueError('provided network id: {0} was not found.'
''.format(public_network_id))
# no network id provided, try to auto discover a public network
else:
LOG.info("No network supplied, trying auto discover for network")
network_list = client.list_networks()
for network in network_list['networks']:
if network['router:external'] and network['subnets']:
LOG.info("Found network, using: {0}".format(network['id']))
public_network_id = network['id']
public_network_name = network['name']
break
# Couldn't find an existing external network
else:
LOG.error("No external networks found. "
"Please note that any test that relies on external "
"connectivity would most likely fail.")
if public_network_id is not None:
conf.set('network', 'public_network_id', public_network_id)
if public_network_name is not None:
conf.set('network', 'floating_network_name', public_network_name)
else:
client = clients.get_nova_net_client()
networks = client.list_networks()
if networks:
label = networks['networks'][0]['label']
if label:
conf.set('compute', 'fixed_network_name', label)
elif not has_neutron:
raise Exception('fixed_network_name could not be discovered and'
' must be specified')
def configure_keystone_feature_flags(conf, services):
"""Set keystone feature flags based upon version ID."""
supported_versions = services.get('identity', {}).get('versions', [])
if len(supported_versions) <= 1:
return
for version in supported_versions:
major, minor = version.split('.')[:2]
# Enable the domain specific roles feature flag. For more information,
# see https://developer.openstack.org/api-ref/identity/v3
if major == 'v3' and int(minor) >= 6:
conf.set('identity-feature-enabled',
'forbid_global_implied_dsr',
'True')
def configure_boto(conf, services):
"""Set boto URLs based on discovered APIs."""
if 'ec2' in services:
conf.set('boto', 'ec2_url', services['ec2']['url'])
if 's3' in services:
conf.set('boto', 's3_url', services['s3']['url'])
def configure_horizon(conf):
"""Derive the horizon URIs from the identity's URI."""
uri = conf.get('identity', 'uri')
u = urllib2.urlparse.urlparse(uri)
base = '%s://%s%s' % (u.scheme, u.netloc.replace(
':' + str(u.port), ''), '/dashboard')
assert base.startswith('http:') or base.startswith('https:')
has_horizon = True
try:
urllib2.urlopen(base)
except urllib2.URLError:
has_horizon = False
conf.set('service_available', 'horizon', str(has_horizon))
conf.set('dashboard', 'dashboard_url', base + '/')
conf.set('dashboard', 'login_url', base + '/auth/login/')
def configure_discovered_services(conf, services):
"""Set service availability and supported extensions and versions.
Set True/False per service in the [service_available] section of `conf`
depending of wheter it is in services. In the [<service>-feature-enabled]
section, set extensions and versions found in `services`.
:param conf: ConfigParser configuration
:param services: dictionary of discovered services - expects each service
to have a dictionary containing 'extensions' and 'versions' keys
"""
# check if volume service is disabled
if conf.has_section('services') and conf.has_option('services', 'volume'):
if not conf.getboolean('services', 'volume'):
SERVICE_NAMES.pop('volume')
SERVICE_VERSIONS.pop('volume')
# set service availability
for service, codename in SERVICE_NAMES.iteritems():
# ceilometer is still transitioning from metering to telemetry
if service == 'telemetry' and 'metering' in services:
service = 'metering'
conf.set('service_available', codename, str(service in services))
# TODO(arxcruz): Remove this once/if we get the following reviews merged
# in all branches supported by tempestconf, or once/if tempestconf do not
# support anymore the OpenStack release where those patches are not
# available.
# https://review.openstack.org/#/c/492526/
# https://review.openstack.org/#/c/492525/
if 'alarming' in services:
conf.set('service_available', 'aodh', 'True')
conf.set('service_available', 'aodh_plugin', 'True')
# set supported API versions for services with more of them
for service, service_info in SERVICE_VERSIONS.iteritems():
supported_versions = services.get(
service_info['catalog'], {}).get('versions', [])
section = service + '-feature-enabled'
for version in service_info['supported_versions']:
is_supported = any(version in item
for item in supported_versions)
conf.set(section, 'api_' + version, str(is_supported))
# set service extensions
keystone_v3_support = conf.get('identity-feature-enabled', 'api_v3')
for service, ext_key in SERVICE_EXTENSION_KEY.iteritems():
if service in services:
extensions = ','.join(services[service].get('extensions', ""))
if service == 'object-store':
# tempest.conf is inconsistent and uses 'object-store' for the
# catalog name but 'object-storage-feature-enabled'
service = 'object-storage'
elif service == 'identity' and keystone_v3_support:
identity_v3_ext = api_discovery.get_identity_v3_extensions(
conf.get("identity", "uri_v3"))
extensions = list(set(extensions.split(',') + identity_v3_ext))
extensions = ','.join(extensions)
conf.set(service + '-feature-enabled', ext_key, extensions)
def _download_file(url, destination):
if os.path.exists(destination):
LOG.info("Image '%s' already fetched to '%s'.", url, destination)
return
LOG.info("Downloading '%s' and saving as '%s'", url, destination)
f = urllib2.urlopen(url)
data = f.read()
with open(destination, "wb") as dest:
dest.write(data)
def _download_image(client, id, path):
"""Download file from glance."""
LOG.info("Downloading image %s to %s", id, path)
body = client.show_image_file(id)
LOG.debug(type(body.data))
with open(path, 'wb') as out:
out.write(body.data)
def _upload_image(client, name, path, disk_format):
"""Upload image file from `path` into Glance with `name."""
LOG.info("Uploading image '%s' from '%s'", name, os.path.abspath(path))
with open(path) as data:
image = client.create_image(name=name,
disk_format=disk_format,
container_format='bare',
visibility="public")
client.store_image_file(image['id'], data)
return image
def _find_image(client, image_id, image_name):
"""Find image by ID or name (the image client doesn't have this)."""
if image_id:
try:
return client.show_image(image_id)
except exceptions.NotFound:
pass
found = filter(lambda x: x['name'] == image_name,
client.list_images()['images'])
if found:
return found[0]
else:
return None
def main(): def main():
args = parse_arguments() args = parse_arguments()
args.remove = parse_values_to_remove(args.remove)
set_logging(args.debug, args.verbose) set_logging(args.debug, args.verbose)
conf = tempest_conf.TempestConf() conf = tempest_conf.TempestConf()
@ -785,46 +305,35 @@ def main():
set_options(conf, args.deployer_input, args.non_admin, set_options(conf, args.deployer_input, args.non_admin,
args.overrides, args.test_accounts, cloud_creds) args.overrides, args.test_accounts, cloud_creds)
uri = conf.get("identity", "uri")
api_version = 2
if "v3" in uri:
api_version = 3
conf.set("identity", "auth_version", "v3")
conf.set("identity", "uri_v3", uri)
else:
# TODO(arxcruz) make a check if v3 is enabled
conf.set("identity", "uri_v3", uri.replace("v2.0", "v3"))
credentials = Credentials(conf, not args.non_admin) credentials = Credentials(conf, not args.non_admin)
clients = ClientManager(conf, credentials) clients = ClientManager(conf, credentials)
swift_discover = conf.get_defaulted('object-storage-feature-enabled', services = Services(clients, conf, credentials)
'discoverability')
services = api_discovery.discover(
clients.auth_provider,
clients.identity_region,
object_store_discovery=conf.get_bool_value(swift_discover),
api_version=api_version,
disable_ssl_certificate_validation=conf.get_defaulted(
'identity',
'disable_ssl_certificate_validation'
)
)
if args.create and args.test_accounts is None: if args.create and args.test_accounts is None:
create_tempest_users(clients.tenants, clients.roles, clients.users, users = Users(clients.tenants, clients.roles, clients.users, conf)
conf, services) users.create_tempest_users(services.is_service('orchestration'))
create_tempest_flavors(clients.flavors, conf, args.create) flavors = Flavors(clients.flavors, args.create, conf)
create_tempest_images(clients.images, conf, args.image, args.create, flavors.create_tempest_flavors()
image = services.get_service('image')
image.set_image_preferences(args.create, args.image,
args.image_disk_format) args.image_disk_format)
has_neutron = "network" in services image.create_tempest_images(conf)
LOG.info("Setting up network") has_neutron = services.is_service("network")
LOG.debug("Is neutron present: {0}".format(has_neutron)) network = services.get_service("network")
create_tempest_networks(clients, conf, has_neutron, args.network_id) network.create_tempest_networks(has_neutron, conf, args.network_id)
configure_discovered_services(conf, services) services.set_service_availability()
check_volume_backup_service(clients.volume_service, conf, services) services.set_supported_api_versions()
check_ceilometer_service(clients.service_client, conf, services) services.set_service_extensions()
configure_boto(conf, services) volume.check_volume_backup_service(conf, clients.volume_client,
configure_keystone_feature_flags(conf, services) services.is_service("volumev3"))
ceilometer.check_ceilometer_service(conf, clients.service_client)
boto.configure_boto(conf,
s3_service=services.get_service("s3"))
identity = services.get_service('identity')
identity.configure_keystone_feature_flags(conf)
configure_horizon(conf) configure_horizon(conf)
# remove all unwanted values if were specified # remove all unwanted values if were specified

View File

View File

@ -0,0 +1,97 @@
# Copyright 2013 Red Hat, 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 json
import re
import urllib3
import urlparse
from config_tempest.constants import LOG
MULTIPLE_SLASH = re.compile(r'/+')
class ServiceError(Exception):
pass
class Service(object):
def __init__(self, name, service_url, token, disable_ssl_validation,
client=None):
self.name = name
self.service_url = service_url
self.headers = {'Accept': 'application/json', 'X-Auth-Token': token}
self.disable_ssl_validation = disable_ssl_validation
self.client = client
self.extensions = []
self.versions = []
def do_get(self, url, top_level=False, top_level_path=""):
parts = list(urlparse.urlparse(url))
# 2 is the path offset
if top_level:
parts[2] = '/' + top_level_path
parts[2] = MULTIPLE_SLASH.sub('/', parts[2])
url = urlparse.urlunparse(parts)
try:
if self.disable_ssl_validation:
urllib3.disable_warnings()
http = urllib3.PoolManager(cert_reqs='CERT_NONE')
else:
http = urllib3.PoolManager()
r = http.request('GET', url, headers=self.headers)
except Exception as e:
LOG.error("Request on service '%s' with url '%s' failed",
(self.name, url))
raise e
if r.status >= 400:
raise ServiceError("Request on service '%s' with url '%s' failed"
" with code %d" % (self.name, url, r.status))
return r.data
def set_extensions(self):
self.extensions = []
def set_versions(self):
self.versions = []
def get_extensions(self):
return self.extensions
def get_versions(self):
return self.versions
class VersionedService(Service):
def set_versions(self, top_level=True):
body = self.do_get(self.service_url, top_level=top_level)
body = json.loads(body)
self.versions = self.deserialize_versions(body)
def deserialize_versions(self, body):
return map(lambda x: x['id'], body['versions'])
def no_port_cut_url(self):
# if there is no port defined, cut the url from version to the end
u = urllib3.util.parse_url(self.service_url)
url = self.service_url
if u.port is None:
found = re.findall(r'v\d', url)
if len(found) > 0:
index = url.index(found[0])
url = self.service_url[:index]
return (url, u.port is not None)

View File

@ -0,0 +1,26 @@
# Copyright 2016 Red Hat, 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.
def configure_boto(conf, ec2_service=None, s3_service=None):
"""Set boto URLs based on discovered APIs.
:type ec2_service: config_tempest.services.base.Service
:type s3_service: config_tempest.services.base.Service
"""
if ec2_service:
conf.set('boto', 'ec2_url', ec2_service.service_url)
if s3_service:
conf.set('boto', 's3_url', s3_service.service_url)

View File

@ -0,0 +1,36 @@
# Copyright 2016 Red Hat, 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 config_tempest.constants as C
from tempest.lib import exceptions
def check_ceilometer_service(conf, service_client):
"""If a metering service is available, set it to conf
:type conf: TempestConf object
:type service_client: Tempest's identity.v3.services_client.ServicesClient
"""
try:
params = {'type': 'metering'}
services = service_client.list_services(**params)
except exceptions.Forbidden:
C.LOG.warning("User has no permissions to list services - "
"metering service can't be discovered.")
return
if services and len(services['services']):
metering = services['services'][0]
if 'ceilometer' in metering['name'] and metering['enabled']:
conf.set('service_available', 'ceilometer', 'True')

View File

@ -0,0 +1,30 @@
# Copyright 2013 Red Hat, 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.
from base import VersionedService
import json
class ComputeService(VersionedService):
def set_extensions(self):
body = self.do_get(self.service_url + '/extensions')
body = json.loads(body)
self.extensions = map(lambda x: x['alias'], body['extensions'])
def set_versions(self):
url, top_level = self.no_port_cut_url()
body = self.do_get(url, top_level=top_level)
body = json.loads(body)
self.versions = self.deserialize_versions(body)

View File

@ -0,0 +1,33 @@
# Copyright 2016 Red Hat, 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 urllib2
def configure_horizon(conf):
"""Derive the horizon URIs from the identity's URI."""
uri = conf.get('identity', 'uri')
u = urllib2.urlparse.urlparse(uri)
base = '%s://%s%s' % (u.scheme, u.netloc.replace(
':' + str(u.port), ''), '/dashboard')
assert base.startswith('http:') or base.startswith('https:')
has_horizon = True
try:
urllib2.urlopen(base)
except urllib2.URLError:
has_horizon = False
conf.set('service_available', 'horizon', str(has_horizon))
conf.set('dashboard', 'dashboard_url', base + '/')
conf.set('dashboard', 'login_url', base + '/auth/login/')

View File

@ -0,0 +1,112 @@
# Copyright 2013 Red Hat, 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 json
import requests
import urlparse
from base import VersionedService
from config_tempest.constants import LOG
class IdentityService(VersionedService):
def __init__(self, name, service_url, token, disable_ssl_validation,
client=None):
super(IdentityService, self).__init__(
name, service_url, token, disable_ssl_validation, client)
self.extensions_v3 = []
version = ''
if 'v2' in self.service_url:
version = '/v2.0'
url_parse = urlparse.urlparse(self.service_url)
self.service_url = '{}://{}{}'.format(url_parse.scheme,
url_parse.netloc, version)
def set_extensions(self):
if 'v2' in self.service_url:
body = self.do_get(self.service_url + '/extensions')
body = json.loads(body)
values = body['extensions']['values']
self.extensions = map(lambda x: x['alias'], values)
return
# Keystone api changed in v3, the concept of extensions changed. Right
# now, all the existing extensions are part of keystone core api, so,
# there's no longer the /extensions endpoint. The extensions that are
# stable, are enabled by default, the ones marked as experimental are
# disabled by default. Checking the tempest source, there's no test
# pointing to extensions endpoint, so I am very confident that this
# will not be an issue. If so, we need to list all the /OS-XYZ
# extensions to identify what is enabled or not. This would be a manual
# check every time keystone change, add or delete an extension, so I
# rather prefer to set empty list here for now.
self.extensions = []
def set_identity_v3_extensions(self):
"""Returns discovered identity v3 extensions
As keystone V3 uses a JSON Home to store the extensions.
This method implements a different discovery method.
:param keystone_v3_url: Keystone V3 auth url
:return: A list with the discovered extensions
"""
try:
r = requests.get(self.service_url,
verify=False,
headers={'Accept': 'application/json-home'})
except requests.exceptions.RequestException as re:
LOG.error("Request on service '%s' with url '%s' failed",
'identity', self.service_url)
raise re
ext_h = 'http://docs.openstack.org/api/openstack-identity/3/ext/'
res = [x for x in json.loads(r.content)['resources'].keys()]
ext = [ex for ex in res if 'ext' in ex]
ext = [str(e).replace(ext_h, '').split('/')[0] for e in ext]
self.extensions_v3 = list(set(ext))
def set_versions(self):
super(IdentityService, self).set_versions(top_level=False)
def get_extensions(self):
all_ext_lst = self.extensions + self.extensions_v3
return list(set(all_ext_lst))
def deserialize_versions(self, body):
try:
versions = []
for v in body['versions']['values']:
# TripleO is in transition to v3 only, so the environment
# still returns v2 versions even though they're deprecated.
# Therefor pick only versions with stable status.
if v['status'] == 'stable':
versions.append(v['id'])
return versions
except KeyError:
return [body['version']['id']]
def configure_keystone_feature_flags(self, conf):
"""Set keystone feature flags based upon version ID."""
supported_versions = self.get_versions()
if len(supported_versions) <= 1:
return
for version in supported_versions:
major, minor = version.split('.')[:2]
# Enable the domain specific roles feature flag.
# For more information see:
# https://developer.openstack.org/api-ref/identity/v3
if major == 'v3' and int(minor) >= 6:
conf.set('identity-feature-enabled',
'forbid_global_implied_dsr',
'True')

View File

@ -0,0 +1,168 @@
# Copyright 2013 Red Hat, 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 os
import shutil
import urllib2
from base import VersionedService
from config_tempest.constants import LOG
from tempest.lib import exceptions
class ImageService(VersionedService):
def __init__(self, name, service_url, token, disable_ssl_validation,
client=None):
super(ImageService, self).__init__(name, service_url, token,
disable_ssl_validation,
client)
self.allow_creation = False
self.image_path = ""
self.disk_format = ""
def set_image_preferences(self, allow_creation, image_path, disk_format):
"""Sets image prefferences.
:type allow_creation: boolean
:type image_path: string
:type disk_format: string
"""
self.allow_creation = allow_creation
self.image_path = image_path
self.disk_format = disk_format
def set_versions(self):
super(ImageService, self).set_versions(top_level=False)
def create_tempest_images(self, conf):
"""Uploads an image to the glance.
The method creates images specified in conf, if they're not created
already. Then it sets their IDs to the conf.
:type conf: TempestConf object
"""
img_path = os.path.join(conf.get("scenario", "img_dir"),
os.path.basename(self.image_path))
name = self.image_path[self.image_path.rfind('/') + 1:]
conf.set('scenario', 'img_file', name)
alt_name = name + "_alt"
image_id = None
if conf.has_option('compute', 'image_ref'):
image_id = conf.get('compute', 'image_ref')
image_id = self.find_or_upload_image(image_id, name,
image_source=self.image_path,
image_dest=img_path)
alt_image_id = None
if conf.has_option('compute', 'image_ref_alt'):
alt_image_id = conf.get('compute', 'image_ref_alt')
alt_image_id = self.find_or_upload_image(alt_image_id, alt_name,
image_source=self.image_path,
image_dest=img_path)
conf.set('compute', 'image_ref', image_id)
conf.set('compute', 'image_ref_alt', alt_image_id)
def find_or_upload_image(self, image_id, image_name, image_source='',
image_dest=''):
"""If the image is not found, uploads it.
:type image_id: string
:type image_name: string
:type image_source: string
:type image_dest: string
"""
image = self._find_image(image_id, image_name)
if not image and not self.allow_creation:
raise Exception("Image '%s' not found, but resource creation"
" isn't allowed. Either use '--create' or provide"
" an existing image_ref" % image_name)
if image:
LOG.info("(no change) Found image '%s'", image['name'])
path = os.path.abspath(image_dest)
if not os.path.isfile(path):
self._download_image(image['id'], path)
else:
LOG.info("Creating image '%s'", image_name)
if image_source.startswith("http:") or \
image_source.startswith("https:"):
self._download_file(image_source, image_dest)
else:
shutil.copyfile(image_source, image_dest)
image = self._upload_image(image_name, image_dest)
return image['id']
def _find_image(self, image_id, image_name):
"""Find image by ID or name (the image client doesn't have this).
:type image_id: string
:type image_name: string
"""
if image_id:
try:
return self.client.show_image(image_id)
except exceptions.NotFound:
pass
found = filter(lambda x: x['name'] == image_name,
self.client.list_images()['images'])
if found:
return found[0]
else:
return None
def _upload_image(self, name, path):
"""Upload image file from `path` into Glance with `name`.
:type name: string
:type path: string
"""
LOG.info("Uploading image '%s' from '%s'", name, os.path.abspath(path))
with open(path) as data:
image = self.client.create_image(name=name,
disk_format=self.disk_format,
container_format='bare',
visibility="public")
self.client.store_image_file(image['id'], data)
return image
def _download_image(self, id, path):
"""Download image from glance.
:type id: string
:type path: string
"""
LOG.info("Downloading image %s to %s", id, path)
body = self.client.show_image_file(id)
LOG.debug(type(body.data))
with open(path, 'wb') as out:
out.write(body.data)
def _download_file(self, url, destination):
"""Downloads a file specified by `url` to `destination`.
:type url: string
:type destination: string
"""
if os.path.exists(destination):
LOG.info("Image '%s' already fetched to '%s'.", url, destination)
return
LOG.info("Downloading '%s' and saving as '%s'", url, destination)
f = urllib2.urlopen(url)
data = f.read()
with open(destination, "wb") as dest:
dest.write(data)

View File

@ -0,0 +1,90 @@
# Copyright 2013, 2016 Red Hat, 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 json
from base import VersionedService
from config_tempest.constants import LOG
class NetworkService(VersionedService):
def set_extensions(self):
body = self.do_get(self.service_url + '/v2.0/extensions.json')
body = json.loads(body)
self.extensions = map(lambda x: x['alias'], body['extensions'])
def create_tempest_networks(self, has_neutron, conf, network_id):
LOG.info("Setting up network")
LOG.debug("Is neutron present: {0}".format(has_neutron))
if has_neutron:
self.client = self.client.get_neutron_client()
self.create_tempest_networks_neutron(conf, network_id)
else:
self.client = self.client.get_nova_net_client()
self.create_tempest_networks_nova(conf)
def create_tempest_networks_neutron(self, conf, public_network_id):
self._public_network_name = None
self._public_network_id = public_network_id
# if user supplied the network we should use
if public_network_id:
self._supplied_network()
# no network id provided, try to auto discover a public network
else:
self._discover_network()
if self._public_network_id is not None:
conf.set('network', 'public_network_id', self._public_network_id)
if self._public_network_name is not None:
conf.set('network', 'floating_network_name',
self._public_network_name)
def _supplied_network(self):
LOG.info("Looking for existing network id: {0}"
"".format(self._public_network_id))
# check if network exists
network_list = self.client.list_networks()
for network in network_list['networks']:
if network['id'] == self._public_network_id:
self._public_network_name = network['name']
break
else:
raise ValueError('provided network id: {0} was not found.'
''.format(self._public_network_id))
def _discover_network(self):
LOG.info("No network supplied, trying auto discover for network")
network_list = self.client.list_networks()
for network in network_list['networks']:
if network['router:external'] and network['subnets']:
LOG.info("Found network, using: {0}".format(network['id']))
self._public_network_id = network['id']
self._public_network_name = network['name']
break
# Couldn't find an existing external network
else:
LOG.error("No external networks found. "
"Please note that any test that relies on external "
"connectivity would most likely fail.")
def create_tempest_networks_nova(self, conf):
networks = self.client.list_networks()
if networks:
label = networks['networks'][0]['label']
if label:
conf.set('compute', 'fixed_network_name', label)
else:
raise Exception('fixed_network_name could not be '
'discovered and must be specified')

View File

@ -0,0 +1,31 @@
# Copyright 2016 Red Hat, 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 json
from base import Service
class ObjectStorageService(Service):
def set_extensions(self, object_store_discovery=False):
if not object_store_discovery:
self.extensions = []
elif 'v3' not in self.service_url: # it's not a v3 url
body = self.do_get(self.service_url, top_level=True,
top_level_path="info")
body = json.loads(body)
# Remove Swift general information from extensions list
body.pop('swift')
self.extensions = body.keys()

View File

@ -0,0 +1,204 @@
# Copyright 2013, 2016, 2018 Red Hat, 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 urlparse
from base import Service
from compute import ComputeService
import config_tempest.constants as C
from identity import IdentityService
from image import ImageService
from network import NetworkService
from object_storage import ObjectStorageService
from volume import VolumeService
service_dict = {'compute': ComputeService,
'image': ImageService,
'network': NetworkService,
'object-store': ObjectStorageService,
'volumev3': VolumeService,
'identity': IdentityService}
class Services(object):
def __init__(self, clients, conf, creds):
self._clients = clients
self._conf = conf
self._creds = creds
swift_discover = conf.get_defaulted('object-storage-feature-enabled',
'discoverability')
self._object_store_discovery = conf.get_bool_value(swift_discover)
self._ssl_validation = creds.disable_ssl_certificate_validation
self._region = clients.identity_region
self._services = []
self.set_catalog_and_url()
self.discover()
def discover(self):
token, auth_data = self._clients.auth_provider.get_auth()
for entry in auth_data[self.service_catalog]:
name = entry['type']
ep = self.get_endpoints(entry)
url = ep[self.public_url]
if 'identity' in url:
url = self.edit_identity_url(ep[self.public_url])
service_class = self.get_service_class(name)
service = service_class(name, url, token, self._ssl_validation,
self._clients.get_service_client(name))
# discover extensions of the service
if name == 'object-store':
service.set_extensions(self._object_store_discovery)
else:
service.set_extensions()
# discover versions of the service
service.set_versions()
self._services.append(service)
def get_endpoints(self, entry):
for ep in entry['endpoints']:
if self._creds.api_version == 3:
if (ep['region'] == self._region and
ep['interface'] == 'public'):
return ep
else:
if ep['region'] == self._region:
return ep
try:
return entry['endpoints'][0]
except IndexError:
return []
def set_catalog_and_url(self):
if self._creds.api_version == 3:
self.service_catalog = 'catalog'
self.public_url = 'url'
else:
self.service_catalog = 'serviceCatalog'
self.public_url = 'publicURL'
def edit_identity_url(self, url):
"""A port and identity version are added to url if contains 'identity'
:param url: url address of an endpoint
:type url: string
:rtype: string
"""
# self._clients.auth_provider.auth_url stores identity.uri(_v3) value
# from TempestConf
port = urlparse.urlparse(self._clients.auth_provider.auth_url).port
if port is None:
port = ""
else:
port = ":" + str(port)
replace_text = port + "/identity/" + self._creds.identity_version
return url.replace("/identity", replace_text)
def get_service_class(self, name):
"""Returns class name by the service name
:param name: Codename of a service
:type name: string
:return: Class name of the service
:rtype: string
"""
return service_dict.get(name, Service)
def get_service(self, name):
"""Finds and returns a service object
:param name: Codename of a service
:type name: string
:return: Service object
"""
for service in self._services:
if service.name == name:
return service
return None
def is_service(self, name):
"""Returns true if a service is available, false otherwise
:param name: Codename of a service
:type name: string
:rtype: boolean
"""
if self.get_service(name) is None:
return False
return True
def set_service_availability(self):
# check if volume service is disabled
if self._conf.has_option('services', 'volume'):
if not self._conf.getboolean('services', 'volume'):
C.SERVICE_NAMES.pop('volume')
C.SERVICE_VERSIONS.pop('volume')
for service, codename in C.SERVICE_NAMES.iteritems():
# ceilometer is still transitioning from metering to telemetry
if service == 'telemetry' and self.is_service('metering'):
service = 'metering'
available = str(self.is_service(service))
self._conf.set('service_available', codename, available)
# TODO(arxcruz): Remove this once/if we get the following reviews
# merged in all branches supported by tempestconf, or once/if
# tempestconf do not support anymore the OpenStack release where
# those patches are not available.
# https://review.openstack.org/#/c/492526/
# https://review.openstack.org/#/c/492525/
if self.is_service('alarming'):
self._conf.set('service_available', 'aodh', 'True')
self._conf.set('service_available', 'aodh_plugin', 'True')
def set_supported_api_versions(self):
# set supported API versions for services with more of them
for service, service_info in C.SERVICE_VERSIONS.iteritems():
service_object = self.get_service(service_info['catalog'])
if service_object is None:
supported_versions = []
else:
supported_versions = service_object.get_versions()
section = service + '-feature-enabled'
for version in service_info['supported_versions']:
is_supported = any(version in item
for item in supported_versions)
self._conf.set(section, 'api_' + version, str(is_supported))
def set_service_extensions(self):
postfix = "-feature-enabled"
keystone_v3_support = self._conf.get('identity' + postfix, 'api_v3')
if keystone_v3_support:
self.get_service('identity').set_identity_v3_extensions()
for service, ext_key in C.SERVICE_EXTENSION_KEY.iteritems():
if not self.is_service(service):
continue
service_object = self.get_service(service)
if service_object is not None:
extensions = ','.join(service_object.get_extensions())
if service == 'object-store':
# tempest.conf is inconsistent and uses 'object-store' for
# the catalog name but 'object-storage-feature-enabled'
service = 'object-storage'
self._conf.set(service + postfix, ext_key, extensions)

View File

@ -0,0 +1,55 @@
# Copyright 2013, 2016 Red Hat, 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 json
from base import VersionedService
import config_tempest.constants as C
from tempest.lib import exceptions
class VolumeService(VersionedService):
def set_extensions(self):
body = self.do_get(self.service_url + '/extensions')
body = json.loads(body)
self.extensions = map(lambda x: x['alias'], body['extensions'])
def set_versions(self):
url, top_level = self.no_port_cut_url()
body = self.do_get(url, top_level=top_level)
body = json.loads(body)
self.versions = self.deserialize_versions(body)
def check_volume_backup_service(conf, volume_client, is_volumev3):
"""Verify if the cinder backup service is enabled"""
if not is_volumev3:
C.LOG.info("No volume service found, "
"skipping backup service check")
return
try:
params = {'binary': 'cinder-backup'}
is_backup = volume_client.list_services(**params)
except exceptions.Forbidden:
C.LOG.warning("User has no permissions to list services - "
"cinder-backup service can't be discovered.")
return
if is_backup:
# We only set backup to false if the service isn't running
# otherwise we keep the default value
service = is_backup['services']
if not service or service[0]['state'] == 'down':
conf.set('volume-feature-enabled', 'backup', 'False')

View File

@ -1,4 +1,4 @@
# Copyright 2018 Red Hat, Inc. # Copyright 2016, 2017 Red Hat, Inc.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -14,14 +14,12 @@
# under the License. # under the License.
import ConfigParser import ConfigParser
import logging
import sys import sys
from constants import LOG
from oslo_config import cfg from oslo_config import cfg
import tempest.config import tempest.config
LOG = logging.getLogger(__name__)
class TempestConf(ConfigParser.SafeConfigParser): class TempestConf(ConfigParser.SafeConfigParser):
# causes the config parser to preserve case of the options # causes the config parser to preserve case of the options

View File

@ -20,7 +20,6 @@ import json
import mock import mock
from oslotest import base from oslotest import base
from config_tempest import api_discovery as api
from config_tempest.clients import ClientManager from config_tempest.clients import ClientManager
from config_tempest.credentials import Credentials from config_tempest.credentials import Credentials
from config_tempest import tempest_conf from config_tempest import tempest_conf
@ -83,6 +82,8 @@ class BaseServiceTest(base.BaseTestCase):
"""Test case base class for all api_discovery unit tests""" """Test case base class for all api_discovery unit tests"""
FAKE_TOKEN = "s6d5f45sdf4s564f4s6464sdfsd514" FAKE_TOKEN = "s6d5f45sdf4s564f4s6464sdfsd514"
FAKE_CLIENT_MOCK = 'config_tempest.tests.base.BaseServiceTest' + \
'.FakeServiceClient'
FAKE_HEADERS = { FAKE_HEADERS = {
'Accept': 'application/json', 'X-Auth-Token': FAKE_TOKEN 'Accept': 'application/json', 'X-Auth-Token': FAKE_TOKEN
} }
@ -131,6 +132,14 @@ class BaseServiceTest(base.BaseTestCase):
} }
} }
) )
FAKE_IDENTITY_VERSION = (
{
'version': {
'status': 'stable',
'id': 'v3.8',
}
}
)
FAKE_EXTENSIONS = ( FAKE_EXTENSIONS = (
{ {
"extensions": [{ "extensions": [{
@ -202,22 +211,54 @@ class BaseServiceTest(base.BaseTestCase):
def __init__(self): def __init__(self):
self.content = json.dumps(self.FAKE_V3_EXTENSIONS) self.content = json.dumps(self.FAKE_V3_EXTENSIONS)
class FakeServiceClient(object):
def __init__(self, services=None):
self.client = mock.Mock()
self.return_value = mock.Mock()
self.services = services
def list_networks(self):
return self.return_value
def list_services(self, **kwargs):
return self.services
def _fake_service_do_get_method(self, fake_data): def _fake_service_do_get_method(self, fake_data):
function2mock = 'config_tempest.api_discovery.Service.do_get' function2mock = 'config_tempest.services.base.Service.do_get'
do_get_output = json.dumps(fake_data) do_get_output = json.dumps(fake_data)
mocked_do_get = mock.Mock() mocked_do_get = mock.Mock()
mocked_do_get.return_value = do_get_output mocked_do_get.return_value = do_get_output
self.useFixture(MonkeyPatch(function2mock, mocked_do_get)) self.useFixture(MonkeyPatch(function2mock, mocked_do_get))
def _test_get_service_class(self, service, cls): def _set_get_extensions(self, service, expected_resp, fake_data):
resp = api.get_service_class(service) # mock do_get response
self.assertEqual(resp, cls)
def _get_extensions(self, service, expected_resp, fake_data):
self._fake_service_do_get_method(fake_data) self._fake_service_do_get_method(fake_data)
# set the fake extensions
service.set_extensions()
# check if extensions were set
self.assertItemsEqual(service.extensions, expected_resp)
# check if get method returns the right data
resp = service.get_extensions() resp = service.get_extensions()
self.assertItemsEqual(resp, expected_resp) self.assertItemsEqual(resp, expected_resp)
def _set_get_versions(self, service, expected_resp, fake_data):
# mock do_get response
self._fake_service_do_get_method(fake_data)
# set the fake versions
service.set_versions()
# check if versions were set
self.assertItemsEqual(service.versions, expected_resp)
# check if get method returns the right data
resp = service.get_versions()
self.assertItemsEqual(resp, expected_resp)
def _test_deserialize_versions(self, service, expected_resp, fake_data): def _test_deserialize_versions(self, service, expected_resp, fake_data):
resp = service.deserialize_versions(fake_data) resp = service.deserialize_versions(fake_data)
self.assertItemsEqual(resp, expected_resp) self.assertItemsEqual(resp, expected_resp)
def _assert_conf_get_not_raises(self, exc, section, value):
try:
self.conf.get(section, value)
except exc:
return
self.assertTrue(False)

View File

@ -0,0 +1,105 @@
# Copyright 2018 Red Hat, 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 mock
import unittest
from config_tempest.services.base import Service
from config_tempest.services.base import VersionedService
from config_tempest.tests.base import BaseServiceTest
class TestService(BaseServiceTest):
def setUp(self):
super(TestService, self).setUp()
self.Service = Service("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def _mocked_do_get(self, mock_urllib3):
mock_http = mock_urllib3.PoolManager()
expected_resp = mock_http.request('GET',
self.FAKE_URL,
self.FAKE_HEADERS)
return expected_resp.data
@unittest.skip('Failing due to Storyboard: 2001245')
@mock.patch('config_tempest.api_discovery.urllib3')
def test_do_get(self, mock_urllib3):
resp = self.Service.do_get(self.FAKE_URL)
expected_resp = self._mocked_do_get(mock_urllib3)
self.assertEqual(resp, expected_resp)
def test_service_properties(self):
self.assertEqual(self.Service.name, "ServiceName")
self.assertEqual(self.Service.service_url, self.FAKE_URL)
self.assertEqual(self.Service.headers, self.FAKE_HEADERS)
self.assertEqual(self.Service.disable_ssl_validation, False)
self.assertEqual(self.Service.client, None)
self.assertEqual(self.Service.extensions, [])
self.assertEqual(self.Service.versions, [])
def test_set_extensions(self):
self.Service.extensions = ['ext']
self.Service.set_extensions()
self.assertEqual(self.Service.extensions, [])
def test_set_versions(self):
self.Service.versions = ['ver']
self.Service.set_versions()
self.assertEqual(self.Service.versions, [])
def test_get_extensions(self):
self.Service.extensions = ['ext']
self.assertEqual(self.Service.get_extensions(), ['ext'])
def test_get_versions(self):
self.Service.versions = ['ver']
self.assertEqual(self.Service.get_versions(), ['ver'])
class TestVersionedService(BaseServiceTest):
def setUp(self):
super(TestVersionedService, self).setUp()
self.Service = VersionedService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_set_get_versions(self):
expected_resp = ['v2.0', 'v2.1']
self._fake_service_do_get_method(self.FAKE_VERSIONS)
self.Service.set_versions()
resp = self.Service.get_versions()
self.assertItemsEqual(resp, expected_resp)
self.assertItemsEqual(self.Service.versions, expected_resp)
def test_deserialize_versions(self):
expected_resp = ['v2.0', 'v2.1']
self._test_deserialize_versions(self.Service,
expected_resp,
self.FAKE_VERSIONS)
def test_no_port_cut_url(self):
resp = self.Service.no_port_cut_url()
self.assertEqual(resp, (self.FAKE_URL, True))
url = "http://10.200.16.10"
self.Service.service_url = url
resp = self.Service.no_port_cut_url()
self.assertEqual(resp, (self.Service.service_url, False))
self.Service.service_url = url + "/v3/cut/it/off"
resp = self.Service.no_port_cut_url()
self.assertEqual(resp, (url + '/', False))

View File

@ -0,0 +1,47 @@
# Copyright 2018 Red Hat, 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 ConfigParser
from config_tempest.services import base
from config_tempest.services import boto
from config_tempest.tempest_conf import TempestConf
from config_tempest.tests.base import BaseServiceTest
class TestBotoService(BaseServiceTest):
def setUp(self):
super(TestBotoService, self).setUp()
self.conf = TempestConf()
self.es2 = base.Service("ec2",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
self.s3 = base.Service("s3",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_configure_boto(self):
boto.configure_boto(self.conf)
self._assert_conf_get_not_raises(ConfigParser.NoSectionError,
"boto",
"ec2_url")
self._assert_conf_get_not_raises(ConfigParser.NoSectionError,
"boto",
"s3_url")
boto.configure_boto(self.conf, self.es2, self.s3)
self.assertEqual(self.conf.get("boto", "ec2_url"), self.FAKE_URL)
self.assertEqual(self.conf.get("boto", "s3_url"), self.FAKE_URL)

View File

@ -0,0 +1,46 @@
# Copyright 2018 Red Hat, 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 ConfigParser
from config_tempest.services import ceilometer
from config_tempest.tempest_conf import TempestConf
from config_tempest.tests.base import BaseServiceTest
class TestCeilometerService(BaseServiceTest):
def setUp(self):
super(TestCeilometerService, self).setUp()
self.conf = TempestConf()
def test_check_ceilometer_service(self):
client_service_mock = self.FakeServiceClient(services={})
ceilometer.check_ceilometer_service(self.conf, client_service_mock)
self._assert_conf_get_not_raises(ConfigParser.NoSectionError,
"service_available",
"ceilometer")
client_service_mock = self.FakeServiceClient(services={
'services': [
{
"name": "ceilometer",
"enabled": True
}
]
})
ceilometer.check_ceilometer_service(self.conf, client_service_mock)
self.assertEqual(self.conf.get('service_available', 'ceilometer'),
'True')

View File

@ -0,0 +1,34 @@
# Copyright 2018 Red Hat, 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.
from config_tempest.services.compute import ComputeService
from config_tempest.tests.base import BaseServiceTest
class TestComputeService(BaseServiceTest):
def setUp(self):
super(TestComputeService, self).setUp()
self.Service = ComputeService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_set_get_extensions(self):
exp_resp = ['NMN', 'OS-DCF']
self._set_get_extensions(self.Service, exp_resp, self.FAKE_EXTENSIONS)
def test_set_get_versions(self):
exp_resp = ['v2.0', 'v2.1']
self._set_get_versions(self.Service, exp_resp, self.FAKE_VERSIONS)

View File

@ -0,0 +1,48 @@
# Copyright 2018 Red Hat, 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.
from fixtures import MonkeyPatch
import mock
from config_tempest.services import horizon
from config_tempest.tests.base import BaseConfigTempestTest
class TestConfigTempest(BaseConfigTempestTest):
def setUp(self):
super(TestConfigTempest, self).setUp()
self.conf = self._get_conf("v2.0", "v3")
def test_configure_horizon_ipv4(self):
mock_function = mock.Mock(return_value=True)
self.useFixture(MonkeyPatch('urllib2.urlopen', mock_function))
horizon.configure_horizon(self.conf)
self.assertEqual(self.conf.get('service_available', 'horizon'), "True")
self.assertEqual(self.conf.get('dashboard', 'dashboard_url'),
"http://172.16.52.151/dashboard/")
self.assertEqual(self.conf.get('dashboard', 'login_url'),
"http://172.16.52.151/dashboard/auth/login/")
def test_configure_horizon_ipv6(self):
mock_function = mock.Mock(return_value=True)
self.useFixture(MonkeyPatch('urllib2.urlopen', mock_function))
self.conf.set('identity', 'uri', 'http://[::1]:5000/v3', priority=True)
horizon.configure_horizon(self.conf)
self.assertEqual(self.conf.get('service_available', 'horizon'), "True")
self.assertEqual(self.conf.get('dashboard', 'dashboard_url'),
"http://[::1]/dashboard/")
self.assertEqual(self.conf.get('dashboard', 'login_url'),
"http://[::1]/dashboard/auth/login/")

View File

@ -0,0 +1,90 @@
# Copyright 2018 Red Hat, 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.
from fixtures import MonkeyPatch
import mock
from config_tempest.services.identity import IdentityService
from config_tempest.tempest_conf import TempestConf
from config_tempest.tests.base import BaseServiceTest
class TestIdentityService(BaseServiceTest):
def setUp(self):
super(TestIdentityService, self).setUp()
self.Service = IdentityService("ServiceName",
self.FAKE_URL + 'v2.0/',
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_set_extensions(self):
expected_resp = ['OS-DCF', 'NMN']
self._set_get_extensions(self.Service, expected_resp,
self.FAKE_IDENTITY_EXTENSIONS)
# try with URL containing v3 version
self.Service.service_url = self.FAKE_URL + 'v3'
expected_resp = []
self._set_get_extensions(self.Service, expected_resp,
self.FAKE_IDENTITY_EXTENSIONS)
def test_get_extensions(self):
exp_resp = ['OS-INHERIT', 'OS-OAUTH1',
'OS-SIMPLE-CERT', 'OS-EP-FILTER']
self.Service.extensions = exp_resp[:2]
self.Service.extensions_v3 = exp_resp[2:]
self.assertItemsEqual(self.Service.get_extensions(), exp_resp)
def test_set_identity_v3_extensions(self):
expected_resp = ['OS-INHERIT', 'OS-OAUTH1',
'OS-SIMPLE-CERT', 'OS-EP-FILTER']
fake_resp = self.FakeRequestResponse()
mocked_requests = mock.Mock()
mocked_requests.return_value = fake_resp
self.useFixture(MonkeyPatch('requests.get', mocked_requests))
self.Service.service_url = self.FAKE_URL + "v3"
self.Service.set_identity_v3_extensions()
self.assertItemsEqual(self.Service.extensions_v3, expected_resp)
self.assertItemsEqual(self.Service.get_extensions(), expected_resp)
def test_set_get_versions(self):
exp_resp = ['v3.8']
self._set_get_versions(self.Service, exp_resp,
self.FAKE_IDENTITY_VERSIONS)
def test_deserialize_versions(self):
expected_resp = ['v3.8']
self._test_deserialize_versions(self.Service,
expected_resp,
self.FAKE_IDENTITY_VERSIONS)
self._test_deserialize_versions(self.Service,
expected_resp,
self.FAKE_IDENTITY_VERSION)
expected_resp = ['v2.1', 'v3.8']
# add not deprecated v2 version to FAKE_IDENTITY_VERSIONS
v2 = {'status': 'stable', 'id': 'v2.1'}
self.FAKE_IDENTITY_VERSIONS['versions']['values'].append(v2)
self._test_deserialize_versions(self.Service,
expected_resp,
self.FAKE_IDENTITY_VERSIONS)
@mock.patch('config_tempest.services.identity'
'.IdentityService.get_versions')
def test_configure_keystone_feature_flags(self, mock_get_versions):
conf = TempestConf()
mock_get_versions.return_value = ['v3.8', 'v2.0']
self.Service.configure_keystone_feature_flags(conf)
self.assertEqual(
conf.get('identity-feature-enabled',
'forbid_global_implied_dsr'), 'True')

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*- # Copyright 2018 Red Hat, Inc.
# Copyright 2017 Red Hat, Inc.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -18,47 +16,86 @@
from fixtures import MonkeyPatch from fixtures import MonkeyPatch
import mock import mock
from config_tempest import main as tool from config_tempest.services.image import ImageService
from config_tempest.tests.base import BaseConfigTempestTest from config_tempest.tempest_conf import TempestConf
from config_tempest.tests.base import BaseServiceTest
class TestCreateTempestImages(BaseConfigTempestTest): class TestImageService(BaseServiceTest):
CLIENT_MOCK = 'tempest.lib.services.image.v2.images_client.ImagesClient'
IMAGES_LIST = [
{"status": "active", "name": "ImageName"},
{"status": "default", "name": "MyImage"},
{"status": "active", "name": "MyImage"}
]
def setUp(self): def setUp(self):
super(TestCreateTempestImages, self).setUp() super(TestImageService, self).setUp()
self.conf = self._get_conf("v2.0", "v3") self.Service = ImageService("ServiceName",
self.client = self._get_clients(self.conf).images self.FAKE_URL,
self.image_path = "my_path/my_image.qcow2" self.FAKE_TOKEN,
self.allow_creation = False disable_ssl_validation=False)
self.disk_format = ".format" self.Service.allow_creation = False
self.Service.image_path = "my_path/my_image.qcow2"
self.Service.disk_format = ".format"
self.Service.client = self.FakeServiceClient()
self.dir = "/img/" self.dir = "/img/"
self.conf = TempestConf()
self.conf.set("scenario", "img_dir", self.dir) self.conf.set("scenario", "img_dir", self.dir)
@mock.patch('config_tempest.main.find_or_upload_image') @mock.patch('config_tempest.services.image.ImageService'
def test_create_tempest_images_exception(self, mock_find_upload): '.find_or_upload_image')
mock_find_upload.side_effect = Exception
exc = Exception
self.assertRaises(exc,
tool.create_tempest_images,
client=self.client,
conf=self.conf,
image_path=self.image_path,
allow_creation=self.allow_creation,
disk_format=self.disk_format)
@mock.patch('config_tempest.main.find_or_upload_image')
def _test_create_tempest_images(self, mock_find_upload): def _test_create_tempest_images(self, mock_find_upload):
mock_find_upload.side_effect = ["id_c", "id_d"] mock_find_upload.side_effect = ["id_c", "id_d"]
tool.create_tempest_images(client=self.client, self.Service.create_tempest_images(conf=self.conf)
conf=self.conf,
image_path=self.image_path,
allow_creation=self.allow_creation,
disk_format=self.disk_format)
self.assertEqual(self.conf.get('compute', 'image_ref'), 'id_c') self.assertEqual(self.conf.get('compute', 'image_ref'), 'id_c')
self.assertEqual(self.conf.get('compute', 'image_ref_alt'), 'id_d') self.assertEqual(self.conf.get('compute', 'image_ref_alt'), 'id_d')
self.assertEqual(self.conf.get('scenario', 'img_file'), self.assertEqual(self.conf.get('scenario', 'img_file'),
'my_image.qcow2') 'my_image.qcow2')
@mock.patch('config_tempest.services.image.ImageService._find_image')
@mock.patch('config_tempest.services.image.ImageService._download_file')
@mock.patch('config_tempest.services.image.ImageService._upload_image')
def _test_find_or_upload_image_not_found_creation_allowed_format(
self, mock_upload_image,
mock_download_file, mock_find_image, format):
mock_find_image.return_value = None
mock_upload_image.return_value = {"id": "my_fake_id"}
image_source = format + "://any_random_url"
image_dest = "my_dest"
image_name = "my_image"
self.Service.allow_creation = True
image_id = self.Service.find_or_upload_image(
image_id=None, image_dest=image_dest,
image_name=image_name, image_source=image_source)
mock_download_file.assert_called_with(image_source, image_dest)
mock_upload_image.assert_called_with(image_name, image_dest)
self.assertEqual(image_id, "my_fake_id")
def _mock_list_images(self, return_value, image_name, expected_resp):
mock_function = mock.Mock(return_value=return_value)
func2mock = self.FAKE_CLIENT_MOCK + '.list_images'
self.useFixture(MonkeyPatch(func2mock, mock_function))
resp = self.Service._find_image(image_id=None,
image_name=image_name)
self.assertEqual(resp, expected_resp)
def test_set_get_versions(self):
exp_resp = ['v2.0', 'v2.1']
self._set_get_versions(self.Service, exp_resp, self.FAKE_VERSIONS)
@mock.patch('config_tempest.services.image.ImageService'
'.find_or_upload_image')
def test_create_tempest_images_exception(self, mock_find_upload):
mock_find_upload.side_effect = Exception
exc = Exception
self.assertRaises(exc,
self.Service.create_tempest_images,
conf=self.conf)
def test_create_tempest_images_ref_alt_ref(self): def test_create_tempest_images_ref_alt_ref(self):
self.conf.set('compute', 'image_ref', 'id_a') self.conf.set('compute', 'image_ref', 'id_a')
self.conf.set('compute', 'image_ref_alt', 'id_b') self.conf.set('compute', 'image_ref_alt', 'id_b')
@ -75,29 +112,68 @@ class TestCreateTempestImages(BaseConfigTempestTest):
def test_create_tempest_images_no_ref_no_alt_ref(self): def test_create_tempest_images_no_ref_no_alt_ref(self):
self._test_create_tempest_images() self._test_create_tempest_images()
@mock.patch('config_tempest.services.image.ImageService._find_image')
def test_find_or_upload_image_not_found_creation_not_allowed(
self, mock_find_image):
mock_find_image.return_value = None
exc = Exception
self.assertRaises(exc, self.Service.find_or_upload_image,
image_id=None, image_name=None)
class TestFindImage(BaseConfigTempestTest): def test_find_or_upload_image_not_found_creation_allowed_http(self):
self._test_find_or_upload_image_not_found_creation_allowed_format(
format="http")
CLIENT_MOCK = 'tempest.lib.services.image.v2.images_client.ImagesClient' def test_find_or_upload_image_not_found_creation_allowed_https(self):
IMAGES_LIST = [ self._test_find_or_upload_image_not_found_creation_allowed_format(
{"status": "active", "name": "ImageName"}, format="https")
{"status": "default", "name": "MyImage"},
{"status": "active", "name": "MyImage"}
]
def setUp(self): @mock.patch('shutil.copyfile')
super(TestFindImage, self).setUp() @mock.patch('config_tempest.services.image.ImageService._find_image')
conf = self._get_conf("v2.0", "v3") @mock.patch('config_tempest.services.image.ImageService._download_file')
self.client = self._get_clients(conf).images @mock.patch('config_tempest.services.image.ImageService._upload_image')
def test_find_or_upload_image_not_found_creation_allowed_ftp_old(
self, mock_upload_image, mock_download_file, mock_find_image,
mock_copy):
mock_find_image.return_value = None
mock_upload_image.return_value = {"id": "my_fake_id"}
# image source does not start with http or https
image_source = "ftp://any_random_url"
image_dest = "place_on_disk"
image_name = "my_image"
self.Service.allow_creation = True
image_id = self.Service.find_or_upload_image(
image_id=None, image_name=image_name,
image_source=image_source, image_dest=image_dest)
mock_copy.assert_called_with(image_source, image_dest)
mock_upload_image.assert_called_with(
image_name, image_dest)
self.assertEqual(image_id, "my_fake_id")
def _mock_list_images(self, return_value, image_name, expected_resp): @mock.patch('os.path.isfile')
mock_function = mock.Mock(return_value=return_value) @mock.patch('config_tempest.services.image.ImageService._find_image')
func2mock = self.CLIENT_MOCK + '.list_images' def test_find_or_upload_image_found_downloaded(
self.useFixture(MonkeyPatch(func2mock, mock_function)) self, mock_find_image, mock_isfile):
resp = tool._find_image(client=self.client, mock_find_image.return_value = \
image_id=None, {"status": "active", "name": "ImageName", "id": "my_fake_id"}
image_name=image_name) mock_isfile.return_value = True
self.assertEqual(resp, expected_resp) image_id = self.Service.find_or_upload_image(
image_id=None, image_name=None)
self.assertEqual(image_id, "my_fake_id")
@mock.patch('config_tempest.services.image.ImageService._download_image')
@mock.patch('os.path.isfile')
@mock.patch('config_tempest.services.image.ImageService._find_image')
def test_find_or_upload_image_found_not_downloaded(
self, mock_find_image, mock_isfile, mock_download_image):
image_id = "my_fake_id"
mock_find_image.return_value = \
{"status": "active", "name": "ImageName", "id": image_id}
mock_isfile.return_value = False
image_id = self.Service.find_or_upload_image(
image_id=None, image_name=None)
mock_download_image.assert_called()
self.assertEqual(image_id, "my_fake_id")
def test_find_image_found(self): def test_find_image_found(self):
expected_resp = {"status": "default", "name": "MyImage"} expected_resp = {"status": "default", "name": "MyImage"}
@ -113,106 +189,8 @@ class TestFindImage(BaseConfigTempestTest):
def test_find_image_by_id(self): def test_find_image_by_id(self):
expected_resp = {"id": "001", "status": "active", "name": "ImageName"} expected_resp = {"id": "001", "status": "active", "name": "ImageName"}
mock_function = mock.Mock(return_value=expected_resp) mock_function = mock.Mock(return_value=expected_resp)
func2mock = self.CLIENT_MOCK + '.show_image' func2mock = self.FAKE_CLIENT_MOCK + '.show_image'
self.useFixture(MonkeyPatch(func2mock, mock_function)) self.useFixture(MonkeyPatch(func2mock, mock_function))
resp = tool._find_image(client=self.client, resp = self.Service._find_image(image_id="001",
image_id="001",
image_name="cirros") image_name="cirros")
self.assertEqual(resp, expected_resp) self.assertEqual(resp, expected_resp)
class TestFindOrUploadImage(BaseConfigTempestTest):
def setUp(self):
super(TestFindOrUploadImage, self).setUp()
conf = self._get_conf("v2.0", "v3")
self.client = self._get_clients(conf).images
@mock.patch('config_tempest.main._find_image')
def test_find_or_upload_image_not_found_creation_not_allowed(
self, mock_find_image):
mock_find_image.return_value = None
exc = Exception
self.assertRaises(exc, tool.find_or_upload_image, client=self.client,
image_id=None, image_name=None,
allow_creation=False)
@mock.patch('config_tempest.main._find_image')
@mock.patch('config_tempest.main._download_file')
@mock.patch('config_tempest.main._upload_image')
def _test_find_or_upload_image_not_found_creation_allowed_format(
self, mock_upload_image,
mock_download_file, mock_find_image, format):
mock_find_image.return_value = None
mock_upload_image.return_value = {"id": "my_fake_id"}
image_source = format + "://any_random_url"
image_dest = "my_dest"
image_name = "my_image"
disk_format = "my_format"
image_id = tool.find_or_upload_image(
client=self.client, image_id=None, image_dest=image_dest,
image_name=image_name, image_source=image_source,
allow_creation=True, disk_format=disk_format)
mock_download_file.assert_called_with(image_source, image_dest)
mock_upload_image.assert_called_with(self.client,
image_name, image_dest,
disk_format)
self.assertEqual(image_id, "my_fake_id")
def test_find_or_upload_image_not_found_creation_allowed_http(self):
self._test_find_or_upload_image_not_found_creation_allowed_format(
format="http")
def test_find_or_upload_image_not_found_creation_allowed_https(self):
self._test_find_or_upload_image_not_found_creation_allowed_format(
format="https")
@mock.patch('shutil.copyfile')
@mock.patch('config_tempest.main._find_image')
@mock.patch('config_tempest.main._download_file')
@mock.patch('config_tempest.main._upload_image')
def test_find_or_upload_image_not_found_creation_allowed_ftp_old(
self, mock_upload_image, mock_download_file, mock_find_image,
mock_copy):
mock_find_image.return_value = None
mock_upload_image.return_value = {"id": "my_fake_id"}
# image source does not start with http or https
image_source = "ftp://any_random_url"
image_dest = "place_on_disk"
disk_format = "my_format"
image_name = "my_image"
image_id = tool.find_or_upload_image(
client=self.client, image_id=None, image_name=image_name,
image_source=image_source, image_dest=image_dest,
allow_creation=True, disk_format=disk_format)
mock_copy.assert_called_with(image_source, image_dest)
mock_upload_image.assert_called_with(
self.client, image_name, image_dest, disk_format)
self.assertEqual(image_id, "my_fake_id")
@mock.patch('os.path.isfile')
@mock.patch('config_tempest.main._find_image')
def test_find_or_upload_image_found_downloaded(
self, mock_find_image, mock_isfile):
mock_find_image.return_value = \
{"status": "active", "name": "ImageName", "id": "my_fake_id"}
mock_isfile.return_value = True
image_id = tool.find_or_upload_image(
client=self.client, image_id=None,
image_name=None, allow_creation=True)
self.assertEqual(image_id, "my_fake_id")
@mock.patch('config_tempest.main._download_image')
@mock.patch('os.path.isfile')
@mock.patch('config_tempest.main._find_image')
def test_find_or_upload_image_found_not_downloaded(
self, mock_find_image, mock_isfile, mock_download_image):
image_id = "my_fake_id"
mock_find_image.return_value = \
{"status": "active", "name": "ImageName", "id": image_id}
mock_isfile.return_value = False
image_id = tool.find_or_upload_image(
client=self.client, image_id=None,
image_name=None, allow_creation=True)
mock_download_image.assert_called()
self.assertEqual(image_id, "my_fake_id")

View File

@ -0,0 +1,101 @@
# Copyright 2018 Red Hat, 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 mock
from config_tempest.services.network import NetworkService
from config_tempest.tempest_conf import TempestConf
from config_tempest.tests.base import BaseServiceTest
class TestNetworkService(BaseServiceTest):
FAKE_NETWORK_LIST = {
'networks': [{
'provider:physical_network': None,
'id': '1ea533d7-4c65-4f25',
'router:external': True,
'availability_zone_hints': [],
'availability_zones': [],
'ipv4_address_scope': None,
'status': 'ACTIVE',
'subnets': ['fake_subnet'],
'label': 'my_fake_label',
'name': 'tempest-network',
'admin_state_up': True,
}]
}
def setUp(self):
super(TestNetworkService, self).setUp()
self.conf = TempestConf()
self.Service = NetworkService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
self.Service.client = self.FakeServiceClient()
def test_set_get_extensions(self):
exp_resp = ['NMN', 'OS-DCF']
self._set_get_extensions(self.Service, exp_resp, self.FAKE_EXTENSIONS)
def test_tempest_network_id_not_found(self):
return_mock = mock.Mock(return_value={"networks": []})
self.Service.client.list_networks = return_mock
self.Service._public_network_id = "doesn't_exist"
self.assertRaises(ValueError,
self.Service._supplied_network)
def test_create_network_id_supplied_by_user(self):
return_mock = mock.Mock(return_value=self.FAKE_NETWORK_LIST)
self.Service.client.list_networks = return_mock
self.Service._public_network_id = '1ea533d7-4c65-4f25'
self.Service._supplied_network()
self.assertEqual(self.Service._public_network_name, 'tempest-network')
def test_create_network_auto_discover(self):
return_mock = mock.Mock(return_value=self.FAKE_NETWORK_LIST)
self.Service.client.list_networks = return_mock
self.Service._discover_network()
self.assertEqual(self.Service._public_network_id, '1ea533d7-4c65-4f25')
self.assertEqual(self.Service._public_network_name, 'tempest-network')
@mock.patch('config_tempest.services.network.LOG')
def test_create_network_auto_discover_not_found(self, mock_logging):
# delete subnets => network will not be found
self.FAKE_NETWORK_LIST['networks'][0]['subnets'] = []
return_mock = mock.Mock(return_value=self.FAKE_NETWORK_LIST)
self.Service.client.list_networks = return_mock
self.Service._discover_network()
# check if LOG.error was called
self.assertTrue(mock_logging.error.called)
def test_network_not_discovered(self):
FAKE_NETWORK_LIST = {
'networks': [{
'label': ""
}]
}
exception = Exception
self.Service.client.return_value = FAKE_NETWORK_LIST
self.assertRaises(exception,
self.Service.create_tempest_networks_nova,
conf=self.conf)
def test_create_fixed_network(self):
self.Service.client.return_value = self.FAKE_NETWORK_LIST
self.Service.create_tempest_networks_nova(conf=self.conf)
self.assertEqual(self.conf.get('compute', 'fixed_network_name'),
'my_fake_label')

View File

@ -0,0 +1,37 @@
# Copyright 2018 Red Hat, 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.
from config_tempest.services.object_storage import ObjectStorageService
from config_tempest.tests.base import BaseServiceTest
class TestObjectStorageService(BaseServiceTest):
def setUp(self):
super(TestObjectStorageService, self).setUp()
self.Service = ObjectStorageService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_set_get_extensions(self):
expected_resp = ['formpost', 'ratelimit',
'methods', 'account_quotas']
self._fake_service_do_get_method(self.FAKE_STORAGE_EXTENSIONS)
self.Service.set_extensions(object_store_discovery=True)
self.assertItemsEqual(self.Service.extensions, expected_resp)
self.assertItemsEqual(self.Service.get_extensions(), expected_resp)
self.Service.set_extensions()
self.assertItemsEqual(self.Service.extensions, [])
self.assertItemsEqual(self.Service.get_extensions(), [])

View File

@ -0,0 +1,120 @@
# Copyright 2018 Red Hat, 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.
from config_tempest.services.services import Services
from config_tempest.tests.base import BaseConfigTempestTest
import mock
class TestServices(BaseConfigTempestTest):
FAKE_ENTRY = {
'endpoints': [
{
'region': 'my_region',
'interface': 'public'
},
{
'region': 'other_region',
'interface': 'private'
}
]
}
def setUp(self):
super(TestServices, self).setUp()
@mock.patch('config_tempest.services.services.Services.discover')
def _create_services_instance(self, mock_discover):
conf = self._get_conf('v2', 'v3')
creds = self._get_creds(conf)
clients = mock.Mock()
services = Services(clients, conf, creds)
return services
def test_get_endpoints_api_2(self):
services = self._create_services_instance()
services._region = 'my_region'
resp = services.get_endpoints(self.FAKE_ENTRY)
self.assertEqual(resp, self.FAKE_ENTRY['endpoints'][0])
services._region = 'doesnt_exist_region'
resp = services.get_endpoints(self.FAKE_ENTRY)
self.assertEqual(resp, self.FAKE_ENTRY['endpoints'][0])
services._region = 'other_region'
resp = services.get_endpoints(self.FAKE_ENTRY)
self.assertEqual(resp, self.FAKE_ENTRY['endpoints'][1])
def test_get_endpoints_api_3(self):
services = self._create_services_instance()
services._creds.api_version = 3
services._region = 'my_region'
resp = services.get_endpoints(self.FAKE_ENTRY)
self.assertEqual(resp, self.FAKE_ENTRY['endpoints'][0])
services._region = 'other_region'
resp = services.get_endpoints(self.FAKE_ENTRY)
self.assertEqual(resp, self.FAKE_ENTRY['endpoints'][0])
def test_get_endpoints_no_endpoints(self):
services = self._create_services_instance()
resp = services.get_endpoints({'endpoints': []})
self.assertEqual(resp, [])
def test_set_catalog_and_url(self):
services = self._create_services_instance()
# api version = 2
services.set_catalog_and_url()
self.assertEqual(services.service_catalog, 'serviceCatalog')
self.assertEqual(services.public_url, 'publicURL')
# api version = 3
services._creds.api_version = 3
services.set_catalog_and_url()
self.assertEqual(services.service_catalog, 'catalog')
self.assertEqual(services.public_url, 'url')
def test_edit_identity_url(self):
services = self._create_services_instance()
url_port = 'https://10.0.0.101:13000/v2.0'
identity_url = 'https://10.0.0.101/identity'
url_no_port = 'https://10.0.0.101/v2.0'
services._clients.auth_provider.auth_url = url_port
url = services.edit_identity_url(url_port)
self.assertEqual(url_port, url)
url = services.edit_identity_url(identity_url)
self.assertEqual("https://10.0.0.101:13000/identity/v2", url)
url = services.edit_identity_url(url_no_port)
self.assertEqual(url_no_port, url)
services._clients.auth_provider.auth_url = url_no_port
url = services.edit_identity_url(identity_url)
self.assertEqual(identity_url + "/v2", url)
def test_get_service(self):
services = self._create_services_instance()
exp_resp = mock.Mock()
exp_resp.name = 'my_service'
services._services = [exp_resp]
resp = services.get_service('my_service')
self.assertEqual(resp, exp_resp)
resp = services.get_service('my')
self.assertEqual(resp, None)
def test_is_service(self):
services = self._create_services_instance()
service = mock.Mock()
service.name = 'my_service'
services._services = [service]
resp = services.is_service('my_service')
self.assertEqual(resp, True)
resp = services.is_service('other_service')
self.assertEqual(resp, False)

View File

@ -0,0 +1,65 @@
# Copyright 2018 Red Hat, 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 mock
from config_tempest.services import volume
from config_tempest.tempest_conf import TempestConf
from config_tempest.tests.base import BaseServiceTest
class TestVolumeService(BaseServiceTest):
def setUp(self):
super(TestVolumeService, self).setUp()
self.Service = volume.VolumeService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
self.conf = TempestConf()
def test_set_get_extensions(self):
exp_resp = ['NMN', 'OS-DCF']
self._set_get_extensions(self.Service, exp_resp, self.FAKE_EXTENSIONS)
def test_set_get_versions(self):
exp_resp = ['v2.0', 'v2.1']
self._set_get_versions(self.Service, exp_resp, self.FAKE_VERSIONS)
@mock.patch('config_tempest.services.volume.C.LOG')
def test_check_volume_backup_service_no_volume(self, mock_logging):
volume.check_volume_backup_service(self.conf, None, False)
self.assertTrue(mock_logging.info.called)
def test_check_volume_backup_service_state_down(self):
client_service_mock = self.FakeServiceClient(services={
'services': [
{
"state": "down"
}
]
})
volume.check_volume_backup_service(self.conf,
client_service_mock, True)
self.assertEqual(self.conf.get('volume-feature-enabled',
'backup'), 'False')
def test_check_volume_backup_service_no_service(self):
client_service_mock = self.FakeServiceClient(services={
'services': []
})
volume.check_volume_backup_service(self.conf,
client_service_mock, True)
self.assertEqual(self.conf.get('volume-feature-enabled',
'backup'), 'False')

View File

@ -1,128 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Red Hat, 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.
from config_tempest import api_discovery as api
from config_tempest.tests.base import BaseServiceTest
from fixtures import MonkeyPatch
from mock import Mock
class TestApiDiscoveryMethods(BaseServiceTest):
FAKE_AUTH_DATA = (
{
'serviceCatalog': [
{
'endpoints_links': [],
'endpoints': [{
'adminURL': 'http://172.16.52.151:8080/v1/AUTH_402',
'region': 'RegionOne',
'publicURL': 'http://172.16.52.151:8080/v1/AUTH_402',
'internalURL': 'http://172.16.52.151:8080/v1/AUTH_402',
'id': '22c221db6ffd4236a3fd054c60aa8fd6'
}],
'type': 'object-store',
'name': 'swift'
}
]
}
)
EXPECTED_RESP = (
{
'object-store':
{
'extensions': 'discovered extensions',
'url': 'http://172.16.52.151:8080/v1/AUTH_402',
'versions': []
}
}
)
FAKE_AUTH_DATA_V3 = (
{
'serviceCatalog': [
{
'endpoints_links': [],
'endpoints': [{
'adminURL': 'http://172.16.52.151:8774/v3/402',
'region': 'RegionOne',
'publicURL': 'http://172.16.52.151:8774/v3/402',
'internalURL': 'http://172.16.52.151:8774/v3/402',
'id': '01bbde4f9fb54d35badf0561a53b2bdb'
}],
'type': 'compute',
'name': 'nova'
}
]
}
)
EXPECTED_RESP_V3 = (
{
'compute':
{
'url': 'http://172.16.52.151:8774/v3/402',
'versions': 'discovered versions'
}
}
)
def setUp(self):
super(TestApiDiscoveryMethods, self).setUp()
class FakeAuthProvider(object):
def __init__(self, auth_url, auth_data):
self.auth_url = auth_url # 'http://172.16.52.151:5000/v2.0'
self.auth_data = auth_data
def get_auth(self):
token = 'AAAAABYkehvCCJOsO2GWGqBbxk0mhH7VulICOW'
return (token, self.auth_data)
def _test_discover(self, url, data, function2mock,
mock_ret_val, expected_resp):
provider = self.FakeAuthProvider(url, data)
mocked_function = Mock()
mocked_function.return_value = mock_ret_val
self.useFixture(MonkeyPatch(function2mock, mocked_function))
resp = api.discover(provider, "RegionOne")
self.assertEqual(resp, expected_resp)
def test_get_identity_v3_extensions(self):
expected_resp = ['OS-INHERIT', 'OS-OAUTH1',
'OS-SIMPLE-CERT', 'OS-EP-FILTER']
fake_resp = self.FakeRequestResponse()
mocked_requests = Mock()
mocked_requests.return_value = fake_resp
self.useFixture(MonkeyPatch('requests.get', mocked_requests))
resp = api.get_identity_v3_extensions(self.FAKE_URL + "v3")
self.assertItemsEqual(resp, expected_resp)
def test_discover_with_v2_url(self):
function2mock = ('config_tempest.api_discovery.'
'ObjectStorageService.get_extensions')
mock_ret_val = "discovered extensions"
self._test_discover(self.FAKE_URL + "v2.0", self.FAKE_AUTH_DATA,
function2mock, mock_ret_val, self.EXPECTED_RESP)
def test_discover_with_v3_url(self):
function2mock = ('config_tempest.api_discovery.'
'ComputeService.get_versions')
mock_ret_val = "discovered versions"
self._test_discover(self.FAKE_URL + "v3", self.FAKE_AUTH_DATA_V3,
function2mock, mock_ret_val, self.EXPECTED_RESP_V3)

View File

@ -1,173 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Red Hat, 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.
from config_tempest import api_discovery as api
from config_tempest.tests.base import BaseServiceTest
import mock
import unittest
class TestService(BaseServiceTest):
def setUp(self):
super(TestService, self).setUp()
self.Service = api.Service("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def _mocked_do_get(self, mock_urllib3):
mock_http = mock_urllib3.PoolManager()
expected_resp = mock_http.request('GET',
self.FAKE_URL,
self.FAKE_HEADERS)
return expected_resp.data
@unittest.skip('Failing due to Storyboard: 2001245')
@mock.patch('config_tempest.api_discovery.urllib3')
def test_do_get(self, mock_urllib3):
resp = self.Service.do_get(self.FAKE_URL)
expected_resp = self._mocked_do_get(mock_urllib3)
self.assertEqual(resp, expected_resp)
def test_service_headers(self):
self.assertEqual(self.Service.headers, self.FAKE_HEADERS)
class TestVersionedService(BaseServiceTest):
def setUp(self):
super(TestVersionedService, self).setUp()
self.Service = api.VersionedService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_get_versions(self):
expected_resp = ['v2.0', 'v2.1']
self._fake_service_do_get_method(self.FAKE_VERSIONS)
resp = self.Service.get_versions()
self.assertItemsEqual(resp, expected_resp)
def test_deserialize_versions(self):
expected_resp = ['v2.0', 'v2.1']
self._test_deserialize_versions(self.Service,
expected_resp,
self.FAKE_VERSIONS)
class TestComputeService(BaseServiceTest):
def setUp(self):
super(TestComputeService, self).setUp()
self.Service = api.ComputeService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_get_extensions(self):
expected_resp = ['NMN', 'OS-DCF']
self._get_extensions(self.Service, expected_resp, self.FAKE_EXTENSIONS)
def test_get_service_class(self):
self._test_get_service_class('compute', api.ComputeService)
class TestImageService(BaseServiceTest):
def setUp(self):
super(TestImageService, self).setUp()
def test_get_service_class(self):
self._test_get_service_class('image', api.ImageService)
class TestNetworkService(BaseServiceTest):
def setUp(self):
super(TestNetworkService, self).setUp()
self.Service = api.NetworkService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_get_extensions(self):
expected_resp = ['NMN', 'OS-DCF']
self._get_extensions(self.Service, expected_resp, self.FAKE_EXTENSIONS)
def test_get_service_class(self):
self._test_get_service_class('network', api.NetworkService)
class TestVolumeService(BaseServiceTest):
def setUp(self):
super(TestVolumeService, self).setUp()
self.Service = api.VolumeService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_get_extensions(self):
expected_resp = ['NMN', 'OS-DCF']
self._get_extensions(self.Service, expected_resp, self.FAKE_EXTENSIONS)
def test_get_service_class(self):
self._test_get_service_class('volumev3', api.VolumeService)
class TestIdentityService(BaseServiceTest):
def setUp(self):
super(TestIdentityService, self).setUp()
self.Service = api.IdentityService("ServiceName",
self.FAKE_URL + 'v2.0/',
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_get_extensions(self):
expected_resp = ['OS-DCF', 'NMN']
self._get_extensions(self.Service, expected_resp,
self.FAKE_IDENTITY_EXTENSIONS)
def test_deserialize_versions(self):
expected_resp = ['v3.8']
self._test_deserialize_versions(self.Service,
expected_resp,
self.FAKE_IDENTITY_VERSIONS)
expected_resp = ['v2.1', 'v3.8']
# add not deprecated v2 version to FAKE_IDENTITY_VERSIONS
v2 = {'status': 'stable', 'id': 'v2.1'}
self.FAKE_IDENTITY_VERSIONS['versions']['values'].append(v2)
self._test_deserialize_versions(self.Service,
expected_resp,
self.FAKE_IDENTITY_VERSIONS)
def test_get_service_class(self):
self._test_get_service_class('identity', api.IdentityService)
class TestObjectStorageService(BaseServiceTest):
def setUp(self):
super(TestObjectStorageService, self).setUp()
self.Service = api.ObjectStorageService("ServiceName",
self.FAKE_URL,
self.FAKE_TOKEN,
disable_ssl_validation=False)
def test_get_extensions(self):
expected_resp = ['formpost', 'ratelimit',
'methods', 'account_quotas']
self._get_extensions(self.Service, expected_resp,
self.FAKE_STORAGE_EXTENSIONS)
def test_get_service_class(self):
self._test_get_service_class('object-store',
api.ObjectStorageService)

View File

@ -16,9 +16,86 @@
# under the License. # under the License.
from fixtures import MonkeyPatch from fixtures import MonkeyPatch
import logging
import mock import mock
from config_tempest.clients import ClientManager
from config_tempest.clients import ProjectsClient
from config_tempest.tests.base import BaseConfigTempestTest from config_tempest.tests.base import BaseConfigTempestTest
from tempest.lib import exceptions
# disable logging when running unit tests
logging.disable(logging.CRITICAL)
class TestProjectsClient(BaseConfigTempestTest):
LIST_PROJECTS = [{'name': 'my_name'}]
def setUp(self):
super(TestProjectsClient, self).setUp()
self.conf = self._get_conf("v2.0", "v3")
self.creds = self._get_creds(self.conf)
# self.projects_manager = ProjectsClient(self.conf, self.creds)
self.client_manager = ClientManager(self.conf, self.creds)
def _get_projects_client(self, identity_version):
return ProjectsClient(
self.client_manager.auth_provider,
self.conf.get_defaulted('identity', 'catalog_type'),
self.client_manager.identity_region,
'publicURL',
identity_version,
**self.client_manager._get_default_params(self.conf))
def test_init(self):
resp = self._get_projects_client('v2')
self.assertEqual(type(resp.client).__name__, 'TenantsClient')
# use v3 identity version and check if ProjectClient is instantiated
resp = self._get_projects_client('v3')
self.assertEqual(type(resp.client).__name__, 'ProjectsClient')
@mock.patch('tempest.lib.services.identity.v2.tenants_client.'
'TenantsClient.list_tenants')
def test_get_project_by_name_v2(self, mock_list_tenant):
client = self._get_projects_client('v2')
mock_list_tenant.return_value = {'tenants': self.LIST_PROJECTS}
resp = client.get_project_by_name('my_name')
self.assertEqual(resp['name'], 'my_name')
@mock.patch('tempest.lib.services.identity.v3.projects_client.'
'ProjectsClient.list_projects')
def test_get_project_by_name_v3(self, mock_list_project):
client = self._get_projects_client('v3')
mock_list_project.return_value = {'projects': self.LIST_PROJECTS}
resp = client.get_project_by_name('my_name')
self.assertEqual(resp['name'], 'my_name')
@mock.patch('tempest.lib.services.identity.v3.projects_client.'
'ProjectsClient.list_projects')
def test_get_project_by_name_not_found(self, mock_list_project):
client = self._get_projects_client('v3')
mock_list_project.return_value = {'projects': self.LIST_PROJECTS}
try:
client.get_project_by_name('doesnt_exist')
except exceptions.NotFound:
# expected behaviour
pass
@mock.patch('tempest.lib.services.identity.v2.tenants_client.'
'TenantsClient.create_tenant')
def test_create_project_v2(self, mock_create_tenant):
client = self._get_projects_client('v2')
client.create_project('name', 'description')
mock_create_tenant.assert_called_with(
name='name', description='description')
@mock.patch('tempest.lib.services.identity.v3.projects_client.'
'ProjectsClient.create_project')
def test_create_project_v3(self, mock_create_project):
client = self._get_projects_client('v3')
client.create_project('name', 'description')
mock_create_project.assert_called_with(
name='name', description='description')
class TestClientManager(BaseConfigTempestTest): class TestClientManager(BaseConfigTempestTest):
@ -26,41 +103,49 @@ class TestClientManager(BaseConfigTempestTest):
def setUp(self): def setUp(self):
super(TestClientManager, self).setUp() super(TestClientManager, self).setUp()
self.conf = self._get_conf("v2.0", "v3") self.conf = self._get_conf("v2.0", "v3")
self.client = self._get_clients(self.conf) self.creds = self._get_creds(self.conf)
# self.client = self._get_clients(self.conf) # ?
self.client_manager = ClientManager(self.conf, self.creds)
def test_init_manager_as_admin(self): def test_init_manager_as_admin(self):
self.creds = self._get_creds(self.conf, True)
mock_function = mock.Mock(return_value={"id": "my_fake_id"}) mock_function = mock.Mock(return_value={"id": "my_fake_id"})
func2mock = ('config_tempest.clients.ProjectsClient.' func2mock = ('config_tempest.clients.ProjectsClient.'
'get_project_by_name') 'get_project_by_name')
self.useFixture(MonkeyPatch(func2mock, mock_function)) self.useFixture(MonkeyPatch(func2mock, mock_function))
self._get_clients(self.conf, self._get_creds(self.conf, admin=True)) ClientManager(self.conf, self.creds)
# check if admin credentials were set
admin_tenant = self.conf.get("identity", "admin_tenant_name")
admin_password = self.conf.get("identity", "admin_password")
self.assertEqual(self.conf.get("identity", "admin_username"), "admin")
self.assertEqual(admin_tenant, "adminTenant")
self.assertEqual(admin_password, "adminPass")
# check if admin tenant id was set # check if admin tenant id was set
admin_tenant_id = self.conf.get("identity", "admin_tenant_id") admin_tenant_id = self.conf.get("identity", "admin_tenant_id")
self.assertEqual(admin_tenant_id, "my_fake_id") self.assertEqual(admin_tenant_id, "my_fake_id")
def test_init_manager_as_admin_using_new_auth(self): def test_get_service_client(self):
self.conf = self._get_alt_conf("v2.0", "v3") resp = self.client_manager.get_service_client('image')
self.client = self._get_clients(self.conf) self.assertEqual(resp, self.client_manager.images)
mock_function = mock.Mock(return_value={"id": "my_fake_id"}) resp = self.client_manager.get_service_client('network')
func2mock = ('config_tempest.clients.ProjectsClient' self.assertEqual(resp, self.client_manager)
'.get_project_by_name') resp = self.client_manager.get_service_client('doesnt_exist')
self.useFixture(MonkeyPatch(func2mock, mock_function)) self.assertEqual(resp, None)
self._get_clients(self.conf, self._get_creds(self.conf, admin=True))
# check if admin credentials were set def test_set_users_client(self):
admin_tenant = self.conf.get("auth", "admin_project_name") self.client_manager.users = None
admin_password = self.conf.get("auth", "admin_password") self.client_manager.set_users_client(
self.assertEqual(self.conf.get("auth", "admin_username"), "admin") self.client_manager.auth_provider,
self.assertEqual(admin_tenant, "adminTenant") self.creds.identity_version,
self.assertEqual(admin_password, "adminPass") self.conf.get_defaulted('identity', 'catalog_type'),
# check if admin tenant id was set 'publicURL',
admin_tenant_id = self.conf.get("identity", "admin_tenant_id") self.client_manager._get_default_params(self.conf))
self.assertEqual(admin_tenant_id, "my_fake_id") self.assertEqual(
use_dynamic_creds_bool = self.conf.get("auth", type(self.client_manager.users).__name__,
"use_dynamic_credentials") 'UsersClient')
self.assertEqual(use_dynamic_creds_bool, "True")
def test_set_roles_client(self):
self.client_manager.roles = None
self.client_manager.set_roles_client(
self.client_manager.auth_provider,
self.creds.identity_version,
self.conf.get_defaulted('identity', 'catalog_type'),
'publicURL',
self.client_manager._get_default_params(self.conf))
self.assertEqual(
type(self.client_manager.roles).__name__,
'RolesClient')

View File

@ -120,250 +120,3 @@ class TestOsClientConfigSupport(BaseConfigTempestTest):
self.conf.get('identity', 'admin_username'), self.conf.get('identity', 'admin_username'),
self.conf.get('identity', 'admin_password'), self.conf.get('identity', 'admin_password'),
self.conf.get('identity', 'admin_tenant_name')) self.conf.get('identity', 'admin_tenant_name'))
class TestConfigTempest(BaseConfigTempestTest):
FAKE_SERVICES = {
'compute': {
'url': 'http://172.16.52.151:8774/v2.1/402486',
'extensions': ['NMN', 'OS-DCF', 'OS-EXT-AZ', 'OS-EXT-IMG-SIZE'],
'versions': ['v2.0', 'v2.1']
},
'network': {
'url': 'http://172.16.52.151:9696',
'extensions': ['default-subnetpools', 'network-ip-availability'],
'versions': ['v2.0']
},
'image': {
'url': 'http://172.16.52.151:9292',
'extensions': [],
'versions': ['v2.4', 'v2.3', 'v2.2']
},
'volumev3': {
'url': 'http://172.16.52.151:8776/v3/402486',
'extensions': ['OS-SCH-HNT', 'os-hosts'],
'versions': ['v2.0', 'v3.0']
},
'volume': {
'url': 'http://172.16.52.151:8776/v1/402486',
'extensions': [],
'versions': []
},
'identity': {
'url': 'http://172.16.52.151:5000/v3',
'versions': ['v3.8', 'v2.0']
},
'ec2': {
'url': 'http://172.16.52.151:5000'
},
's3': {
'url': 'http://172.16.52.151:5000'
}
}
def setUp(self):
super(TestConfigTempest, self).setUp()
self.conf = self._get_conf("v2.0", "v3")
def _mock_get_identity_v3_extensions(self):
mock_function = mock.Mock(return_value=['FAKE-EXTENSIONS'])
func2mock = 'config_tempest.api_discovery.get_identity_v3_extensions'
self.useFixture(MonkeyPatch(func2mock, mock_function))
def test_check_volume_backup_service(self):
client = self._get_clients(self.conf).volume_service
CLIENT_MOCK = ('tempest.lib.services.volume.v2.'
'services_client.ServicesClient')
func2mock = '.list_services'
mock_function = mock.Mock(return_value={'services': []})
self.useFixture(MonkeyPatch(CLIENT_MOCK + func2mock, mock_function))
tool.check_volume_backup_service(client, self.conf, self.FAKE_SERVICES)
self.assertEqual(self.conf.get('volume-feature-enabled', 'backup'),
'False')
def test_check_ceilometer_service(self):
client = self._get_clients(self.conf).service_client
CLIENT_MOCK = ('tempest.lib.services.identity.v3.'
'services_client.ServicesClient')
func2mock = '.list_services'
mock_function = mock.Mock(return_value={'services': [
{'name': 'ceilometer', 'enabled': True, 'type': 'metering'}]})
self.useFixture(MonkeyPatch(CLIENT_MOCK + func2mock, mock_function))
tool.check_ceilometer_service(client, self.conf, self.FAKE_SERVICES)
self.assertEqual(self.conf.get('service_available', 'ceilometer'),
'True')
def test_configure_keystone_feature_flags(self):
tool.configure_keystone_feature_flags(self.conf, self.FAKE_SERVICES)
self.assertEqual(
self.conf.get('identity-feature-enabled',
'forbid_global_implied_dsr'), 'True')
def test_configure_boto(self):
tool.configure_boto(self.conf, self.FAKE_SERVICES)
expected_url = "http://172.16.52.151:5000"
self.assertEqual(self.conf.get("boto", "ec2_url"), expected_url)
self.assertEqual(self.conf.get("boto", "s3_url"), expected_url)
def test_configure_horizon_ipv4(self):
mock_function = mock.Mock(return_value=True)
self.useFixture(MonkeyPatch('urllib2.urlopen', mock_function))
tool.configure_horizon(self.conf)
self.assertEqual(self.conf.get('service_available', 'horizon'), "True")
self.assertEqual(self.conf.get('dashboard', 'dashboard_url'),
"http://172.16.52.151/dashboard/")
self.assertEqual(self.conf.get('dashboard', 'login_url'),
"http://172.16.52.151/dashboard/auth/login/")
def test_configure_horizon_ipv6(self):
mock_function = mock.Mock(return_value=True)
self.useFixture(MonkeyPatch('urllib2.urlopen', mock_function))
self.conf.set('identity', 'uri', 'http://[::1]:5000/v3', priority=True)
tool.configure_horizon(self.conf)
self.assertEqual(self.conf.get('service_available', 'horizon'), "True")
self.assertEqual(self.conf.get('dashboard', 'dashboard_url'),
"http://[::1]/dashboard/")
self.assertEqual(self.conf.get('dashboard', 'login_url'),
"http://[::1]/dashboard/auth/login/")
def test_discovered_services(self):
self._mock_get_identity_v3_extensions()
tool.configure_discovered_services(self.conf, self.FAKE_SERVICES)
# check enabled services
enabled_services = ["image", "volume", "compute", "network"]
# iterating through tuples = (service_name, codename)
for service in tool.SERVICE_NAMES.iteritems():
if service[0] in enabled_services:
enabled = "True"
else:
enabled = "False"
self.assertEqual(self.conf.get("service_available", service[1]),
enabled)
# check versions
for service, service_info in tool.SERVICE_VERSIONS.iteritems():
section = service + '-feature-enabled'
for version in service_info['supported_versions']:
# only image v1 is expected to be False
exp_support = str(not(service == "image" and version == "v1"))
self.assertEqual(self.conf.get(section, 'api_' + version),
exp_support)
# check extensions
for service, ext_key in tool.SERVICE_EXTENSION_KEY.iteritems():
if service in self.FAKE_SERVICES:
section = service + '-feature-enabled'
if service == "identity":
exp_ext = ",FAKE-EXTENSIONS"
else:
extensions = self.FAKE_SERVICES[service]['extensions']
exp_ext = ','.join(extensions)
self.assertEqual(self.conf.get(section, 'api_extensions'),
exp_ext)
def test_discovered_services_volume_service_disabled(self):
self.conf.set("services", "volume", "False")
self._mock_get_identity_v3_extensions()
tool.configure_discovered_services(self.conf, self.FAKE_SERVICES)
self.assertFalse(self.conf.has_option("service_available", "cinder"))
self.assertFalse(self.conf.has_option("volume-feature-enabled",
"api_v1"))
self.assertFalse(self.conf.has_option("volume-feature-enabled",
"api_v2"))
class TestFlavors(BaseConfigTempestTest):
"""Flavors test class
Tests for create_tempest_flavors and find_or_create_flavor methods.
"""
CLIENT_MOCK = 'tempest.lib.services.compute.flavors_client.FlavorsClient'
FLAVORS_LIST = [
{"id": "Fakeid", "name": "Name"},
{"id": "MyFakeID", "name": "MyID"}
]
def setUp(self):
super(TestFlavors, self).setUp()
self.conf = self._get_conf("v2.0", "v3")
self.client = self._get_clients(self.conf).flavors
def _mock_create_tempest_flavor(self, mock_function):
func2mock = 'config_tempest.main.find_or_create_flavor'
self.useFixture(MonkeyPatch(func2mock, mock_function))
tool.create_tempest_flavors(client=self.client,
conf=self.conf,
allow_creation=True)
def _mock_find_or_create_flavor(self, return_value, func2mock, flavor_name,
expected_resp, allow_creation=False,
flavor_id=None):
mock_function = mock.Mock(return_value=return_value)
self.useFixture(MonkeyPatch(self.CLIENT_MOCK + func2mock,
mock_function))
resp = tool.find_or_create_flavor(self.client,
flavor_id=flavor_id,
flavor_name=flavor_name,
allow_creation=allow_creation)
self.assertEqual(resp, expected_resp)
def test_create_tempest_flavors(self):
mock_function = mock.Mock(return_value="FakeID")
self._mock_create_tempest_flavor(mock_function)
self.assertEqual(self.conf.get('compute', 'flavor_ref'), "FakeID")
self.assertEqual(self.conf.get('compute', 'flavor_ref_alt'), "FakeID")
calls = [mock.call(self.client, None, 'm1.nano', True, ram=64),
mock.call(self.client, None, 'm1.micro', True, ram=128)]
mock_function.assert_has_calls(calls, any_order=True)
def test_create_tempest_flavors_overwrite(self):
mock_function = mock.Mock(return_value="FakeID")
self.conf.set('compute', 'flavor_ref', "FAKE_ID")
self.conf.set('compute', 'flavor_ref_alt', "FAKE_ID")
self._mock_create_tempest_flavor(mock_function)
calls = [mock.call(self.client, "FAKE_ID", 'm1.nano', True, ram=64),
mock.call(self.client, "FAKE_ID", 'm1.micro', True, ram=128)]
mock_function.assert_has_calls(calls, any_order=True)
def test_create_flavor_not_allowed(self):
# mock list_flavors() to return empty list
mock_function = mock.Mock(return_value={"flavors": []})
self.useFixture(MonkeyPatch(self.CLIENT_MOCK + '.list_flavors',
mock_function))
exc = Exception
self.assertRaises(exc,
tool.find_or_create_flavor,
client=self.client,
flavor_id="id",
flavor_name="name",
allow_creation=False)
def test_create_flavor(self):
return_value = {"flavor": {"id": "MyFakeID", "name": "MyID"}}
# mock list_flavors() to return empty list
mock_function = mock.Mock(return_value={"flavors": []})
self.useFixture(MonkeyPatch(self.CLIENT_MOCK + '.list_flavors',
mock_function))
self._mock_find_or_create_flavor(return_value=return_value,
func2mock='.create_flavor',
flavor_name="MyID",
expected_resp="MyFakeID",
allow_creation=True)
def test_find_flavor_by_id(self):
return_value = {"flavors": self.FLAVORS_LIST}
self._mock_find_or_create_flavor(return_value=return_value,
func2mock='.list_flavors',
flavor_id="MyFakeID",
flavor_name=None,
expected_resp="MyFakeID")
def test_find_flavor_by_name(self):
return_value = {"flavors": self.FLAVORS_LIST}
self._mock_find_or_create_flavor(return_value=return_value,
func2mock='.list_flavors',
flavor_name="MyID",
expected_resp="MyFakeID")

View File

@ -1,134 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Red Hat, 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.
from fixtures import MonkeyPatch
import mock
from config_tempest import main as tool
from config_tempest.tests.base import BaseConfigTempestTest
class TestCreateTempestNetworks(BaseConfigTempestTest):
FAKE_NETWORK_LIST = {
'networks': [{
'provider:physical_network': None,
'id': '1ea533d7-4c65-4f25',
'router:external': True,
'availability_zone_hints': [],
'availability_zones': [],
'ipv4_address_scope': None,
'status': 'ACTIVE',
'subnets': ['fake_subnet'],
'label': 'my_fake_label',
'name': 'tempest-network',
'admin_state_up': True,
}]
}
NEUTRON_CLIENT_MOCK = ('tempest.lib.services.network.'
'networks_client.NetworksClient')
NOVA_CLIENT_MOCK = ('tempest.lib.services.compute.'
'networks_client.NetworksClient')
def setUp(self):
super(TestCreateTempestNetworks, self).setUp()
self.conf = self._get_conf("v2.0", "v3")
self.clients = self._get_clients(self.conf)
def _mock_list_networks(self, client, client_mock, return_value):
mock_function = mock.Mock(return_value=return_value)
client_mock = client_mock + '.list_networks'
self.useFixture(MonkeyPatch(client_mock, mock_function))
def test_tempest_network_id_not_found(self):
neutron_client = self.clients.get_neutron_client()
self._mock_list_networks(neutron_client,
self.NEUTRON_CLIENT_MOCK,
return_value={"networks": []})
self.assertRaises(ValueError,
tool.create_tempest_networks,
clients=self.clients,
conf=self.conf,
has_neutron=True,
public_network_id="doesn't_exist")
def test_create_network_id_supplied_by_user(self):
neutron_client = self.clients.get_neutron_client()
self._mock_list_networks(neutron_client,
self.NEUTRON_CLIENT_MOCK,
return_value=self.FAKE_NETWORK_LIST)
tool.create_tempest_networks(clients=self.clients,
conf=self.conf,
has_neutron=True,
public_network_id='1ea533d7-4c65-4f25')
self.assertEqual(self.conf.get('network', 'public_network_id'),
'1ea533d7-4c65-4f25')
def test_create_network_auto_discover(self):
neutron_client = self.clients.get_neutron_client()
self._mock_list_networks(neutron_client,
self.NEUTRON_CLIENT_MOCK,
return_value=self.FAKE_NETWORK_LIST)
tool.create_tempest_networks(clients=self.clients,
conf=self.conf,
has_neutron=True,
public_network_id=None)
self.assertEqual(self.conf.get('network', 'public_network_id'),
'1ea533d7-4c65-4f25')
self.assertEqual(self.conf.get('network', 'floating_network_name'),
'tempest-network')
@mock.patch('config_tempest.main.LOG')
def test_create_network_auto_discover_not_found(self, mock_logging):
neutron_client = self.clients.get_neutron_client()
# delete subnets => network will not be found
self.FAKE_NETWORK_LIST['networks'][0]['subnets'] = []
self._mock_list_networks(neutron_client,
self.NEUTRON_CLIENT_MOCK,
return_value=self.FAKE_NETWORK_LIST)
tool.create_tempest_networks(clients=self.clients,
conf=self.conf,
has_neutron=True,
public_network_id=None)
# check if LOG.error was called
self.assertTrue(mock_logging.error.called)
def test_network_not_discovered(self):
exception = Exception
nova_client = self.clients.get_nova_net_client()
self._mock_list_networks(nova_client,
self.NOVA_CLIENT_MOCK,
return_value=False)
self.assertRaises(exception,
tool.create_tempest_networks,
clients=self.clients,
conf=self.conf,
has_neutron=False,
public_network_id=None)
def test_create_fixed_network(self):
nova_client = self.clients.get_nova_net_client()
self._mock_list_networks(nova_client,
self.NOVA_CLIENT_MOCK,
return_value=self.FAKE_NETWORK_LIST)
tool.create_tempest_networks(clients=self.clients,
conf=self.conf,
has_neutron=False,
public_network_id=None)
self.assertEqual(self.conf.get('compute', 'fixed_network_name'),
'my_fake_label')

View File

@ -28,6 +28,28 @@ class TestCredentials(BaseConfigTempestTest):
self.conf = self._get_conf("v2.0", "v3") self.conf = self._get_conf("v2.0", "v3")
self.creds = self._get_creds(self.conf) self.creds = self._get_creds(self.conf)
def test_get_credential(self):
# set conf containing the newer values (admin creds in auth section)
self.creds._conf = self._get_alt_conf("v2.0", "v3")
resp = self.creds.get_credential("username")
self.assertEqual(resp, "demo")
# set admin credentials
self.creds.admin = True
resp = self.creds.get_credential("username")
self.assertEqual(resp, "admin")
def test_get_identity_credential(self):
for i in range(0, 2):
resp = self.creds.get_identity_credential("username")
self.assertEqual(resp, "demo")
# set admin credentials
self.creds.admin = True
resp = self.creds.get_identity_credential("admin_username")
self.assertEqual(resp, "admin")
# use conf which contains the newer values - (admin creds
# in auth section)
self.creds._conf = self._get_alt_conf("v2.0", "v3")
def test_get_identity_version_v2(self): def test_get_identity_version_v2(self):
resp = self.creds._get_identity_version() resp = self.creds._get_identity_version()
self.assertEqual(resp, 'v2') self.assertEqual(resp, 'v2')
@ -38,6 +60,23 @@ class TestCredentials(BaseConfigTempestTest):
resp = creds._get_identity_version() resp = creds._get_identity_version()
self.assertEqual(resp, 'v3') self.assertEqual(resp, 'v3')
def test_get_creds_kwargs(self):
expected_resp = {
'username': 'demo',
'password': 'secret',
'tenant_name': 'demo'
}
self.assertEqual(self.creds._get_creds_kwargs(), expected_resp)
self.creds.identity_version = 'v3'
expected_resp = {
'username': 'demo',
'password': 'secret',
'project_name': 'demo',
'domain_name': 'Default',
'user_domain_name': 'Default'
}
self.assertEqual(self.creds._get_creds_kwargs(), expected_resp)
def test_set_credentials_v2(self): def test_set_credentials_v2(self):
mock_function = mock.Mock() mock_function = mock.Mock()
function2mock = 'config_tempest.credentials.auth.get_credentials' function2mock = 'config_tempest.credentials.auth.get_credentials'

View File

@ -0,0 +1,112 @@
# Copyright 2018 Red Hat, 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.
from fixtures import MonkeyPatch
import mock
from config_tempest.flavors import Flavors
from config_tempest.tests.base import BaseConfigTempestTest
class TestFlavors(BaseConfigTempestTest):
"""Flavors test class
Tests for create_tempest_flavors and find_or_create_flavor methods.
"""
CLIENT_MOCK = 'tempest.lib.services.compute.flavors_client.FlavorsClient'
FLAVORS_LIST = [
{"id": "Fakeid", "name": "Name"},
{"id": "MyFakeID", "name": "MyID"}
]
def setUp(self):
super(TestFlavors, self).setUp()
self.conf = self._get_conf("v2.0", "v3")
self.client = self._get_clients(self.conf).flavors
self.Service = Flavors(self.client, True, self.conf)
def _mock_create_tempest_flavor(self, mock_function):
func2mock = 'config_tempest.flavors.Flavors.find_or_create_flavor'
self.useFixture(MonkeyPatch(func2mock, mock_function))
self.Service.create_tempest_flavors()
def _mock_find_or_create_flavor(self, return_value, func2mock, flavor_name,
expected_resp, allow_creation=False,
flavor_id=None):
self.Service.allow_creation = allow_creation
mock_function = mock.Mock(return_value=return_value)
self.useFixture(MonkeyPatch(self.CLIENT_MOCK + func2mock,
mock_function))
resp = self.Service.find_or_create_flavor(flavor_id=flavor_id,
flavor_name=flavor_name)
self.assertEqual(resp, expected_resp)
def test_create_tempest_flavors(self):
mock_function = mock.Mock(return_value="FakeID")
self._mock_create_tempest_flavor(mock_function)
self.assertEqual(self.conf.get('compute', 'flavor_ref'), "FakeID")
self.assertEqual(self.conf.get('compute', 'flavor_ref_alt'), "FakeID")
calls = [mock.call(None, 'm1.nano', ram=64),
mock.call(None, 'm1.micro', ram=128)]
mock_function.assert_has_calls(calls, any_order=True)
def test_create_tempest_flavors_overwrite(self):
mock_function = mock.Mock(return_value="FakeID")
self.conf.set('compute', 'flavor_ref', "FAKE_ID")
self.conf.set('compute', 'flavor_ref_alt', "FAKE_ID")
self._mock_create_tempest_flavor(mock_function)
calls = [mock.call("FAKE_ID", 'm1.nano', ram=64),
mock.call("FAKE_ID", 'm1.micro', ram=128)]
mock_function.assert_has_calls(calls, any_order=True)
def test_create_flavor_not_allowed(self):
# mock list_flavors() to return empty list
self.Service.allow_creation = False
mock_function = mock.Mock(return_value={"flavors": []})
self.useFixture(MonkeyPatch(self.CLIENT_MOCK + '.list_flavors',
mock_function))
exc = Exception
self.assertRaises(exc,
self.Service.find_or_create_flavor,
flavor_id="id",
flavor_name="name")
def test_create_flavor(self):
return_value = {"flavor": {"id": "MyFakeID", "name": "MyID"}}
# mock list_flavors() to return empty list
mock_function = mock.Mock(return_value={"flavors": []})
self.useFixture(MonkeyPatch(self.CLIENT_MOCK + '.list_flavors',
mock_function))
self._mock_find_or_create_flavor(return_value=return_value,
func2mock='.create_flavor',
flavor_name="MyID",
expected_resp="MyFakeID",
allow_creation=True)
def test_find_flavor_by_id(self):
return_value = {"flavors": self.FLAVORS_LIST}
self._mock_find_or_create_flavor(return_value=return_value,
func2mock='.list_flavors',
flavor_id="MyFakeID",
flavor_name=None,
expected_resp="MyFakeID")
def test_find_flavor_by_name(self):
return_value = {"flavors": self.FLAVORS_LIST}
self._mock_find_or_create_flavor(return_value=return_value,
func2mock='.list_flavors',
flavor_name="MyID",
expected_resp="MyFakeID")

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*- # Copyright 2018 Red Hat, Inc.
# Copyright 2017 Red Hat, Inc.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -17,98 +15,80 @@
import mock import mock
from config_tempest import main as tool
from config_tempest.tests.base import BaseConfigTempestTest from config_tempest.tests.base import BaseConfigTempestTest
from config_tempest.users import Users
from tempest.lib import exceptions from tempest.lib import exceptions
class TestCreateTempestUser(BaseConfigTempestTest): class TestUsers(BaseConfigTempestTest):
def setUp(self): def setUp(self):
super(TestCreateTempestUser, self).setUp() super(TestUsers, self).setUp()
self.conf = self._get_conf("v2.0", "v3") self.conf = self._get_conf("v2.0", "v3")
self.tenants_client = self._get_clients(self.conf).tenants tenants_client = self._get_clients(self.conf).tenants
self.users_client = self._get_clients(self.conf).users users_client = self._get_clients(self.conf).users
self.roles_client = self._get_clients(self.conf).roles roles_client = self._get_clients(self.conf).roles
self.Service = Users(tenants_client, roles_client,
users_client, self.conf)
self.username = "test_user"
self.password = "cryptic"
self.tenant_name = "project"
self.tenant_description = "Tenant for Tempest %s user" % self.username
self.role_name = "fake_role"
self.email = "%s@test.com" % self.username
self.users = {'users':
[{'name': "test_user",
'id': "fake_user_id"},
{'name': "test_user2",
'id': "fake_user_id2"}]}
self.roles = {'roles':
[{'name': "fake_role",
'id': "fake_role_id"},
{'name': "fake_role2",
'id': "fake_role_id2"}]}
@mock.patch('config_tempest.main.create_user_with_tenant') @mock.patch('config_tempest.users.Users.'
@mock.patch('config_tempest.main.give_role_to_user') 'create_user_with_tenant')
@mock.patch('config_tempest.users.Users.give_role_to_user')
def _test_create_tempest_user(self, def _test_create_tempest_user(self,
mock_give_role_to_user, mock_give_role_to_user,
mock_create_user_with_tenant, mock_create_user_with_tenant,
services): orchestration=False):
alt_username = "my_user" alt_username = "my_user"
alt_password = "my_pass" alt_password = "my_pass"
alt_tenant_name = "my_tenant" alt_tenant_name = "my_tenant"
self.conf.set("identity", "alt_username", alt_username) self.conf.set("identity", "alt_username", alt_username)
self.conf.set("identity", "alt_password", alt_password) self.conf.set("identity", "alt_password", alt_password)
self.conf.set("identity", "alt_tenant_name", alt_tenant_name) self.conf.set("identity", "alt_tenant_name", alt_tenant_name)
tool.create_tempest_users(self.tenants_client, self.Service.create_tempest_users(orchestration)
self.roles_client, if orchestration:
self.users_client,
self.conf,
services=services)
if 'orchestration' in services:
self.assertEqual(mock_give_role_to_user.mock_calls, [ self.assertEqual(mock_give_role_to_user.mock_calls, [
mock.call(self.tenants_client, mock.call(self.conf.get('identity',
self.roles_client,
self.users_client,
self.conf.get('identity',
'admin_username'), 'admin_username'),
self.conf.get('identity',
'tenant_name'),
role_name='admin'), role_name='admin'),
mock.call(self.tenants_client, mock.call(self.conf.get('identity',
self.roles_client,
self.users_client,
self.conf.get('identity',
'username'), 'username'),
self.conf.get('identity',
'tenant_name'),
role_name='heat_stack_owner', role_name='heat_stack_owner',
role_required=False), role_required=False),
]) ])
else: else:
mock_give_role_to_user.assert_called_with( mock_give_role_to_user.assert_called_with(
self.tenants_client, self.roles_client,
self.users_client,
self.conf.get('identity', 'admin_username'), self.conf.get('identity', 'admin_username'),
self.conf.get('identity', 'tenant_name'),
role_name='admin') role_name='admin')
self.assertEqual(mock_create_user_with_tenant.mock_calls, [ self.assertEqual(mock_create_user_with_tenant.mock_calls, [
mock.call(self.tenants_client, mock.call(self.conf.get('identity', 'username'),
self.users_client,
self.conf.get('identity', 'username'),
self.conf.get('identity', 'password'), self.conf.get('identity', 'password'),
self.conf.get('identity', 'tenant_name')), self.conf.get('identity', 'tenant_name')),
mock.call(self.tenants_client, mock.call(self.conf.get('identity', 'alt_username'),
self.users_client,
self.conf.get('identity', 'alt_username'),
self.conf.get('identity', 'alt_password'), self.conf.get('identity', 'alt_password'),
self.conf.get('identity', 'alt_tenant_name')), self.conf.get('identity', 'alt_tenant_name')),
]) ])
def test_create_tempest_user(self): def test_create_tempest_user(self):
services = ['compute', 'network'] self._test_create_tempest_user(orchestration=False)
self._test_create_tempest_user(services=services)
def test_create_tempest_user_with_orchestration(self): def test_create_tempest_user_with_orchestration(self):
services = ['compute', 'network', 'orchestration'] self._test_create_tempest_user(orchestration=True)
self._test_create_tempest_user(services=services)
class TestCreateUserWithTenant(BaseConfigTempestTest):
def setUp(self):
super(TestCreateUserWithTenant, self).setUp()
self.conf = self._get_conf("v2.0", "v3")
self.tenants_client = self._get_clients(self.conf).tenants
self.users_client = self._get_clients(self.conf).users
self.username = "test_user"
self.password = "cryptic"
self.tenant_name = "project"
self.tenant_description = "Tenant for Tempest %s user" % self.username
self.email = "%s@test.com" % self.username
@mock.patch('config_tempest.clients.ProjectsClient' @mock.patch('config_tempest.clients.ProjectsClient'
'.get_project_by_name') '.get_project_by_name')
@ -120,9 +100,7 @@ class TestCreateUserWithTenant(BaseConfigTempestTest):
mock_create_project, mock_create_project,
mock_get_project_by_name): mock_get_project_by_name):
mock_get_project_by_name.return_value = {'id': "fake-id"} mock_get_project_by_name.return_value = {'id': "fake-id"}
tool.create_user_with_tenant( self.Service.create_user_with_tenant(
tenants_client=self.tenants_client,
users_client=self.users_client,
username=self.username, username=self.username,
password=self.password, password=self.password,
tenant_name=self.tenant_name) tenant_name=self.tenant_name)
@ -146,9 +124,7 @@ class TestCreateUserWithTenant(BaseConfigTempestTest):
mock_get_project_by_name.return_value = {'id': "fake-id"} mock_get_project_by_name.return_value = {'id': "fake-id"}
exc = exceptions.Conflict exc = exceptions.Conflict
mock_create_project.side_effect = exc mock_create_project.side_effect = exc
tool.create_user_with_tenant( self.Service.create_user_with_tenant(
tenants_client=self.tenants_client,
users_client=self.users_client,
username=self.username, username=self.username,
password=self.password, password=self.password,
tenant_name=self.tenant_name) tenant_name=self.tenant_name)
@ -160,8 +136,6 @@ class TestCreateUserWithTenant(BaseConfigTempestTest):
tenantId="fake-id", tenantId="fake-id",
email=self.email) email=self.email)
@mock.patch('tempest.lib.services.identity.v2.'
'users_client.UsersClient.update_user_password')
@mock.patch('tempest.common.identity.get_user_by_username') @mock.patch('tempest.common.identity.get_user_by_username')
@mock.patch('config_tempest.clients.ProjectsClient.' @mock.patch('config_tempest.clients.ProjectsClient.'
'get_project_by_name') 'get_project_by_name')
@ -172,16 +146,13 @@ class TestCreateUserWithTenant(BaseConfigTempestTest):
def test_create_user_with_tenant_user_exists( def test_create_user_with_tenant_user_exists(
self, mock_create_user, mock_create_tenant, self, mock_create_user, mock_create_tenant,
mock_get_project_by_name, mock_get_project_by_name,
mock_get_user_by_username, mock_get_user_by_username):
mock_update_user_password):
mock_get_project_by_name.return_value = {'id': "fake-id"} mock_get_project_by_name.return_value = {'id': "fake-id"}
exc = exceptions.Conflict exc = exceptions.Conflict
mock_create_user.side_effect = exc mock_create_user.side_effect = exc
fake_user = {'id': "fake_user_id"} fake_user = {'id': "fake_user_id"}
mock_get_user_by_username.return_value = fake_user mock_get_user_by_username.return_value = fake_user
tool.create_user_with_tenant( self.Service.create_user_with_tenant(
tenants_client=self.tenants_client,
users_client=self.users_client,
username=self.username, password=self.password, username=self.username, password=self.password,
tenant_name=self.tenant_name) tenant_name=self.tenant_name)
mock_create_tenant.assert_called_with( mock_create_tenant.assert_called_with(
@ -191,8 +162,6 @@ class TestCreateUserWithTenant(BaseConfigTempestTest):
tenantId="fake-id", tenantId="fake-id",
email=self.email) email=self.email)
@mock.patch('tempest.lib.services.identity.v2.'
'users_client.UsersClient.update_user_password')
@mock.patch('tempest.common.identity.get_user_by_username') @mock.patch('tempest.common.identity.get_user_by_username')
@mock.patch('config_tempest.clients.ProjectsClient.' @mock.patch('config_tempest.clients.ProjectsClient.'
'get_project_by_name') 'get_project_by_name')
@ -202,17 +171,14 @@ class TestCreateUserWithTenant(BaseConfigTempestTest):
def test_create_user_with_tenant_exists_user_exists( def test_create_user_with_tenant_exists_user_exists(
self, mock_create_user, mock_create_project, self, mock_create_user, mock_create_project,
mock_get_project_by_name, mock_get_project_by_name,
mock_get_user_by_username, mock_get_user_by_username):
mock_update_user_password):
mock_get_project_by_name.return_value = {'id': "fake-id"} mock_get_project_by_name.return_value = {'id': "fake-id"}
exc = exceptions.Conflict exc = exceptions.Conflict
mock_create_project.side_effects = exc mock_create_project.side_effects = exc
mock_create_user.side_effect = exc mock_create_user.side_effect = exc
fake_user = {'id': "fake_user_id"} fake_user = {'id': "fake_user_id"}
mock_get_user_by_username.return_value = fake_user mock_get_user_by_username.return_value = fake_user
tool.create_user_with_tenant(tenants_client=self.tenants_client, self.Service.create_user_with_tenant(username=self.username,
users_client=self.users_client,
username=self.username,
password=self.password, password=self.password,
tenant_name=self.tenant_name) tenant_name=self.tenant_name)
mock_create_project.assert_called_with( mock_create_project.assert_called_with(
@ -222,29 +188,6 @@ class TestCreateUserWithTenant(BaseConfigTempestTest):
tenantId="fake-id", tenantId="fake-id",
email=self.email) email=self.email)
class TestGiveRoleToUser(BaseConfigTempestTest):
def setUp(self):
super(TestGiveRoleToUser, self).setUp()
self.conf = self._get_conf("v2.0", "v3")
self.tenants_client = self._get_clients(self.conf).tenants
self.users_client = self._get_clients(self.conf).users
self.roles_client = self._get_clients(self.conf).roles
self.username = "test_user"
self.tenant_name = "project"
self.role_name = "fake_role"
self.users = {'users':
[{'name': "test_user",
'id': "fake_user_id"},
{'name': "test_user2",
'id': "fake_user_id2"}]}
self.roles = {'roles':
[{'name': "fake_role",
'id': "fake_role_id"},
{'name': "fake_role2",
'id': "fake_role_id2"}]}
@mock.patch('config_tempest.clients.ProjectsClient.' @mock.patch('config_tempest.clients.ProjectsClient.'
'get_project_by_name') 'get_project_by_name')
@mock.patch('tempest.lib.services.identity.v2.' @mock.patch('tempest.lib.services.identity.v2.'
@ -266,12 +209,8 @@ class TestGiveRoleToUser(BaseConfigTempestTest):
{'id': "fake_tenant_id"} {'id': "fake_tenant_id"}
mock_list_users.return_value = self.users mock_list_users.return_value = self.users
mock_list_roles.return_value = self.roles mock_list_roles.return_value = self.roles
tool.give_role_to_user( self.Service.give_role_to_user(
tenants_client=self.tenants_client,
roles_client=self.roles_client,
users_client=self.users_client,
username=self.username, username=self.username,
tenant_name=self.tenant_name,
role_name=self.role_name) role_name=self.role_name)
mock_create_user_role_on_project.assert_called_with( mock_create_user_role_on_project.assert_called_with(
"fake_tenant_id", "fake_user_id", "fake_role_id") "fake_tenant_id", "fake_user_id", "fake_role_id")
@ -300,12 +239,8 @@ class TestGiveRoleToUser(BaseConfigTempestTest):
mock_list_roles.return_value = self.roles mock_list_roles.return_value = self.roles
exc = Exception exc = Exception
self.assertRaises(exc, self.assertRaises(exc,
tool.give_role_to_user, self.Service.give_role_to_user,
tenants_client=self.tenants_client,
roles_client=self.roles_client,
users_client=self.users_client,
username=self.username, username=self.username,
tenant_name=self.tenant_name,
role_name=role_name) role_name=role_name)
@mock.patch('config_tempest.clients.ProjectsClient.' @mock.patch('config_tempest.clients.ProjectsClient.'
@ -330,12 +265,8 @@ class TestGiveRoleToUser(BaseConfigTempestTest):
{'id': "fake_tenant_id"} {'id': "fake_tenant_id"}
mock_list_users.return_value = self.users mock_list_users.return_value = self.users
mock_list_roles.return_value = self.roles mock_list_roles.return_value = self.roles
tool.give_role_to_user( self.Service.give_role_to_user(
tenants_client=self.tenants_client,
roles_client=self.roles_client,
users_client=self.users_client,
username=self.username, username=self.username,
tenant_name=self.tenant_name,
role_name=self.role_name, role_name=self.role_name,
role_required=False) role_required=False)
@ -361,10 +292,6 @@ class TestGiveRoleToUser(BaseConfigTempestTest):
mock_get_project_by_name.return_value = {'id': "fake_tenant_id"} mock_get_project_by_name.return_value = {'id': "fake_tenant_id"}
mock_list_users.return_value = self.users mock_list_users.return_value = self.users
mock_list_roles.return_value = self.roles mock_list_roles.return_value = self.roles
tool.give_role_to_user( self.Service.give_role_to_user(
tenants_client=self.tenants_client,
roles_client=self.roles_client,
users_client=self.users_client,
username=self.username, username=self.username,
tenant_name=self.tenant_name,
role_name=self.role_name) role_name=self.role_name)

118
config_tempest/users.py Normal file
View File

@ -0,0 +1,118 @@
# Copyright 2016, 2018 Red Hat, 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.
from constants import LOG
from tempest.lib import exceptions
class Users(object):
def __init__(self, tenants_client, roles_client, users_client, conf):
"""Init.
:type tenants_client: ProjectsClient object
:type roles_client: RolesClient object from tempest lib
:type users_client: UsersClient object from tempest lib
:type conf: TempestConf object
"""
self.tenants_client = tenants_client
self.roles_client = roles_client
self.users_client = users_client
self._conf = conf
def create_tempest_users(self, orchestration=False):
"""Create users necessary for Tempest if they don't exist already.
:type orchestration: boolean
"""
sec = 'identity'
self.create_user_with_tenant(self._conf.get(sec, 'username'),
self._conf.get(sec, 'password'),
self._conf.get(sec, 'tenant_name'))
self.create_user_with_tenant(self._conf.get(sec, 'alt_username'),
self._conf.get(sec, 'alt_password'),
self._conf.get(sec, 'alt_tenant_name'))
username = self._conf.get_defaulted('auth', 'admin_username')
if username is None:
username = self._conf.get_defaulted('identity', 'admin_username')
self.give_role_to_user(username, role_name='admin')
# Prior to juno, and with earlier juno defaults, users needed to have
# the heat_stack_owner role to use heat stack apis. We assign that role
# to the user if the role is present.
if orchestration:
self.give_role_to_user(self._conf.get('identity', 'username'),
role_name='heat_stack_owner',
role_required=False)
def give_role_to_user(self, username, role_name,
role_required=True):
"""Give the user a role in the project (tenant).
:type username: string
:type role_name: string
:type role_required: boolean
"""
tenant_name = self._conf.get('identity', 'tenant_name')
tenant_id = self.tenants_client.get_project_by_name(tenant_name)['id']
users = self.users_client.list_users()
user_ids = [u['id'] for u in users['users'] if u['name'] == username]
user_id = user_ids[0]
roles = self.roles_client.list_roles()
role_ids = [r['id'] for r in roles['roles'] if r['name'] == role_name]
if not role_ids:
if role_required:
raise Exception("required role %s not found" % role_name)
LOG.debug("%s role not required", role_name)
return
role_id = role_ids[0]
try:
self.roles_client.create_user_role_on_project(tenant_id, user_id,
role_id)
LOG.debug("User '%s' was given the '%s' role in project '%s'",
username, role_name, tenant_name)
except exceptions.Conflict:
LOG.debug("(no change) User '%s' already has the '%s' role in"
" project '%s'", username, role_name, tenant_name)
def create_user_with_tenant(self, username, password, tenant_name):
"""Create a user and a tenant if it doesn't exist.
:type username: string
:type password: string
:type tenant_name: string
"""
LOG.info("Creating user '%s' with tenant '%s' and password '%s'",
username, tenant_name, password)
tenant_description = "Tenant for Tempest %s user" % username
email = "%s@test.com" % username
# create a tenant
try:
self.tenants_client.create_project(name=tenant_name,
description=tenant_description)
except exceptions.Conflict:
LOG.info("(no change) Tenant '%s' already exists", tenant_name)
tenant_id = self.tenants_client.get_project_by_name(tenant_name)['id']
params = {'name': username, 'password': password,
'tenantId': tenant_id, 'email': email}
# create a user
try:
self.users_client.create_user(**params)
except exceptions.Conflict:
LOG.info("User '%s' already exists.", username)