Merge "Adding PYTHON_ENABLE option"
authorCarlos Goncalves <carlos.goncalves@neclab.eu>
Wed, 31 May 2017 07:50:33 +0000 (07:50 +0000)
committerGerrit Code Review <gerrit@opnfv.org>
Wed, 31 May 2017 07:50:33 +0000 (07:50 +0000)
15 files changed:
.gitignore
devstack/README.rst [new file with mode: 0644]
devstack/plugin.sh [new file with mode: 0644]
devstack/settings [new file with mode: 0644]
test-requirements.txt [new file with mode: 0644]
tests/config.py [new file with mode: 0644]
tests/identity_auth.py
tests/image.py [new file with mode: 0644]
tests/lib/installers/fuel
tests/logger.py
tests/main.py [new file with mode: 0644]
tests/monitor.py
tests/os_clients.py [new file with mode: 0644]
tests/run.sh
tox.ini [new file with mode: 0644]

index d2ded27..65b51a6 100644 (file)
@@ -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 (file)
index 0000000..cd836f1
--- /dev/null
@@ -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 (file)
index 0000000..a7f6a63
--- /dev/null
@@ -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 (file)
index 0000000..83e02e3
--- /dev/null
@@ -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 (file)
index 0000000..2928e0f
--- /dev/null
@@ -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 (file)
index 0000000..2288d36
--- /dev/null
@@ -0,0 +1,30 @@
+##############################################################################\r
+# Copyright (c) 2017 ZTE Corporation and others.\r
+#\r
+# All rights reserved. This program and the accompanying materials\r
+# are made available under the terms of the Apache License, Version 2.0\r
+# which accompanies this distribution, and is available at\r
+# http://www.apache.org/licenses/LICENSE-2.0\r
+##############################################################################\r
+from oslo_config import cfg\r
+\r
+import image\r
+import os_clients\r
+\r
+\r
+def list_opts():\r
+    return [\r
+        ('os_clients', os_clients.OPTS),\r
+        ('image', image.IMAGE_OPTS),\r
+    ]\r
+\r
+\r
+def prepare_conf(conf=None):\r
+    if conf is None:\r
+        conf = cfg.ConfigOpts()\r
+\r
+    for group, options in list_opts():\r
+        conf.register_opts(list(options),\r
+                           group=None if group == 'DEFAULT' else group)\r
+\r
+    return conf\r
index 4726ca3..ffecc68 100644 (file)
@@ -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 (file)
index 0000000..0b4a3d7
--- /dev/null
@@ -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.......')
index da0de34..0c56963 100644 (file)
@@ -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
 }
index a4f3323..72043ab 100644 (file)
@@ -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 (file)
index 0000000..50e0821
--- /dev/null
@@ -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())
index 8e8aa7a..8244dc8 100644 (file)
@@ -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 (file)
index 0000000..2eb406e
--- /dev/null
@@ -0,0 +1,21 @@
+##############################################################################\r
+# Copyright (c) 2017 ZTE Corporation and others.\r
+#\r
+# All rights reserved. This program and the accompanying materials\r
+# are made available under the terms of the Apache License, Version 2.0\r
+# which accompanies this distribution, and is available at\r
+# http://www.apache.org/licenses/LICENSE-2.0\r
+##############################################################################\r
+from oslo_config import cfg\r
+\r
+import glanceclient.client as glanceclient\r
+\r
+\r
+OPTS = [\r
+    cfg.StrOpt('glance_version', default='2', help='glance version'),\r
+]\r
+\r
+\r
+def glance_client(version, session):\r
+    return glanceclient.Client(version=version,\r
+                               session=session)\r
index 7f95a8a..daf2394 100755 (executable)
@@ -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 (file)
index 0000000..2f74083
--- /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