Docstrings and trusty deploy fixes

This commit is contained in:
Liam Young 2016-05-06 19:08:45 +00:00
parent a1d6ab62cd
commit 7605fb49cf
5 changed files with 109 additions and 40 deletions
actions
hooks
lib/charm/openstack
templates

3
actions/__init__.py Normal file

@ -0,0 +1,3 @@
import sys
sys.path.append('lib')

@ -13,6 +13,7 @@ import charmhelpers.fetch as fetch
def setup_git(branch, git_dir, tempest_conf): def setup_git(branch, git_dir, tempest_conf):
"""Clone tempest and symlink in rendered tempest.conf"""
conf = hookenv.config() conf = hookenv.config()
if not os.path.exists(git_dir): if not os.path.exists(git_dir):
git_url = conf['tempest-source'] git_url = conf['tempest-source']
@ -23,36 +24,47 @@ def setup_git(branch, git_dir, tempest_conf):
os.symlink(tempest_conf, conf_symlink) os.symlink(tempest_conf, conf_symlink)
def execute_tox(run_dir, logfile): def execute_tox(run_dir, logfile, tox_target):
"""Trigger tempest run through tox setting proxies if needed"""
env = os.environ.copy() env = os.environ.copy()
conf = hookenv.config() conf = hookenv.config()
if conf.get('http-proxy'): if conf.get('http-proxy'):
env['http_proxy'] = conf['http-proxy'] env['http_proxy'] = conf['http-proxy']
if conf.get('https-proxy'): if conf.get('https-proxy'):
env['https_proxy'] = conf['https-proxy'] env['https_proxy'] = conf['https-proxy']
cmd = ['tox', '-e', 'smoke'] cmd = ['tox', '-e', tox_target]
f = open(logfile, "w") f = open(logfile, "w")
subprocess.call(cmd, cwd=run_dir, stdout=f, stderr=f) subprocess.call(cmd, cwd=run_dir, stdout=f, stderr=f)
def run_smoke_test(): def get_tempest_files(branch_name):
action_args = hookenv.action_get() """Prepare tempets files and directories
branch = action_args['branch']
@return git_dir, logfile, run_dir
"""
log_time_str = time.strftime("%Y%m%d%H%M%S", time.gmtime()) log_time_str = time.strftime("%Y%m%d%H%M%S", time.gmtime())
tempest_git_dir = '{}/tempest-{}'.format( git_dir = '{}/tempest-{}'.format(
tempest.TempestCharm.TEMPEST_ROOT, tempest.TempestCharm.TEMPEST_ROOT,
branch branch_name
) )
tempest_logfile = '{}/run_{}.log'.format( logfile = '{}/run_{}.log'.format(
tempest.TempestCharm.TEMPEST_LOGDIR, tempest.TempestCharm.TEMPEST_LOGDIR,
log_time_str log_time_str
) )
run_dir = git_dir + '/tempest'
return git_dir, logfile, run_dir
def run_test(tox_target):
"""Run smoke tests"""
action_args = hookenv.action_get()
branch_name = action_args['branch']
tempest_git_dir, tempest_logfile, run_dir = get_tempest_files(branch_name)
action_info = { action_info = {
'tempest-logfile': tempest_logfile, 'tempest-logfile': tempest_logfile,
} }
run_dir = tempest_git_dir + '/tempest' setup_git(branch_name, tempest_git_dir, tempest.TempestCharm.TEMPEST_CONF)
setup_git(branch, tempest_git_dir, tempest.TempestCharm.TEMPEST_CONF) execute_tox(run_dir, tempest_logfile, tox_target)
execute_tox(run_dir, tempest_logfile)
hookenv.action_set(action_info) hookenv.action_set(action_info)
@ -60,4 +72,4 @@ if __name__ == '__main__':
# Cloud may have different artifacts (flavors, images etc) since last run # Cloud may have different artifacts (flavors, images etc) since last run
# so rerun handlers file to regenerate config. # so rerun handlers file to regenerate config.
reactive.main() reactive.main()
run_smoke_test() run_test('smoke')

