diff options
-rw-r--r-- | devstack/README.rst | 27 | ||||
-rw-r--r-- | devstack/plugin.sh | 92 | ||||
-rw-r--r-- | devstack/settings | 3 | ||||
-rw-r--r-- | tests/config.py | 8 | ||||
-rw-r--r-- | tests/consumer.py | 1 | ||||
-rw-r--r-- | tests/identity_auth.py | 10 | ||||
-rw-r--r-- | tests/image.py | 45 | ||||
-rw-r--r-- | tests/inspector.py | 1 | ||||
-rw-r--r-- | tests/main.py | 33 | ||||
-rw-r--r-- | tests/monitor.py | 7 | ||||
-rw-r--r-- | tests/os_clients.py | 13 | ||||
-rwxr-xr-x | tests/run.sh | 11 | ||||
-rw-r--r-- | tests/user.py | 163 |
13 files changed, 363 insertions, 51 deletions
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/tests/config.py b/tests/config.py index 2288d36e..7a0bef2d 100644 --- a/tests/config.py +++ b/tests/config.py @@ -6,16 +6,20 @@ # which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+import itertools
from oslo_config import cfg
import image
import os_clients
+import user
def list_opts():
return [
- ('os_clients', os_clients.OPTS),
- ('image', image.IMAGE_OPTS),
+ ('DEFAULT', itertools.chain(
+ os_clients.OPTS,
+ image.OPTS,
+ user.OPTS))
]
diff --git a/tests/consumer.py b/tests/consumer.py index 3c012b4f..042cf20a 100644 --- a/tests/consumer.py +++ b/tests/consumer.py @@ -12,7 +12,6 @@ from flask import Flask from flask import request import json import logger as doctor_log -import os import time LOG = doctor_log.Logger('doctor_consumer').getLogger() diff --git a/tests/identity_auth.py b/tests/identity_auth.py index a40c41cf..ffecc68a 100644 --- a/tests/identity_auth.py +++ b/tests/identity_auth.py @@ -17,9 +17,11 @@ 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') + 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( @@ -27,9 +29,11 @@ def get_identity_auth(): 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_name=project_domain_name, + project_domain_id=project_domain_id) def get_session(auth=None): diff --git a/tests/image.py b/tests/image.py index 0b4a3d72..453322b8 100644 --- a/tests/image.py +++ b/tests/image.py @@ -7,71 +7,68 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## import os -import urllib2 +import urllib.request 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', +OPTS = [ + cfg.StrOpt('image_name', default=os.environ.get('IMAGE_NAME', 'cirros'), help='the name of test image', required=True), - cfg.StrOpt('format', + cfg.StrOpt('image_format', default='qcow2', help='the format of test image', required=True), - cfg.StrOpt('file_name', + cfg.StrOpt('image_filename', default='cirros.img', help='the name of image file', required=True), - cfg.StrOpt('url', + cfg.StrOpt('image_download_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): + def __init__(self, conf, log): self.conf = conf + self.log = log self.glance = \ - glance_client(conf.os_clients.glance_version, - get_session()) + glance_client(conf.glance_version, get_session()) self.use_existing_image = False self.image = None def create(self): - LOG.info('image create start......') + 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: + if self.conf.image_name not in images: + if not os.path.exists(self.conf.image_filename): + resp = urllib.request.urlopen(self.conf.image_download_url) + with open(self.conf.image_filename, "wb") as file: file.write(resp.read()) - self.image = self.glance.images.create(name=self.conf.image.name, - disk_format=self.conf.image.format, + 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')) + open(self.conf.image_filename, 'rb')) else: self.use_existing_image = True - self.image = images[self.conf.image.name] + self.image = images[self.conf.image_name] - LOG.info('image create end......') + self.log.info('image create end......') def delete(self): - LOG.info('image delete start.......') + 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.......') + self.log.info('image delete end.......') diff --git a/tests/inspector.py b/tests/inspector.py index ba00f40e..d11da299 100644 --- a/tests/inspector.py +++ b/tests/inspector.py @@ -13,7 +13,6 @@ from flask import Flask from flask import request import json import logger as doctor_log -import os import threading import time diff --git a/tests/main.py b/tests/main.py index 50e0821b..46f0c894 100644 --- a/tests/main.py +++ b/tests/main.py @@ -11,6 +11,7 @@ import sys import config from image import Image import logger as doctor_log +from user import User LOG = doctor_log.Logger('doctor').getLogger() @@ -20,33 +21,39 @@ class DoctorTest(object): def __init__(self, conf): self.conf = conf - self.image = Image(self.conf) + self.image = Image(self.conf, LOG) + self.user = User(self.conf, LOG) + + def setup(self): + # prepare the cloud env + + # preparing VM image... + self.image.create() + + # creating test user... + self.user.create() + self.user.update_quota() 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... + self.setup() # 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() + self.cleanup() + + def cleanup(self): + self.image.delete() + self.user.delete() def main(): diff --git a/tests/monitor.py b/tests/monitor.py index 8e8aa7a5..7450c534 100644 --- a/tests/monitor.py +++ b/tests/monitor.py @@ -11,12 +11,11 @@ import argparse from datetime import datetime import json import logger as doctor_log -import os import requests import socket -import sys import time +from keystoneauth1 import session from congressclient.v1 import client import identity_auth @@ -48,8 +47,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 index 2eb406e0..c9eb0b1d 100644 --- a/tests/os_clients.py +++ b/tests/os_clients.py @@ -9,13 +9,24 @@ from oslo_config import cfg
import glanceclient.client as glanceclient
-
+from keystoneclient.v2_0 import client as ks_client
+import novaclient.client as novaclient
OPTS = [
cfg.StrOpt('glance_version', default='2', help='glance version'),
+ cfg.StrOpt('nova_version', default='2.34', help='Nova version'),
]
def glance_client(version, session):
return glanceclient.Client(version=version,
session=session)
+
+
+def keystone_client(session):
+ return ks_client.Client(session=session)
+
+
+def nova_client(version, session):
+ return novaclient.Client(version=version,
+ session=session)
diff --git a/tests/run.sh b/tests/run.sh index c21c3fd7..fda1e753 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -30,6 +30,7 @@ DOCTOR_PW=doctor DOCTOR_PROJECT=doctor DOCTOR_ROLE=_member_ PROFILER_TYPE=${PROFILER_TYPE:-none} +PYTHON_ENABLE=${PYTHON_ENABLE:-false} TOP_DIR=$(cd $(dirname "$0") && pwd) @@ -293,8 +294,7 @@ inject_failure() { echo "disabling network of compute host [$COMPUTE_HOST] for 3 mins..." cat > disable_network.sh << 'END_TXT' #!/bin/bash -x -dev=$(sudo ip a | awk '/ @COMPUTE_IP@\//{print $7}') -[[ -n "$dev" ]] || dev=$(sudo ip a | awk '/ @COMPUTE_IP@\//{print $5}') +dev=$(sudo ip a | awk '/ @COMPUTE_IP@\//{print $NF}') sleep 1 sudo ip link set $dev down echo "doctor set link down at" $(date "+%s.%N") @@ -480,6 +480,13 @@ cleanup() { # Main process +if $PYTHON_ENABLE; then + cd $TOP_DIR + echo "executing tox..." + tox + exit $? +fi + echo "Note: doctor/tests/run.sh has been executed." git log --oneline -1 || true # ignore even you don't have git installed diff --git a/tests/user.py b/tests/user.py new file mode 100644 index 00000000..b21bd1a8 --- /dev/null +++ b/tests/user.py @@ -0,0 +1,163 @@ +############################################################################## +# 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 + +from oslo_config import cfg + +from identity_auth import get_session +from os_clients import keystone_client +from os_clients import nova_client + + +OPTS = [ + cfg.StrOpt('doctor_user', + default='doctor', + help='the name of test user', + required=True), + cfg.StrOpt('doctor_passwd', + default='doctor', + help='the password of test user', + required=True), + cfg.StrOpt('doctor_project', + default='doctor', + help='the name of test project', + required=True), + cfg.StrOpt('doctor_role', + default='_member_', + help='the role of test user', + required=True), + cfg.IntOpt('quota_instances', + default=os.environ.get('VM_COUNT', 1), + help='the quota of instances in test user', + required=True), + cfg.IntOpt('quota_cores', + default=os.environ.get('VM_COUNT', 1), + help='the quota of cores in test user', + required=True), +] + + +class User(object): + + def __init__(self, conf, log): + self.conf = conf + self.log = log + self.keystone = \ + keystone_client(get_session()) + self.nova = \ + nova_client(conf.nova_version, get_session()) + self.users = {} + self.projects = {} + self.roles = {} + self.roles_for_user = {} + self.roles_for_admin = {} + + def create(self): + """create test user, project and etc""" + self.log.info('user create start......') + + self._create_project() + self._create_user() + self._create_role() + self._add_user_role_in_project(is_admin=False) + self._add_user_role_in_project(is_admin=True) + + self.log.info('user create end......') + + def _create_project(self): + """create test project""" + self.projects = {project.name: project + for project in self.keystone.tenants.list()} + if self.conf.doctor_project not in self.projects: + test_project = \ + self.keystone.tenants.create(self.conf.doctor_project) + self.projects[test_project.name] = test_project + + def _create_user(self): + """create test user""" + project = self.projects.get(self.conf.doctor_project) + self.users = {user.name: user for user in self.keystone.users.list()} + if self.conf.doctor_user not in self.users: + test_user = self.keystone.users.create( + self.conf.doctor_user, + password=self.conf.doctor_passwd, + tenant_id=project.id) + self.users[test_user.name] = test_user + + def _create_role(self): + """create test role""" + self.roles = {role.name: role for role in self.keystone.roles.list()} + if self.conf.doctor_role not in self.roles: + test_role = self.keystone.roles.create(self.conf.doctor_role) + self.roles[test_role.name] = test_role + + def _add_user_role_in_project(self, is_admin=False): + """add test user with test role in test project""" + project = self.projects.get(self.conf.doctor_project) + + user_name = 'admin' if is_admin else self.conf.doctor_user + user = self.users.get(user_name) + + role_name = 'admin' if is_admin else self.conf.doctor_role + role = self.roles.get(role_name) + + roles_for_user = self.roles_for_admin \ + if is_admin else self.roles_for_user + + roles_for_user = \ + {role.name: role for role in + self.keystone.roles.roles_for_user(user, tenant=project)} + if role_name not in roles_for_user: + self.keystone.roles.add_user_role(user, role, tenant=project) + roles_for_user[role_name] = role + + def delete(self): + """delete the test user, project and role""" + self.log.info('user delete start......') + + project = self.projects.get(self.conf.doctor_project) + user = self.users.get(self.conf.doctor_user) + role = self.roles.get(self.conf.doctor_role) + + if project: + if 'admin' in self.roles_for_admin: + self.keystone.roles.remove_user_role( + self.users['admin'], + self.roles['admin'], + tenant=project) + + if user: + if role and self.conf.doctor_role in self.roles_for_user: + self.keystone.roles.remove_user_role( + user, role, tenant=project) + self.keystone.roles.delete(role) + self.keystone.users.delete(user) + + self.keystone.tenants.delete(project) + self.log.info('user delete end......') + + def update_quota(self): + self.log.info('user quota update start......') + project = self.projects.get(self.conf.doctor_project) + user = self.users.get(self.conf.doctor_user) + + if project and user: + self.quota = self.nova.quotas.get(project.id, + user_id=user.id) + if self.conf.quota_instances > self.quota.instances: + self.nova.quotas.update(project.id, + instances=self.conf.quota_instances, + user_id=user.id) + if self.conf.quota_cores > self.quota.cores: + self.nova.quotas.update(project.id, + cores=self.conf.quota_cores, + user_id=user.id) + self.log.info('user quota update end......') + else: + raise Exception('No project or role for update quota') |