Merge "Create policy.json file in Ocata for non-admin user"
authorRyota Mibu <r-mibu@cq.jp.nec.com>
Wed, 5 Jul 2017 08:21:12 +0000 (08:21 +0000)
committerGerrit Code Review <gerrit@opnfv.org>
Wed, 5 Jul 2017 08:21:12 +0000 (08:21 +0000)
13 files changed:
devstack/README.rst [new file with mode: 0644]
devstack/plugin.sh [new file with mode: 0644]
devstack/settings [new file with mode: 0644]
tests/config.py
tests/consumer.py
tests/identity_auth.py
tests/image.py
tests/inspector.py
tests/main.py
tests/monitor.py
tests/os_clients.py
tests/run.sh
tests/user.py [new file with mode: 0644]

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
index 2288d36..7a0bef2 100644 (file)
@@ -6,16 +6,20 @@
 # which accompanies this distribution, and is available at\r
 # http://www.apache.org/licenses/LICENSE-2.0\r
 ##############################################################################\r
+import itertools\r
 from oslo_config import cfg\r
 \r
 import image\r
 import os_clients\r
+import user\r
 \r
 \r
 def list_opts():\r
     return [\r
-        ('os_clients', os_clients.OPTS),\r
-        ('image', image.IMAGE_OPTS),\r
+        ('DEFAULT', itertools.chain(\r
+            os_clients.OPTS,\r
+            image.OPTS,\r
+            user.OPTS))\r
     ]\r
 \r
 \r
index 3c012b4..042cf20 100644 (file)
@@ -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()
index a40c41c..ffecc68 100644 (file)
@@ -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):
index 0b4a3d7..453322b 100644 (file)
@@ -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.......')
index ba00f40..d11da29 100644 (file)
@@ -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
 
index 50e0821..46f0c89 100644 (file)
@@ -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():
index 8e8aa7a..7450c53 100644 (file)
@@ -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)
index 2eb406e..c9eb0b1 100644 (file)
@@ -9,13 +9,24 @@
 from oslo_config import cfg\r
 \r
 import glanceclient.client as glanceclient\r
-\r
+from keystoneclient.v2_0 import client as ks_client\r
+import novaclient.client as novaclient\r
 \r
 OPTS = [\r
     cfg.StrOpt('glance_version', default='2', help='glance version'),\r
+    cfg.StrOpt('nova_version', default='2.34', help='Nova version'),\r
 ]\r
 \r
 \r
 def glance_client(version, session):\r
     return glanceclient.Client(version=version,\r
                                session=session)\r
+\r
+\r
+def keystone_client(session):\r
+    return ks_client.Client(session=session)\r
+\r
+\r
+def nova_client(version, session):\r
+    return novaclient.Client(version=version,\r
+                             session=session)\r
index c21c3fd..fda1e75 100755 (executable)
@@ -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 (file)
index 0000000..b21bd1a
--- /dev/null
@@ -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')