@ -2,8 +2,6 @@
# Wrapper to deal with newer Ubuntu versions that don't have py2 installed # Wrapper to deal with newer Ubuntu versions that don't have py2 installed
# by default. # by default.
declare -a DEPS=('libssl-dev' 'libffi-dev' 'apt' 'python3-netaddr' 'python3-netifaces' 'python3-pip' 'python3-yaml' 'python-cinderclient' 'python-glanceclient' 'python-heatclient' 'python-keystoneclient' 'python-neutronclient' 'python-novaclient' 'python-swiftclient' 'python-ceilometerclient' 'openvswitch-test' 'python3-cinderclient' 'python3-glanceclient' 'python3-heatclient' 'python3-keystoneclient' 'python3-neutronclient' 'python3-novaclient' 'python3-swiftclient' 'python3-ceilometerclient', 'tox')
check_and_install() { check_and_install() {
pkg="${1}" pkg="${1}"
if ! dpkg -s ${pkg} 2>&1 > /dev/null; then if ! dpkg -s ${pkg} 2>&1 > /dev/null; then
@ -11,6 +9,20 @@ check_and_install() {
fi fi
} }
if [[ $(lsb_release -sc) -eq "trusty" ]]; then
# Add a random cloud archive for the Openstack python3 clients
add-apt-repository --yes ppa:ubuntu-cloud-archive/mitaka-staging
apt-get update
# The trusty version of tox is too low (tox version is 1.6, required is at least 2.3.1)
# pip install tox to get around this and die a little inside
pip install tox
else
check_and_install 'tox'
fi
declare -a DEPS=('libssl-dev' 'libffi-dev' 'apt' 'python3-netaddr' 'python3-netifaces' 'python3-pip' 'python3-yaml' 'python-cinderclient' 'python-glanceclient' 'python-heatclient' 'python-keystoneclient' 'python-neutronclient' 'python-novaclient' 'python-swiftclient' 'python-ceilometerclient' 'openvswitch-test' 'python3-cinderclient' 'python3-glanceclient' 'python3-heatclient' 'python3-keystoneclient' 'python3-neutronclient' 'python3-novaclient' 'python3-swiftclient' 'python3-ceilometerclient')
PYTHON="python" PYTHON="python"
for dep in ${DEPS[@]}; do for dep in ${DEPS[@]}; do

@ -12,6 +12,9 @@ tempest_charm = None
def get_charm(): def get_charm():
""" Return a new instance of TempestCharm or existing global instance
@returns TempestCharm
"""
global tempest_charm global tempest_charm
if tempest_charm is None: if tempest_charm is None:
tempest_charm = TempestCharmFactory.charm() tempest_charm = TempestCharmFactory.charm()
@ -20,28 +23,38 @@ def get_charm():
class TempestAdminAdapter(adapters.OpenStackRelationAdapter): class TempestAdminAdapter(adapters.OpenStackRelationAdapter):
"""Inspect relations and provide properties that can be used when
rendering templates"""
interface_type = "identity-admin" interface_type = "identity-admin"
def __init__(self, relation): def __init__(self, relation):
"""Initialise a keystone client and collect user defined config"""
self.kc = None self.kc = None
super(TempestAdminAdapter, self).__init__(relation) super(TempestAdminAdapter, self).__init__(relation)
self.init_keystone_client() self.init_keystone_client()
self.uconfig = hookenv.config() self.uconfig = hookenv.config()
@property
def keystone_info(self):
"""Collection keystone information from keystone relation"""
return self.relation.credentials()
def init_keystone_client(self): def init_keystone_client(self):
"""Initialise keystone client"""
if self.kc: if self.kc:
return return
self.keystone_auth_url = '{}://{}:{}/v2.0'.format( self.keystone_auth_url = '{}://{}:{}/v2.0'.format(
'http', 'http',
self.creds['service_hostname'], self.keystone_info['service_hostname'],
self.creds['service_port'] self.keystone_info['service_port']
) )
auth = { auth = {
'username': self.creds['service_username'], 'username': self.keystone_info['service_username'],
'password': self.creds['service_password'], 'password': self.keystone_info['service_password'],
'auth_url': self.keystone_auth_url, 'auth_url': self.keystone_auth_url,
'tenant_name': self.creds['service_tenant_name'], 'tenant_name': self.keystone_info['service_tenant_name'],
'region_name': self.creds['service_region'], 'region_name': self.keystone_info['service_region'],
} }
try: try:
self.kc = keystoneclient.client.Client(**auth) self.kc = keystoneclient.client.Client(**auth)
@ -50,6 +63,10 @@ class TempestAdminAdapter(adapters.OpenStackRelationAdapter):
@property @property
def ec2_creds(self): def ec2_creds(self):
"""Generate EC2 style tokens or return existing EC2 tokens
@returns {'access_token' token1, 'secret_token': token2}
"""
self.init_keystone_client() self.init_keystone_client()
if not self.kc: if not self.kc:
return {} return {}
@ -62,6 +79,10 @@ class TempestAdminAdapter(adapters.OpenStackRelationAdapter):
@property @property
def image_info(self): def image_info(self):
"""Return image ids for the user-defined image names
@returns {'image_id' id1, 'image_alt_id': id2}
"""
self.init_keystone_client() self.init_keystone_client()
glance_endpoint = self.kc.service_catalog.url_for( glance_endpoint = self.kc.service_catalog.url_for(
service_type='image', service_type='image',
@ -81,6 +102,11 @@ class TempestAdminAdapter(adapters.OpenStackRelationAdapter):
@property @property
def network_info(self): def network_info(self):
"""Return public network and router ids for user-defined router and
network names
@returns {'image_id' id1, 'image_alt_id': id2}
"""
self.init_keystone_client() self.init_keystone_client()
neutron_ep = self.kc.service_catalog.url_for( neutron_ep = self.kc.service_catalog.url_for(
service_type='network', service_type='network',
@ -110,6 +136,10 @@ class TempestAdminAdapter(adapters.OpenStackRelationAdapter):
@property @property
def compute_info(self): def compute_info(self):
"""Return flavor ids for user-defined flavors
@returns {'flavor_id' id1, 'flavor_alt_id': id2}
"""
self.init_keystone_client() self.init_keystone_client()
nova_ep = self.kc.service_catalog.url_for( nova_ep = self.kc.service_catalog.url_for(
service_type='compute', service_type='compute',
@ -122,9 +152,9 @@ class TempestAdminAdapter(adapters.OpenStackRelationAdapter):
url.netloc.split(':')[0]) url.netloc.split(':')[0])
try: try:
nova_client = novaclient.client.Client( nova_client = novaclient.client.Client(
self.creds['service_username'], self.keystone_info['service_username'],
self.creds['service_password'], self.keystone_info['service_password'],
self.creds['service_tenant_name'], self.keystone_info['service_tenant_name'],
self.keystone_auth_url, self.keystone_auth_url,
) )
for flavor in nova_client.flavors.list(): for flavor in nova_client.flavors.list():
@ -136,24 +166,26 @@ class TempestAdminAdapter(adapters.OpenStackRelationAdapter):
hookenv.log("Nova is not ready, deferring nova query") hookenv.log("Nova is not ready, deferring nova query")
return compute_info return compute_info
@property
def creds(self):
return self.relation.credentials()
@property
def auth_url(self):
bob = self.get_keystone_client() or 'bugger'
return bob
def get_present_services(self): def get_present_services(self):
"""Query keystone catalogue for a list for registered services
@returns [svc1, svc2, ...]: List of registered services
"""
self.init_keystone_client() self.init_keystone_client()
services = [svc.name for svc in self.kc.services.list() if svc.enabled] services = [svc.name for svc in self.kc.services.list() if svc.enabled]
# if DashboardRelation().get('dashboard_url'):
# services.append('horizon')
return services return services
@property @property
def service_info(self): def service_info(self):
"""Assemble a list of services tempest should tests
Compare the list of keystone registered services with the services the
user has requested be tested. If in 'auto' mode test all services
registered in keystone.
@returns [svc1, svc2, ...]: List of services to test
"""
service_info = {} service_info = {}
tempest_candidates = ['ceilometer', 'cinder', 'glance', 'heat', tempest_candidates = ['ceilometer', 'cinder', 'glance', 'heat',
'horizon', 'ironic', 'neutron', 'nova', 'horizon', 'ironic', 'neutron', 'nova',
@ -181,7 +213,7 @@ class TempestAdminAdapter(adapters.OpenStackRelationAdapter):
class TempestAdapters(adapters.OpenStackRelationAdapters): class TempestAdapters(adapters.OpenStackRelationAdapters):
""" """
Adapters class for the Designate charm. Adapters class for the Tempest charm.
""" """
relation_adapters = { relation_adapters = {
'identity_admin': TempestAdminAdapter, 'identity_admin': TempestAdminAdapter,
@ -194,18 +226,26 @@ class TempestAdapters(adapters.OpenStackRelationAdapters):
class TempestConfigurationAdapter(adapters.ConfigurationAdapter): class TempestConfigurationAdapter(adapters.ConfigurationAdapter):
"""
Manipulate user supplied config as needed
"""
def __init__(self): def __init__(self):
super(TempestConfigurationAdapter, self).__init__() super(TempestConfigurationAdapter, self).__init__()
class TempestCharm(charm.OpenStackCharm): class TempestCharm(charm.OpenStackCharm):
"""Directories and files used for running tempest"""
TEMPEST_ROOT = '/var/lib/tempest/' TEMPEST_ROOT = '/var/lib/tempest/'
TEMPEST_LOGDIR = TEMPEST_ROOT + '/logs' TEMPEST_LOGDIR = TEMPEST_ROOT + '/logs'
TEMPEST_CONF = TEMPEST_ROOT + '/tempest.conf' TEMPEST_CONF = TEMPEST_ROOT + '/tempest.conf'
"""pip.conf for proxy settings etc"""
PIP_CONF = '/root/.pip/pip.conf' PIP_CONF = '/root/.pip/pip.conf'
"""List of packages charm should install
XXX The install hook is currently installing most packages ahead of
this because modules like keystoneclient are needed at load time
"""
packages = [ packages = [
'git', 'testrepository', 'subunit', 'python-nose', 'python-lxml', 'git', 'testrepository', 'subunit', 'python-nose', 'python-lxml',
'python-boto', 'python-junitxml', 'python-subunit', 'python-boto', 'python-junitxml', 'python-subunit',
@ -217,10 +257,14 @@ class TempestCharm(charm.OpenStackCharm):
'python3-keystoneclient', 'python3-neutronclient', 'python3-keystoneclient', 'python3-neutronclient',
'python3-novaclient', 'python3-swiftclient', 'python3-novaclient', 'python3-swiftclient',
'python3-ceilometerclient', 'openvswitch-common', 'libffi-dev', 'python3-ceilometerclient', 'openvswitch-common', 'libffi-dev',
'libssl-dev', 'python-dev', 'python-cffi', 'tox' 'libssl-dev', 'python-dev', 'python-cffi'
] ]
"""Use the Tempest specific adapters"""
adapters_class = TempestAdapters adapters_class = TempestAdapters
"""Tempest has no running services so no services need restarting on
config file change
"""
restart_map = { restart_map = {
TEMPEST_CONF: [], TEMPEST_CONF: [],
PIP_CONF: [], PIP_CONF: [],

@ -1,5 +1,3 @@
# {{ identity_admin.auth_url }}
# {{ identity_admin.creds }}
# {{ identity_admin.ec2_creds.access_token }} # {{ identity_admin.ec2_creds.access_token }}
# {{ identity_admin.image_info }} # {{ identity_admin.image_info }}
# {{ identity_admin.image_info.image_id }} # {{ identity_admin.image_info.image_id }}
@ -39,8 +37,8 @@ login_url={{ dashboard_url }}/horizon/auth/login/
[data_processing] [data_processing]
[debug] [debug]
[identity] [identity]
uri=http://{{ identity_admin.creds.service_hostname }}:5000/v2.0 uri=http://{{ identity_admin.keystone_info.service_hostname }}:5000/v2.0
uri_v3=http://{{ identity_admin.creds.service_hostname }}:5000/v3 uri_v3=http://{{ identity_admin.keystone_info.service_hostname }}:5000/v3
admin_username=admin admin_username=admin
admin_tenant_name=admin admin_tenant_name=admin
admin_password=openstack admin_password=openstack