diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | devstack/README.rst | 27 | ||||
-rw-r--r-- | devstack/plugin.sh | 92 | ||||
-rw-r--r-- | devstack/settings | 3 | ||||
-rw-r--r-- | test-requirements.txt | 13 | ||||
-rw-r--r-- | tests/config.py | 30 | ||||
-rw-r--r-- | tests/identity_auth.py | 41 | ||||
-rw-r--r-- | tests/image.py | 77 | ||||
-rw-r--r-- | tests/lib/installers/fuel | 38 | ||||
-rw-r--r-- | tests/logger.py | 2 | ||||
-rw-r--r-- | tests/main.py | 61 | ||||
-rw-r--r-- | tests/monitor.py | 5 | ||||
-rw-r--r-- | tests/os_clients.py | 21 | ||||
-rwxr-xr-x | tests/run.sh | 51 | ||||
-rw-r--r-- | tox.ini | 26 |
15 files changed, 457 insertions, 33 deletions
@@ -5,3 +5,6 @@ /docs_output/ /releng/ /tests/*.img + +#Build results +.tox diff --git a/devstack/README.rst b/devstack/README.rst new file mode 100644 index 00000000..cd836f13 --- /dev/null +++ b/devstack/README.rst @@ -0,0 +1,27 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +.. (c) 2017 OPNFV. + +==================================== +Enabling OPNFV Doctor using DevStack +==================================== + +This directory contains the files necessary to run OpenStack with enabled +OPNFV Doctor in DevStack. + +To configure DevStack to enable OPNFV Doctor edit +``${DEVSTACK_DIR}/local.conf`` file and add:: + + enable_plugin aodh http://git.openstack.org/openstack/aodh + enable_plugin panko https://git.openstack.org/openstack/panko + enable_plugin ceilometer https://git.openstack.org/openstack/ceilometer + enable_plugin osprofiler https://git.openstack.org/openstack/osprofiler + enable_plugin doctor https://git.opnfv.org/doctor + +to the ``[[local|localrc]]`` section. + +.. note:: The order of enabling plugins matters. + +Run DevStack as normal:: + + $ ./stack.sh diff --git a/devstack/plugin.sh b/devstack/plugin.sh new file mode 100644 index 00000000..a7f6a63b --- /dev/null +++ b/devstack/plugin.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +############################################################################## +# Copyright (c) 2017 ZTE Corporation and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Defaults +# -------- + +CONF_FILES=( + $CINDER_CONF + $HEAT_CONF + $KEYSTONE_CONF + $NOVA_CONF + $NEUTRON_CONF + $GLANCE_API_CONF + $GLANCE_REGISTRY_CONF +# Supported by osprofiler but not used in doctor at the moment +# $TROVE_CONF +# $TROVE_CONDUCTOR_CONF +# $TROVE_GUESTAGENT_CONF +# $TROVE_TASKMANAGER_CONF +# $SENLIN_CONF +# $MAGNUM_CONF +# $ZUN_CONF +) + +function install_doctor { + # no-op + : +} + +function configure_doctor { + for conf in ${CONF_FILES[@]}; do + if [ -f $conf ] + then + iniset $conf profiler enabled true + iniset $conf profiler trace_sqlalchemy true + iniset $conf profiler hmac_keys $(iniget $conf profiler hmac_keys),${DOCTOR_HMAC_KEYS:=doctor} + iniset $conf profiler connection_string ${OSPROFILER_CONNECTION_STRING:=redis://127.0.0.1:6379} + fi + done +} + +function init_doctor { + # no-op + : +} + +# check for service enabled +if is_service_enabled doctor; then + + if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then + # Set up system services + echo_summary "Configuring system services Doctor" + # install_package cowsay + + elif [[ "$1" == "stack" && "$2" == "install" ]]; then + # Perform installation of service source + echo_summary "Installing Doctor" + install_doctor + + elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then + # Configure after the other layer 1 and 2 services have been configured + echo_summary "Configuring Doctor" + configure_doctor + + elif [[ "$1" == "stack" && "$2" == "extra" ]]; then + # Initialize and start the doctor service + echo_summary "Initializing Doctor" + init_doctor + fi + + if [[ "$1" == "unstack" ]]; then + # Shut down doctor services + # no-op + : + fi + + if [[ "$1" == "clean" ]]; then + # Remove state and transient data + # Remember clean.sh first calls unstack.sh + # no-op + : + fi +fi + diff --git a/devstack/settings b/devstack/settings new file mode 100644 index 00000000..83e02e35 --- /dev/null +++ b/devstack/settings @@ -0,0 +1,3 @@ +# setting file for doctor + +enable_service doctor diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 00000000..2928e0f7 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,13 @@ +Flask==0.10.1 +paramiko==1.16.0 +scp==0.10.2 +requests>=2.8.0 +oslo.config==3.22.0 # Apache-2.0 +python-openstackclient==2.3.0 +python-ceilometerclient==2.6.2 +python-keystoneclient==3.5.0 +python-neutronclient==6.0.0 +python-novaclient==6.0.0 +python-congressclient==1.5.0 +python-glanceclient==2.5.0 +virtualenv==15.1.0 diff --git a/tests/config.py b/tests/config.py new file mode 100644 index 00000000..2288d36e --- /dev/null +++ b/tests/config.py @@ -0,0 +1,30 @@ +##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from oslo_config import cfg
+
+import image
+import os_clients
+
+
+def list_opts():
+ return [
+ ('os_clients', os_clients.OPTS),
+ ('image', image.IMAGE_OPTS),
+ ]
+
+
+def prepare_conf(conf=None):
+ if conf is None:
+ conf = cfg.ConfigOpts()
+
+ for group, options in list_opts():
+ conf.register_opts(list(options),
+ group=None if group == 'DEFAULT' else group)
+
+ return conf
diff --git a/tests/identity_auth.py b/tests/identity_auth.py index 4726ca37..ffecc68a 100644 --- a/tests/identity_auth.py +++ b/tests/identity_auth.py @@ -9,26 +9,35 @@ import os -from keystoneauth1.identity import v2 -from keystoneauth1.identity import v3 +from keystoneauth1 import loading +from keystoneauth1 import session def get_identity_auth(): auth_url = os.environ['OS_AUTH_URL'] username = os.environ['OS_USERNAME'] password = os.environ['OS_PASSWORD'] - user_domain_name = os.environ.get('OS_USER_DOMAIN_NAME') + user_domain_name = os.environ.get('OS_USER_DOMAIN_NAME') or 'default' + user_domain_id = os.environ.get('OS_USER_DOMAIN_ID') or 'default' project_name = os.environ.get('OS_PROJECT_NAME') or os.environ.get('OS_TENANT_NAME') - project_domain_name = os.environ.get('OS_PROJECT_DOMAIN_NAME') - if auth_url.endswith('v3'): - return v3.Password(auth_url=auth_url, - username=username, - password=password, - user_domain_name=user_domain_name, - project_name=project_name, - project_domain_name=project_domain_name) - else: - return v2.Password(auth_url=auth_url, - username=username, - password=password, - tenant_name=project_name) + project_domain_name = os.environ.get('OS_PROJECT_DOMAIN_NAME') or 'default' + project_domain_id = os.environ.get('OS_PROJECT_DOMAIN_ID') or 'default' + + loader = loading.get_plugin_loader('password') + return loader.load_from_options( + auth_url=auth_url, + username=username, + password=password, + user_domain_name=user_domain_name, + user_domain_id=user_domain_id, + project_name=project_name, + tenant_name=project_name, + project_domain_name=project_domain_name, + project_domain_id=project_domain_id) + + +def get_session(auth=None): + """Get a user credentials auth session.""" + if auth is None: + auth = get_identity_auth() + return session.Session(auth=auth) diff --git a/tests/image.py b/tests/image.py new file mode 100644 index 00000000..0b4a3d72 --- /dev/null +++ b/tests/image.py @@ -0,0 +1,77 @@ +############################################################################## +# Copyright (c) 2017 ZTE Corporation and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import os +import urllib2 + +from oslo_config import cfg + +from identity_auth import get_session +from os_clients import glance_client +import logger as doctor_log + +IMAGE_OPTS = [ + cfg.StrOpt('name', + default=os.environ.get('IMAGE_NAME', 'cirros'), + help='the name of test image', + required=True), + cfg.StrOpt('format', + default='qcow2', + help='the format of test image', + required=True), + cfg.StrOpt('file_name', + default='cirros.img', + help='the name of image file', + required=True), + cfg.StrOpt('url', + default='https://launchpad.net/cirros/trunk/0.3.0/+download/cirros-0.3.0-x86_64-disk.img', + help='the url where to get the image', + required=True), +] + +LOG = doctor_log.Logger('doctor').getLogger() + + +class Image(object): + + def __init__(self, conf): + self.conf = conf + self.glance = \ + glance_client(conf.os_clients.glance_version, + get_session()) + self.use_existing_image = False + self.image = None + + def create(self): + LOG.info('image create start......') + + images = {image.name: image for image in self.glance.images.list()} + if self.conf.image.name not in images: + if not os.path.exists(self.conf.image.file_name): + resp = urllib2.urlopen(self.conf.image.url) + with open(self.conf.image.file_name, "wb") as file: + file.write(resp.read()) + self.image = self.glance.images.create(name=self.conf.image.name, + disk_format=self.conf.image.format, + container_format="bare", + visibility="public") + self.glance.images.upload(self.image['id'], + open(self.conf.image.file_name, 'rb')) + else: + self.use_existing_image = True + self.image = images[self.conf.image.name] + + LOG.info('image create end......') + + def delete(self): + LOG.info('image delete start.......') + + if not self.use_existing_image and self.image: + self.glance.images.delete(self.image['id']) + + LOG.info('image delete end.......') diff --git a/tests/lib/installers/fuel b/tests/lib/installers/fuel index da0de34b..0c56963c 100644 --- a/tests/lib/installers/fuel +++ b/tests/lib/installers/fuel @@ -74,6 +74,31 @@ function installer_apply_patches { ip netns exec haproxy /usr/lib/ocf/resource.d/fuel/ns_haproxy restart fi fi + + np_conf=/etc/nova/policy.json + if [ -e $np_conf ]; then + entry="os_compute_api:servers:show:host_status" + new="rule:admin_or_owner" + np_backup="${np_conf}-doctor-saved" + if grep -q "${entry}.*${new}" $np_conf; then + echo "Not modifying nova policy" + elif grep -q "${entry}" $np_conf; then + echo "modify nova policy" + cp $np_conf $np_backup + oldline=$(grep "$entry" $np_conf) + newline=$(echo "$oldline" | sed "s/rule.*\"/$new\"/") + sed -i "s/$oldline/$newline/" $np_conf + service nova-api restart + else + echo "add nova policy" + cp $np_conf $np_backup + sed -i "/{/a \ \"${entry}\": \"$new\"" $np_conf + service nova-api restart + fi + else + # TODO(tojuvone) policy.json might not exists in Ocata. + echo "$np_conf does not exist!!!" + fi ' > installer_apply_patches_$node.log 2>&1 done } @@ -83,6 +108,8 @@ function setup_installer { installer_get_ssh_keys get_controller_ips installer_apply_patches + #Might take a moment for nova-api to restart + sleep 20 if ! openstack flavor show $VM_FLAVOR ; then openstack flavor create --ram 512 --disk 1 $VM_FLAVOR \ && touch created_doctor_flavor @@ -138,6 +165,17 @@ function installer_revert_patches { sed -ie "/# added by doctor script/d" $ep_conf service ceilometer-agent-notification restart fi + + np_conf=/etc/nova/policy.json + entry="os_compute_api:servers:show:host_status" + if [ -e $np_conf ]; then + np_backup="${np_conf}-doctor-saved" + if [ -e $np_backup ]; then + cp -f $np_backup $np_conf + rm $np_backup + service nova-api restart + fi + fi ' >> installer_apply_patches_$node.log 2>&1 done } diff --git a/tests/logger.py b/tests/logger.py index a4f33234..72043ab3 100644 --- a/tests/logger.py +++ b/tests/logger.py @@ -16,7 +16,7 @@ import logging import os -class Logger: +class Logger(object): def __init__(self, logger_name): CI_DEBUG = os.getenv('CI_DEBUG') diff --git a/tests/main.py b/tests/main.py new file mode 100644 index 00000000..50e0821b --- /dev/null +++ b/tests/main.py @@ -0,0 +1,61 @@ +############################################################################## +# Copyright (c) 2017 ZTE Corporation and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import sys + +import config +from image import Image +import logger as doctor_log + + +LOG = doctor_log.Logger('doctor').getLogger() + + +class DoctorTest(object): + + def __init__(self, conf): + self.conf = conf + self.image = Image(self.conf) + + def run(self): + """run doctor test""" + try: + LOG.info('doctor test starting.......') + # prepare the cloud env + + # preparing VM image... + self.image.create() + + # creating test user... + + # creating VM... + + # creating alarm... + + # starting doctor sample components... + + # injecting host failure... + + # verify the test results + except Exception as e: + LOG.error('doctor test failed, Exception=%s' % e) + sys.exit(1) + finally: + self.image.delete() + + +def main(): + """doctor main""" + conf = config.prepare_conf() + + doctor = DoctorTest(conf) + doctor.run() + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tests/monitor.py b/tests/monitor.py index 8e8aa7a5..8244dc88 100644 --- a/tests/monitor.py +++ b/tests/monitor.py @@ -17,6 +17,7 @@ import socket import sys import time +from keystoneauth1 import session from congressclient.v1 import client import identity_auth @@ -48,8 +49,8 @@ class DoctorMonitorSample(object): self.inspector_url = 'http://127.0.0.1:12345/events' elif self.inspector_type == 'congress': auth=identity_auth.get_identity_auth() - sess=session.Session(auth=auth) - congress = client.Client(session=sess, service_type='policy') + self.session=session.Session(auth=auth) + congress = client.Client(session=self.session, service_type='policy') ds = congress.list_datasources()['results'] doctor_ds = next((item for item in ds if item['driver'] == 'doctor'), None) diff --git a/tests/os_clients.py b/tests/os_clients.py new file mode 100644 index 00000000..2eb406e0 --- /dev/null +++ b/tests/os_clients.py @@ -0,0 +1,21 @@ +##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from oslo_config import cfg
+
+import glanceclient.client as glanceclient
+
+
+OPTS = [
+ cfg.StrOpt('glance_version', default='2', help='glance version'),
+]
+
+
+def glance_client(version, session):
+ return glanceclient.Client(version=version,
+ session=session)
diff --git a/tests/run.sh b/tests/run.sh index 7f95a8a4..daf23946 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -28,8 +28,7 @@ CONSUMER_PORT=12346 DOCTOR_USER=doctor DOCTOR_PW=doctor DOCTOR_PROJECT=doctor -#TODO: change back to `_member_` when JIRA DOCTOR-55 is done -DOCTOR_ROLE=admin +DOCTOR_ROLE=_member_ PROFILER_TYPE=${PROFILER_TYPE:-none} PYTHON_ENABLE=${PYTHON_ENABLE:-false} @@ -40,13 +39,15 @@ as_doctor_user="--os-username $DOCTOR_USER --os-password $DOCTOR_PW # NOTE: ceilometer command still requires '--os-tenant-name'. #ceilometer="ceilometer ${as_doctor_user/--os-project-name/--os-tenant-name}" ceilometer="ceilometer $as_doctor_user" +as_admin_user="--os-username admin --os-project-name $DOCTOR_PROJECT + --os-tenant-name $DOCTOR_PROJECT" # Functions get_compute_host_info() { - # get computer host info which first VM boot in - COMPUTE_HOST=$(openstack $as_doctor_user server show ${VM_BASENAME}1 | + # get computer host info which first VM boot in as admin user + COMPUTE_HOST=$(openstack $as_admin_user server show ${VM_BASENAME}1 | grep "OS-EXT-SRV-ATTR:host" | awk '{ print $4 }') compute_host_in_undercloud=${COMPUTE_HOST%%.*} die_if_not_set $LINENO COMPUTE_HOST "Failed to get compute hostname" @@ -109,17 +110,25 @@ register_image() { create_test_user() { openstack project list | grep -q " $DOCTOR_PROJECT " || { - openstack project create "$DOCTOR_PROJECT" + openstack project create --description "Doctor Project" \ + "$DOCTOR_PROJECT" } openstack user list | grep -q " $DOCTOR_USER " || { openstack user create "$DOCTOR_USER" --password "$DOCTOR_PW" \ --project "$DOCTOR_PROJECT" } - openstack role show "$DOCTOR_ROLE" || { + openstack role show "$DOCTOR_ROLE" | grep -q " $DOCTOR_ROLE " || { openstack role create "$DOCTOR_ROLE" } - openstack role add "$DOCTOR_ROLE" --user "$DOCTOR_USER" \ - --project "$DOCTOR_PROJECT" + openstack role assignment list --user "$DOCTOR_USER" \ + --project "$DOCTOR_PROJECT" --names | grep -q " $DOCTOR_ROLE " || { + openstack role add "$DOCTOR_ROLE" --user "$DOCTOR_USER" \ + --project "$DOCTOR_PROJECT" + } + openstack role assignment list --user admin --project "$DOCTOR_PROJECT" \ + --names | grep -q " admin " || { + openstack role add admin --user admin --project "$DOCTOR_PROJECT" + } # tojuvone: openstack quota show is broken and have to use nova # https://bugs.launchpad.net/manila/+bug/1652118 # Note! while it is encouraged to use openstack client it has proven @@ -141,6 +150,24 @@ create_test_user() { fi } +remove_test_user() { + openstack project list | grep -q " $DOCTOR_PROJECT " && { + openstack role assignment list --user admin \ + --project "$DOCTOR_PROJECT" --names | grep -q " admin " && { + openstack role remove admin --user admin --project "$DOCTOR_PROJECT" + } + openstack user list | grep -q " $DOCTOR_USER " && { + openstack role assignment list --user "$DOCTOR_USER" \ + --project "$DOCTOR_PROJECT" --names | grep -q " $DOCTOR_ROLE " && { + openstack role remove "$DOCTOR_ROLE" --user "$DOCTOR_USER" \ + --project "$DOCTOR_PROJECT" + } + openstack user delete "$DOCTOR_USER" + } + openstack project delete "$DOCTOR_PROJECT" + } +} + boot_vm() { # test VM done with test user, so can test non-admin @@ -436,12 +463,8 @@ cleanup() { if [[ "$use_existing_image" == false ]] ; then [ -n "$image_id" ] && openstack image delete "$image_id" fi - openstack role remove "$DOCTOR_ROLE" --user "$DOCTOR_USER" \ - --project "$DOCTOR_PROJECT" - openstack project delete "$DOCTOR_PROJECT" - openstack user delete "$DOCTOR_USER" - # NOTE: remove role only for doctor test. - #openstack role delete "$DOCTOR_ROLE" + + remove_test_user cleanup_installer cleanup_inspector diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..2f74083f --- /dev/null +++ b/tox.ini @@ -0,0 +1,26 @@ +[tox] +minversion = 2.3.1 +envlist = verify +skipsdist = True + +[testenv] +install_command = pip install -U {opts} {packages} +setenv = VIRTUAL_ENV={envdir} +deps = -r{toxinidir}/test-requirements.txt +passenv = + OS_AUTH_URL + OS_USERNAME + OS_PASSWORD + OS_USER_DOMAIN_NAME + OS_PROJECT_NAME + OS_TENANT_NAME + OS_PROJECT_DOMAIN_NAME + IMAGE_NAME + VM_COUNT + PROFILER_TYPE + PYTHON_ENABLE + CI_DEBUG + +[testenv:verify] +changedir = {toxinidir}/tests +commands = python main.py |