rally-openstack/rally_openstack/verification/tempest/config.py

225 lines
9.7 KiB
Python

# Copyright 2014: Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import inspect
import os
import six
from six.moves import configparser
from rally.common import cfg
from rally.common import logging
from rally import exceptions
from rally.verification import utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class TempestConfigfileManager(object):
"""Class to create a Tempest config file."""
def __init__(self, deployment):
self.credential = deployment.get_credentials_for("openstack")["admin"]
if not self.credential:
raise exceptions.ValidationError(
"Failed to configure 'tempest' for '%s' environment since "
"admin credentials for OpenStack platform is missed there." %
deployment["name"]
)
self.clients = self.credential.clients()
self.available_services = self.clients.services().values()
self.conf = configparser.ConfigParser()
def _get_service_type_by_service_name(self, service_name):
for s_type, s_name in self.clients.services().items():
if s_name == service_name:
return s_type
def _configure_auth(self, section_name="auth"):
self.conf.set(section_name, "admin_username",
self.credential.username)
self.conf.set(section_name, "admin_password",
self.credential.password)
self.conf.set(section_name, "admin_project_name",
self.credential.tenant_name)
# Keystone v3 related parameter
self.conf.set(section_name, "admin_domain_name",
self.credential.user_domain_name or "Default")
# Sahara has two service types: 'data_processing' and 'data-processing'.
# 'data_processing' is deprecated, but it can be used in previous OpenStack
# releases. So we need to configure the 'catalog_type' option to support
# environments where 'data_processing' is used as service type for Sahara.
def _configure_data_processing(self, section_name="data-processing"):
if "sahara" in self.available_services:
self.conf.set(section_name, "catalog_type",
self._get_service_type_by_service_name("sahara"))
def _configure_identity(self, section_name="identity"):
self.conf.set(section_name, "region",
self.credential.region_name)
# discover keystone versions
def get_versions(auth_url):
from keystoneauth1 import discover
from keystoneauth1 import session
temp_session = session.Session(
verify=(self.credential.https_cacert
or not self.credential.https_insecure),
timeout=CONF.openstack_client_http_timeout)
data = discover.Discover(temp_session, auth_url).version_data()
return dict([(v["version"][0], v["url"]) for v in data])
# check the original auth_url without cropping versioning to identify
# the default version
versions = get_versions(self.credential.auth_url)
cropped_auth_url = self.clients.keystone._remove_url_version()
if cropped_auth_url == self.credential.auth_url:
# the given auth_url doesn't contain version
if set(versions.keys()) == {2, 3}:
# ok, both versions of keystone are enabled, we can take urls
# there
uri = versions[2]
uri_v3 = versions[3]
target_version = 3
elif set(versions.keys()) == {2} or set(versions.keys()) == {3}:
# only one version is available while discovering
# get the most recent version
target_version = sorted(versions.keys())[-1]
if target_version == 2:
uri = versions[2]
uri_v3 = os.path.join(cropped_auth_url, "v3")
else:
# keystone v2 is disabled. let's do it explicitly
self.conf.set("identity-feature-enabled", "api_v2",
"False")
uri_v3 = versions[3]
uri = os.path.join(cropped_auth_url, "v2.0")
else:
# Does Keystone released new version of API ?!
LOG.debug("Discovered keystone versions: %s" % versions)
raise exceptions.RallyException("Failed to discover keystone "
"auth urls.")
else:
if self.credential.auth_url.rstrip("/").endswith("v2.0"):
uri = self.credential.auth_url
uri_v3 = uri.replace("/v2.0", "/v3")
target_version = 2
else:
uri_v3 = self.credential.auth_url
uri = uri_v3.replace("/v3", "/v2.0")
target_version = 3
self.conf.set(section_name, "auth_version", "v%s" % target_version)
self.conf.set(section_name, "uri", uri)
self.conf.set(section_name, "uri_v3", uri_v3)
if self.credential.endpoint_type:
self.conf.set(section_name, "v2_endpoint_type",
self.credential.endpoint_type)
self.conf.set(section_name, "v3_endpoint_type",
self.credential.endpoint_type)
self.conf.set(section_name, "disable_ssl_certificate_validation",
str(self.credential.https_insecure))
self.conf.set(section_name, "ca_certificates_file",
self.credential.https_cacert)
# The compute section is configured in context class for Tempest resources.
# Options which are configured there: 'image_ref', 'image_ref_alt',
# 'flavor_ref', 'flavor_ref_alt'.
def _configure_network(self, section_name="network"):
if "neutron" in self.available_services:
neutronclient = self.clients.neutron()
public_nets = [
net for net in neutronclient.list_networks()["networks"]
if net["status"] == "ACTIVE" and net["router:external"] is True
]
if public_nets:
net_id = public_nets[0]["id"]
net_name = public_nets[0]["name"]
self.conf.set(section_name, "public_network_id", net_id)
self.conf.set(section_name, "floating_network_name", net_name)
else:
novaclient = self.clients.nova()
net_name = next(net.human_id for net in novaclient.networks.list()
if net.human_id is not None)
self.conf.set("compute", "fixed_network_name", net_name)
self.conf.set("validation", "network_for_ssh", net_name)
def _configure_network_feature_enabled(
self, section_name="network-feature-enabled"):
if "neutron" in self.available_services:
neutronclient = self.clients.neutron()
extensions = neutronclient.list_ext("extensions", "/extensions",
retrieve_all=True)
aliases = [ext["alias"] for ext in extensions["extensions"]]
aliases_str = ",".join(aliases)
self.conf.set(section_name, "api_extensions", aliases_str)
def _configure_object_storage(self, section_name="object-storage"):
self.conf.set(section_name, "operator_role",
CONF.openstack.swift_operator_role)
self.conf.set(section_name, "reseller_admin_role",
CONF.openstack.swift_reseller_admin_role)
def _configure_service_available(self, section_name="service_available"):
services = ["cinder", "glance", "heat", "ironic", "neutron", "nova",
"sahara", "swift"]
for service in services:
# Convert boolean to string because ConfigParser fails
# on attempt to get option with boolean value
self.conf.set(section_name, service,
str(service in self.available_services))
def _configure_validation(self, section_name="validation"):
if "neutron" in self.available_services:
self.conf.set(section_name, "connect_method", "floating")
else:
self.conf.set(section_name, "connect_method", "fixed")
def _configure_orchestration(self, section_name="orchestration"):
self.conf.set(section_name, "stack_owner_role",
CONF.openstack.heat_stack_owner_role)
self.conf.set(section_name, "stack_user_role",
CONF.openstack.heat_stack_user_role)
def create(self, conf_path, extra_options=None):
self.conf.read(os.path.join(os.path.dirname(__file__), "config.ini"))
for name, method in inspect.getmembers(self, inspect.ismethod):
if name.startswith("_configure_"):
method()
if extra_options:
utils.add_extra_options(extra_options, self.conf)
with open(conf_path, "w") as configfile:
self.conf.write(configfile)
raw_conf = six.StringIO()
raw_conf.write("# Some empty values of options will be replaced while "
"creating required resources (images, flavors, etc).\n")
self.conf.write(raw_conf)
return raw_conf.getvalue()