Retire the project
Reasons: - cloudv-ostf-adapter has been moved to another repo Changes: - retired the project Change-Id: I8104b242d774ad329a50bac71ab5fb11f9ebfe18
This commit is contained in:
parent
cf6cff8a08
commit
8c27340245
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,8 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*.swp
|
|
||||||
.idea
|
|
||||||
*egg-info
|
|
||||||
.tox
|
|
||||||
.venv
|
|
||||||
*.egg/
|
|
@ -1,4 +0,0 @@
|
|||||||
[DEFAULT]
|
|
||||||
test_command=${PYTHON:-python} -m subunit.run discover ./cloudv_ostf_adapter/tests/unittests $LISTOPT $IDOPTION
|
|
||||||
test_id_option=--load-list $IDFILE
|
|
||||||
test_list_option=--list
|
|
@ -1 +0,0 @@
|
|||||||
recursive-include public *
|
|
13
README
Normal file
13
README
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
This project is no longer maintained.
|
||||||
|
|
||||||
|
The contents of this repository are still available in the Git
|
||||||
|
source code management system. To see the contents of this
|
||||||
|
repository before it reached its end of life, please check out the
|
||||||
|
previous commit with "git checkout HEAD^1".
|
||||||
|
|
||||||
|
For an alternative project, please see cloudv-ostf-adapter at
|
||||||
|
https://gerrit.mirantis.com/gitweb?p=mirantis-cloud-validation/cloudv-ostf-adapter.git
|
||||||
|
|
||||||
|
For any further questions, please email
|
||||||
|
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||||
|
Freenode.
|
336
README.rst
336
README.rst
@ -1,336 +0,0 @@
|
|||||||
=================================
|
|
||||||
Cloud Validation adapter for OSTF
|
|
||||||
=================================
|
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
|
|
||||||
Existing [OSTF](http://docs.mirantis.com/fuel-dev/develop/ostf_contributors_guide.html)
|
|
||||||
code provides a number of tests which cover a number of cases needed for cloud
|
|
||||||
validation. The downside of existing OSTF is that it is tightly coupled with
|
|
||||||
FUEL's nailgun. Given project aims to create standalone adapter for OSTF which
|
|
||||||
is independent of FUEL thus making it possible to run OSTF tests on any random
|
|
||||||
cloud (in theory).
|
|
||||||
|
|
||||||
High-level design
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
CLI tool that works with health check plugins
|
|
||||||
Supported plugins::
|
|
||||||
|
|
||||||
- fuel health check
|
|
||||||
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Please note that if you're using Fuel OSTF plugin, you have to install it manually.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ cloudvalidation cloud-health-check {argument} [argument_parameters]
|
|
||||||
|
|
||||||
Arguments::
|
|
||||||
|
|
||||||
list_plugins - Lists plugins
|
|
||||||
list_plugin_suites - Lists plugin test suites
|
|
||||||
list_plugin_tests - Lists plugin tests from all available suites
|
|
||||||
run_suites - Runs all tests from all suites
|
|
||||||
run_suite - Runs certain test suite
|
|
||||||
run_test - Runs certain test
|
|
||||||
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ cloudvalidation-cli cloud-health-check list_plugins
|
|
||||||
|
|
||||||
+----------+------------------------------------------------------------------+
|
|
||||||
| Property | Value |
|
|
||||||
+----------+------------------------------------------------------------------+
|
|
||||||
| name | fuel_health |
|
|
||||||
| suites | fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_heat.SanityHeatTest |
|
|
||||||
| | fuel_health.tests.smoke.test_create_flavor.FlavorsAdminTest |
|
|
||||||
+----------+------------------------------------------------------------------+
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ cloudvalidation-cli cloud-health-check list_plugin_suites --validation-plugin fuel_health
|
|
||||||
|
|
||||||
+----------+------------------------------------------------------------------+
|
|
||||||
| Property | Value |
|
|
||||||
+----------+------------------------------------------------------------------+
|
|
||||||
| suites | fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_heat.SanityHeatTest |
|
|
||||||
| | fuel_health.tests.smoke.test_create_flavor.FlavorsAdminTest |
|
|
||||||
+----------+------------------------------------------------------------------+
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ cloudvalidation-cli cloud-health-check list_plugin_tests --validation-plugin fuel_health
|
|
||||||
|
|
||||||
+----------+--------------------------------------------------------------------------------------+
|
|
||||||
| Property | Value |
|
|
||||||
+----------+--------------------------------------------------------------------------------------+
|
|
||||||
| tests | fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest:test_list_services |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest:test_list_users |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_flavors |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_images |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_instances |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_rate_limits |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_snapshots |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_volumes |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_heat.SanityHeatTest:test_list_stacks |
|
|
||||||
| | fuel_health.tests.smoke.test_create_flavor.FlavorsAdminTest:test_create_flavor |
|
|
||||||
+----------+--------------------------------------------------------------------------------------+
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ cloudvalidation-cli --config-dir=/etc/cloudv_ostf_adapter cloud-health-check run_suites --validation-plugin-name fuel_health
|
|
||||||
|
|
||||||
|
|
||||||
Request user list ... ok
|
|
||||||
Request flavor list ... ok
|
|
||||||
Request image list ... ok
|
|
||||||
Request instance list ... ok
|
|
||||||
Request absolute limits list ... ok
|
|
||||||
Request snapshot list ... ok
|
|
||||||
Request volume list ... ok
|
|
||||||
Request stack list ... ok
|
|
||||||
Create instance flavor ... ok
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
Ran 9 tests in 5.310s
|
|
||||||
|
|
||||||
OK
|
|
||||||
|
|
||||||
.. code-block::
|
|
||||||
|
|
||||||
$ cloudvalidation-cli --config-dir=/etc/cloudv_ostf_adapter cloud-health-check run_suite --validation-plugin-name fuel_health --suite fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest
|
|
||||||
|
|
||||||
Running test suite: fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest ...
|
|
||||||
Request user list ... ok
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
Ran 1 test in 0.938s
|
|
||||||
|
|
||||||
OK
|
|
||||||
|
|
||||||
Links
|
|
||||||
-----
|
|
||||||
|
|
||||||
* OSTF contributor's guide - http://docs.mirantis.com/fuel-dev/develop/ostf_contributors_guide.html)
|
|
||||||
* OSTF source code - https://github.com/stackforge/fuel-ostf
|
|
||||||
|
|
||||||
========
|
|
||||||
REST API
|
|
||||||
========
|
|
||||||
|
|
||||||
|
|
||||||
Run server
|
|
||||||
----------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ cloudvalidation-server --config-file=path_to_config
|
|
||||||
* Running on http://127.0.0.1:8777/ (Press CTRL+C to quit)
|
|
||||||
|
|
||||||
Example of config
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
[rest]
|
|
||||||
server_host=127.0.0.1
|
|
||||||
server_port=8777
|
|
||||||
log_file=/var/log/ostf.log
|
|
||||||
jobs_dir=/var/log/ostf
|
|
||||||
debug=False
|
|
||||||
|
|
||||||
List of supported operations
|
|
||||||
----------------------------
|
|
||||||
- get list of supported plugins
|
|
||||||
GET /v1/plugins?load_tests=True/False
|
|
||||||
In load_tests=True case tests for plugin will be shown.
|
|
||||||
|
|
||||||
- get suites in plugin
|
|
||||||
GET /v1/plugins/<plugin_name>/suites
|
|
||||||
|
|
||||||
- get tests for all suites in plugin
|
|
||||||
GET /v1/plugins/<plugin_name>/suites/tests
|
|
||||||
|
|
||||||
- get tests per suite in plugin
|
|
||||||
GET /v1/plugins/<plugin_name>/suites/<suite>/tests
|
|
||||||
|
|
||||||
- run suites for plugin
|
|
||||||
POST /v1/plugins/<plugin_name>/suites
|
|
||||||
|
|
||||||
- run suite for plugin
|
|
||||||
POST /v1/plugins/<plugin_name>/suites/<suite>
|
|
||||||
|
|
||||||
- run test for plugin
|
|
||||||
POST /v1/plugins/<plugin_name>/suites/tests/<test>
|
|
||||||
|
|
||||||
- create job with user's tests set
|
|
||||||
POST /v1/jobs/create
|
|
||||||
Example of JSON:
|
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
{
|
|
||||||
"job": {
|
|
||||||
"name": "fake",
|
|
||||||
"description": "description",
|
|
||||||
"tests": [
|
|
||||||
"fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_flavors"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- list of all jobs
|
|
||||||
GET /v1/jobs
|
|
||||||
|
|
||||||
- execute job
|
|
||||||
GET /v1/jobs/execute/<job_id>
|
|
||||||
|
|
||||||
- get status with report for executed job
|
|
||||||
GET /v1/jobs/<job_id>
|
|
||||||
|
|
||||||
- delete job
|
|
||||||
DELETE /v1/jobs/<job_id>
|
|
||||||
|
|
||||||
|
|
||||||
=====================
|
|
||||||
REST API Client usage
|
|
||||||
=====================
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.cloudv_client import client
|
|
||||||
|
|
||||||
cloudvclient = client.Client(CONF.host, CONF.port, CONF.api_version)
|
|
||||||
|
|
||||||
plugins = cloudvclient.plugins.list()
|
|
||||||
plugin_one = plugins[0]['name']
|
|
||||||
|
|
||||||
suites = cloudvalidation.suites.list_suites(plugin_one)
|
|
||||||
|
|
||||||
=========================
|
|
||||||
REST API Client CLI usage
|
|
||||||
=========================
|
|
||||||
|
|
||||||
To connect cloudvalidation client to ReST service you need to do next::
|
|
||||||
|
|
||||||
# create configuration file, that contains
|
|
||||||
[DEFAULT]
|
|
||||||
host = localhost
|
|
||||||
port = 8777
|
|
||||||
api_version = v1
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
# export next operating system variables:
|
|
||||||
export MCLOUDV_HOST=localhost
|
|
||||||
export MCLOUDV_PORT=8777
|
|
||||||
export MCLOUDV_API=v1
|
|
||||||
|
|
||||||
|
|
||||||
Usage examples::
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
cloudvalidation cloud-health-check list_plugins
|
|
||||||
|
|
||||||
+----------+----------------------------------------------------------------------------------------------+
|
|
||||||
| Property | Value |
|
|
||||||
+----------+----------------------------------------------------------------------------------------------+
|
|
||||||
| name | fuel_health |
|
|
||||||
| suites | fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_heat.SanityHeatTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_networking.NetworksTest:test_list_networks_nova_network |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_ceilometer.CeilometerApiTests |
|
|
||||||
| | fuel_health.tests.smoke.test_create_flavor.FlavorsAdminTest |
|
|
||||||
| | fuel_health.tests.smoke.test_create_volume.VolumesTest |
|
|
||||||
| | fuel_health.tests.smoke.test_neutron_actions.TestNeutron |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_create_instance_with_connectivity.TestNovaNetwork |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_image_actions.TestImageAction |
|
|
||||||
| | fuel_health.tests.smoke.test_user_create.TestUserTenantRole |
|
|
||||||
+----------+----------------------------------------------------------------------------------------------+
|
|
||||||
|
|
||||||
|
|
||||||
cloudvalidation cloud-health-check list_plugin_suites --validation-plugin-name fuel_health
|
|
||||||
|
|
||||||
+----------+----------------------------------------------------------------------------------------------+
|
|
||||||
| Property | Value |
|
|
||||||
+----------+----------------------------------------------------------------------------------------------+
|
|
||||||
| suites | fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_heat.SanityHeatTest |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_networking.NetworksTest:test_list_networks_nova_network |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_ceilometer.CeilometerApiTests |
|
|
||||||
| | fuel_health.tests.smoke.test_create_flavor.FlavorsAdminTest |
|
|
||||||
| | fuel_health.tests.smoke.test_create_volume.VolumesTest |
|
|
||||||
| | fuel_health.tests.smoke.test_neutron_actions.TestNeutron |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_create_instance_with_connectivity.TestNovaNetwork |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_image_actions.TestImageAction |
|
|
||||||
| | fuel_health.tests.smoke.test_user_create.TestUserTenantRole |
|
|
||||||
+----------+----------------------------------------------------------------------------------------------+
|
|
||||||
|
|
||||||
|
|
||||||
cloudvalidation cloud-health-check list_plugin_tests --validation-plugin-name fuel_health
|
|
||||||
|
|
||||||
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
| Property | Value |
|
|
||||||
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
| tests | fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest:test_list_services |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest:test_list_users |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_flavors |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_images |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_instances |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_rate_limits |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_snapshots |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest:test_list_volumes |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_heat.SanityHeatTest:test_list_stacks |
|
|
||||||
| | fuel_health.tests.sanity.test_sanity_ceilometer.CeilometerApiTests:test_list_meters |
|
|
||||||
| | fuel_health.tests.smoke.test_create_flavor.FlavorsAdminTest:test_create_flavor |
|
|
||||||
| | fuel_health.tests.smoke.test_create_volume.VolumesTest:test_create_boot_volume |
|
|
||||||
| | fuel_health.tests.smoke.test_create_volume.VolumesTest:test_volume_create |
|
|
||||||
| | fuel_health.tests.smoke.test_neutron_actions.TestNeutron:test_check_neutron_objects_creation |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_create_instance_with_connectivity.TestNovaNetwork:test_001_create_keypairs |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_create_instance_with_connectivity.TestNovaNetwork:test_002_create_security_groups |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_create_instance_with_connectivity.TestNovaNetwork:test_003_check_networks |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_create_instance_with_connectivity.TestNovaNetwork:test_004_create_servers |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_create_instance_with_connectivity.TestNovaNetwork:test_006_check_internet_connectivity_instance_without_floatingIP |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_create_instance_with_connectivity.TestNovaNetwork:test_008_check_public_instance_connectivity_from_instance |
|
|
||||||
| | fuel_health.tests.smoke.test_nova_image_actions.TestImageAction:test_snapshot |
|
|
||||||
| | fuel_health.tests.smoke.test_user_create.TestUserTenantRole:test_create_user |
|
|
||||||
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
|
|
||||||
|
|
||||||
cloudvalidation cloud-health-check run_suites --validation-plugin-name fuel_health
|
|
||||||
|
|
||||||
Note this command will generate big report, so it might be useful to save it into a file.
|
|
||||||
|
|
||||||
|
|
||||||
cloudvalidation cloud-health-check run_suite --validation-plugin-name fuel_health --suite fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest
|
|
||||||
|
|
||||||
Note this command will generate big report, so it might be useful to save it into a file.
|
|
||||||
|
|
||||||
|
|
||||||
cloudvalidation cloud-health-check run_test --validation-plugin-name fuel_health --test fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest:test_list_services
|
|
||||||
|
|
||||||
+-------------------------------------------------------------------------------------+----------+--------+------------------------------------------------------------------------+
|
|
||||||
| Test | Duration | Result | Report |
|
|
||||||
+-------------------------------------------------------------------------------------+----------+--------+------------------------------------------------------------------------+
|
|
||||||
| fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest:test_list_services | 1.184s | Passed | Request active services list ... ok |
|
|
||||||
| | | | |
|
|
||||||
| | | | ---------------------------------------------------------------------- |
|
|
||||||
| | | | Ran 1 test in 1.184s |
|
|
||||||
| | | | |
|
|
||||||
| | | | OK |
|
|
||||||
| | | | |
|
|
||||||
+-------------------------------------------------------------------------------------+----------+--------+------------------------------------------------------------------------+
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
from cloudv_ostf_adapter.common import cfg
|
|
||||||
|
|
||||||
from cloudv_client import jobs
|
|
||||||
from cloudv_client import plugins
|
|
||||||
from cloudv_client import suites
|
|
||||||
from cloudv_client import tests
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
|
||||||
|
|
||||||
def __init__(self, host, port, api_version):
|
|
||||||
kwargs = {
|
|
||||||
'host': host,
|
|
||||||
'port': port,
|
|
||||||
'api_version': api_version
|
|
||||||
}
|
|
||||||
self.plugins = plugins.Plugins(**kwargs)
|
|
||||||
self.suites = suites.Suites(**kwargs)
|
|
||||||
self.tests = tests.Tests(**kwargs)
|
|
||||||
self.jobs = jobs.Jobs(**kwargs)
|
|
@ -1,77 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
try:
|
|
||||||
import simplejson as json
|
|
||||||
except ImportError:
|
|
||||||
import json
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import exception
|
|
||||||
|
|
||||||
|
|
||||||
class Jobs(object):
|
|
||||||
|
|
||||||
_jobs_create_route = ("http://%(host)s:%(port)d/%(api_version)s"
|
|
||||||
"/jobs/create")
|
|
||||||
_jobs_route = ("http://%(host)s:%(port)d/%(api_version)s/jobs")
|
|
||||||
_jobs_execute_route = ("http://%(host)s:%(port)d/%(api_version)s"
|
|
||||||
"/jobs/execute/%(job_id)s")
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
def list(self):
|
|
||||||
jobs_url = self._jobs_route % self.kwargs
|
|
||||||
response = requests.get(jobs_url)
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
return json.loads(response.content)['jobs']
|
|
||||||
|
|
||||||
def create(self, name, description, tests):
|
|
||||||
data = {'job': {'name': name,
|
|
||||||
'description': description,
|
|
||||||
'tests': tests}}
|
|
||||||
jobs_url = self._jobs_create_route % self.kwargs
|
|
||||||
headers = {'Content-Type': 'application/json'}
|
|
||||||
response = requests.post(jobs_url,
|
|
||||||
headers=headers,
|
|
||||||
data=json.dumps(data))
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
return json.loads(response.content)['job']
|
|
||||||
|
|
||||||
def get(self, job_id):
|
|
||||||
jobs_url = self._jobs_route % self.kwargs
|
|
||||||
jobs_url += '/%s' % job_id
|
|
||||||
response = requests.get(jobs_url)
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
return json.loads(response.content)['job']
|
|
||||||
|
|
||||||
def delete(self, job_id):
|
|
||||||
jobs_url = self._jobs_route % self.kwargs
|
|
||||||
jobs_url += '/%s' % job_id
|
|
||||||
response = requests.delete(jobs_url)
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
|
|
||||||
def execute(self, job_id):
|
|
||||||
self.kwargs.update({'job_id': job_id})
|
|
||||||
jobs_url = self._jobs_execute_route % self.kwargs
|
|
||||||
response = requests.post(jobs_url)
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
return json.loads(response.content)['job']
|
|
@ -1,38 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
try:
|
|
||||||
import simplejson as json
|
|
||||||
except ImportError:
|
|
||||||
import json
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import exception
|
|
||||||
|
|
||||||
|
|
||||||
class Plugins(object):
|
|
||||||
|
|
||||||
route = "http://%(host)s:%(port)d/%(api_version)s/plugins"
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.url = self.route % kwargs
|
|
||||||
|
|
||||||
def list(self, load_tests=True):
|
|
||||||
params = {'load_tests': load_tests}
|
|
||||||
response = requests.get(self.url, params=params)
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
resp = json.loads(response.content)
|
|
||||||
return resp['plugins']
|
|
@ -1,68 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
try:
|
|
||||||
import simplejson as json
|
|
||||||
except ImportError:
|
|
||||||
import json
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import exception
|
|
||||||
|
|
||||||
|
|
||||||
class Suites(object):
|
|
||||||
|
|
||||||
_suite_route = ("http://%(host)s:%(port)d/%(api_version)s"
|
|
||||||
"/plugins/%(plugin)s/suites")
|
|
||||||
_suite_test_route = ("http://%(host)s:%(port)d/%(api_version)s/"
|
|
||||||
"plugins/%(plugin)s/suites/%(suite)s/tests")
|
|
||||||
_test_route = ("http://%(host)s:%(port)d/%(api_version)s/"
|
|
||||||
"plugins/%(plugin)s/suites/tests")
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
def list_suites(self, plugin):
|
|
||||||
self.kwargs.update({"plugin": plugin})
|
|
||||||
suite_url = self._suite_route % self.kwargs
|
|
||||||
response = requests.get(suite_url)
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
return json.loads(response.content)['plugin']
|
|
||||||
|
|
||||||
def list_tests_for_suites(self, plugin):
|
|
||||||
self.kwargs.update({"plugin": plugin})
|
|
||||||
suite_url = self._test_route % self.kwargs
|
|
||||||
response = requests.get(suite_url)
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
return json.loads(response.content)['plugin']
|
|
||||||
|
|
||||||
def run_suites(self, plugin):
|
|
||||||
self.kwargs.update({"plugin": plugin})
|
|
||||||
suite_url = self._suite_route % self.kwargs
|
|
||||||
response = requests.post(suite_url, {})
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
return json.loads(response.content)['plugin']['report']
|
|
||||||
|
|
||||||
def run_suite_tests(self, suite, plugin):
|
|
||||||
self.kwargs.update({"suite": suite})
|
|
||||||
self.kwargs.update({"plugin": plugin})
|
|
||||||
url = self._suite_test_route % self.kwargs
|
|
||||||
response = requests.post(url, {})
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(response.status_code)()
|
|
||||||
return json.loads(response.content)['suite']
|
|
@ -1,41 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
try:
|
|
||||||
import simplejson as json
|
|
||||||
except ImportError:
|
|
||||||
import json
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import exception
|
|
||||||
|
|
||||||
|
|
||||||
class Tests(object):
|
|
||||||
|
|
||||||
route = ("http://%(host)s:%(port)d/%(api_version)s"
|
|
||||||
"/plugins/%(plugin)s/suites/tests/%(test)s")
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
def run(self, test, plugin):
|
|
||||||
self.kwargs.update({"test": test})
|
|
||||||
self.kwargs.update({"plugin": plugin})
|
|
||||||
url = self.route % self.kwargs
|
|
||||||
response = requests.post(url, {})
|
|
||||||
if not response.ok:
|
|
||||||
raise exception.exception_mapping.get(
|
|
||||||
response.status_code)()
|
|
||||||
return json.loads(response.content)['plugin']['report']
|
|
@ -1,126 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import six
|
|
||||||
|
|
||||||
|
|
||||||
def args(*args, **kwargs):
|
|
||||||
"""
|
|
||||||
Decorates commandline arguments for actions
|
|
||||||
:param args: sub-category commandline arguments
|
|
||||||
:param kwargs: sub-category commandline arguments
|
|
||||||
:return: decorator: object attribute setter
|
|
||||||
:rtype: callable
|
|
||||||
"""
|
|
||||||
def _decorator(func):
|
|
||||||
func.__dict__.setdefault('args', []).insert(0, (args, kwargs))
|
|
||||||
return func
|
|
||||||
return _decorator
|
|
||||||
|
|
||||||
|
|
||||||
def methods_of(obj):
|
|
||||||
"""
|
|
||||||
Get all callable methods of an object that don't
|
|
||||||
start with underscore (private attributes)
|
|
||||||
returns
|
|
||||||
:param obj: objects to get callable attributes from
|
|
||||||
:type obj: object
|
|
||||||
:return result: a list of tuples of the form (method_name, method)
|
|
||||||
:rtype: list
|
|
||||||
"""
|
|
||||||
result = []
|
|
||||||
for i in dir(obj):
|
|
||||||
if callable(getattr(obj, i)) and not i.startswith('_'):
|
|
||||||
result.append((i, getattr(obj, i)))
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def add_command_parsers(categories):
|
|
||||||
"""
|
|
||||||
Parses actions commandline arguments from each category
|
|
||||||
:param categories: commandline categories
|
|
||||||
:type categories: dict
|
|
||||||
:return: _subparser: commandline subparser
|
|
||||||
"""
|
|
||||||
def _subparser(subparsers):
|
|
||||||
"""
|
|
||||||
Iterates over categories and registers action
|
|
||||||
commandline arguments for each category
|
|
||||||
:param subparsers: commandline subparser
|
|
||||||
:return: None
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
for category in categories:
|
|
||||||
command_object = categories[category]()
|
|
||||||
|
|
||||||
desc = getattr(command_object, 'description', None)
|
|
||||||
parser = subparsers.add_parser(category, description=desc)
|
|
||||||
parser.set_defaults(command_object=command_object)
|
|
||||||
|
|
||||||
category_subparsers = parser.add_subparsers(dest='action')
|
|
||||||
|
|
||||||
for (action, action_fn) in methods_of(command_object):
|
|
||||||
parser = category_subparsers.add_parser(
|
|
||||||
action, description=desc)
|
|
||||||
|
|
||||||
action_kwargs = []
|
|
||||||
for args, kwargs in getattr(action_fn, 'args', []):
|
|
||||||
kwargs.setdefault('dest', args[0][2:])
|
|
||||||
if kwargs['dest'].startswith('action_kwarg_'):
|
|
||||||
action_kwargs.append(
|
|
||||||
kwargs['dest'][len('action_kwarg_'):])
|
|
||||||
else:
|
|
||||||
action_kwargs.append(kwargs['dest'])
|
|
||||||
kwargs['dest'] = 'action_kwarg_' + kwargs['dest']
|
|
||||||
|
|
||||||
parser.add_argument(*args, **kwargs)
|
|
||||||
|
|
||||||
parser.set_defaults(action_fn=action_fn)
|
|
||||||
parser.set_defaults(action_kwargs=action_kwargs)
|
|
||||||
|
|
||||||
parser.add_argument('action_args', nargs='*',
|
|
||||||
help=argparse.SUPPRESS)
|
|
||||||
|
|
||||||
return _subparser
|
|
||||||
|
|
||||||
|
|
||||||
def _main(global_conf, local_conf, category_opt, cli_args):
|
|
||||||
"""
|
|
||||||
|
|
||||||
:param global_conf: staged CONF
|
|
||||||
:param local_conf: tool conf
|
|
||||||
:param category_opt: subparser category options
|
|
||||||
:param cli_args: tool CLI arguments
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
global_conf.register_cli_opt(category_opt)
|
|
||||||
local_conf.parse_args(cli_args)
|
|
||||||
fn = global_conf.category.action_fn
|
|
||||||
fn_args = [arg.decode('utf-8') for arg in global_conf.category.action_args]
|
|
||||||
fn_kwargs = {}
|
|
||||||
for k in global_conf.category.action_kwargs:
|
|
||||||
v = getattr(global_conf.category, 'action_kwarg_' + k)
|
|
||||||
if v is None:
|
|
||||||
continue
|
|
||||||
if isinstance(v, six.string_types):
|
|
||||||
v = v.decode('utf-8')
|
|
||||||
fn_kwargs[k] = v
|
|
||||||
|
|
||||||
try:
|
|
||||||
ret = fn(*fn_args, **fn_kwargs)
|
|
||||||
return ret
|
|
||||||
except Exception as e:
|
|
||||||
print(str(e))
|
|
@ -1,119 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg as config
|
|
||||||
from cloudv_ostf_adapter.common import utils
|
|
||||||
from cloudv_ostf_adapter.cmd import _common as cmd
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter import validation_plugin
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class OSTF(object):
|
|
||||||
"""
|
|
||||||
Represents CLI view for OSTF tests commands:
|
|
||||||
- list validation plugins
|
|
||||||
- list per-plugin test suites
|
|
||||||
"""
|
|
||||||
|
|
||||||
def list_plugins(self):
|
|
||||||
for plugin in validation_plugin.VALIDATION_PLUGINS:
|
|
||||||
_plugin = plugin(load_tests=False)
|
|
||||||
descriptor = _plugin.descriptor()
|
|
||||||
del descriptor['tests']
|
|
||||||
descriptor.update({'suites': "\n".join(descriptor['suites'])})
|
|
||||||
utils.print_dict(descriptor)
|
|
||||||
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
def list_plugin_suites(self, validation_plugin_name):
|
|
||||||
for plugin in validation_plugin.VALIDATION_PLUGINS:
|
|
||||||
_plugin = plugin(load_tests=False)
|
|
||||||
descriptor = _plugin.descriptor()
|
|
||||||
if descriptor['name'] == validation_plugin_name:
|
|
||||||
utils.print_dict({'suites': "\n".join(descriptor['suites'])})
|
|
||||||
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
def list_plugin_tests(self, validation_plugin_name):
|
|
||||||
for plugin in validation_plugin.VALIDATION_PLUGINS:
|
|
||||||
_plugin = plugin(load_tests=False)
|
|
||||||
descriptor = _plugin.descriptor()
|
|
||||||
if descriptor['name'] == validation_plugin_name:
|
|
||||||
utils.print_dict({
|
|
||||||
'tests': "\n".join(plugin().descriptor()['tests'])})
|
|
||||||
|
|
||||||
@cmd.args("--no-format", dest="no_format")
|
|
||||||
@cmd.args("--show-full-report", dest="show_full_report")
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
def run_suites(self, validation_plugin_name):
|
|
||||||
for plugin in validation_plugin.VALIDATION_PLUGINS:
|
|
||||||
_plugin = plugin(load_tests=False)
|
|
||||||
descriptor = _plugin.descriptor()
|
|
||||||
if descriptor['name'] == validation_plugin_name:
|
|
||||||
reports = plugin().run_suites_within_cli()
|
|
||||||
utils.print_formatted(reports,
|
|
||||||
CONF.no_format,
|
|
||||||
CONF.show_full_report)
|
|
||||||
|
|
||||||
@cmd.args("--suite", dest="suite")
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
@cmd.args("--no-format", dest="no_format")
|
|
||||||
@cmd.args("--show-full-report", dest="show_full_report")
|
|
||||||
def run_suite(self, validation_plugin_name, suite):
|
|
||||||
for plugin in validation_plugin.VALIDATION_PLUGINS:
|
|
||||||
_plugin = plugin(load_tests=False)
|
|
||||||
descriptor = _plugin.descriptor()
|
|
||||||
if descriptor['name'] == validation_plugin_name:
|
|
||||||
reports = plugin().run_suite_within_cli(suite)
|
|
||||||
utils.print_formatted(reports,
|
|
||||||
CONF.no_format,
|
|
||||||
CONF.show_full_report)
|
|
||||||
|
|
||||||
@cmd.args("--no-format", dest="no_format")
|
|
||||||
@cmd.args("--show-full-report", dest="show_full_report")
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
@cmd.args("--test", dest="test")
|
|
||||||
def run_test(self, validation_plugin_name, test):
|
|
||||||
for plugin in validation_plugin.VALIDATION_PLUGINS:
|
|
||||||
_plugin = plugin(load_tests=False)
|
|
||||||
descriptor = _plugin.descriptor()
|
|
||||||
if descriptor['name'] == validation_plugin_name:
|
|
||||||
reports = plugin().run_test(test)
|
|
||||||
utils.print_formatted(reports,
|
|
||||||
CONF.no_format,
|
|
||||||
CONF.show_full_report)
|
|
||||||
|
|
||||||
|
|
||||||
CATS = {
|
|
||||||
'cloud-health-check': OSTF
|
|
||||||
}
|
|
||||||
|
|
||||||
category_opt = cfg.SubCommandOpt('category',
|
|
||||||
title='Command categories',
|
|
||||||
help='Available categories',
|
|
||||||
handler=cmd.add_command_parsers(CATS))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Parse options and call the appropriate class/method."""
|
|
||||||
cmd._main(CONF, config, category_opt, sys.argv)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@ -1,136 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg as config
|
|
||||||
from cloudv_client import client
|
|
||||||
from cloudv_ostf_adapter.common import utils
|
|
||||||
from cloudv_ostf_adapter.cmd import _common as cmd
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class ClientV1Shell(object):
|
|
||||||
"""
|
|
||||||
Represents set of capabilities to interact with Cloudvalidation API
|
|
||||||
"""
|
|
||||||
|
|
||||||
_client = client.Client(CONF.host, CONF.port, CONF.api_version)
|
|
||||||
|
|
||||||
def list_plugins(self):
|
|
||||||
"""
|
|
||||||
List plugins
|
|
||||||
"""
|
|
||||||
|
|
||||||
resp = self._client.plugins.list(load_tests=False)
|
|
||||||
for plugin in resp:
|
|
||||||
suites = plugin['suites']
|
|
||||||
plugin['suites'] = "\n".join(suites)
|
|
||||||
del plugin['tests']
|
|
||||||
utils.print_dict(plugin)
|
|
||||||
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
def list_plugin_suites(self, validation_plugin_name):
|
|
||||||
"""
|
|
||||||
List plugin suites
|
|
||||||
Required options:
|
|
||||||
--validation-plugin
|
|
||||||
"""
|
|
||||||
|
|
||||||
resp = self._client.suites.list_suites(validation_plugin_name)
|
|
||||||
suites = resp['suites']
|
|
||||||
resp['suites'] = "\n".join(suites)
|
|
||||||
del resp['name']
|
|
||||||
utils.print_dict(resp)
|
|
||||||
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
def list_plugin_tests(self, validation_plugin_name):
|
|
||||||
"""
|
|
||||||
List plugin tests
|
|
||||||
Required options:
|
|
||||||
--validation-plugin
|
|
||||||
"""
|
|
||||||
|
|
||||||
resp = self._client.suites.list_tests_for_suites(
|
|
||||||
validation_plugin_name)
|
|
||||||
tests = resp['tests']
|
|
||||||
resp['tests'] = "\n".join(tests)
|
|
||||||
del resp['name']
|
|
||||||
utils.print_dict(resp)
|
|
||||||
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
def run_suites(self, validation_plugin_name):
|
|
||||||
"""
|
|
||||||
Run plugin suites
|
|
||||||
Required options:
|
|
||||||
--validation-plugin
|
|
||||||
"""
|
|
||||||
|
|
||||||
resp = self._client.suites.run_suites(validation_plugin_name)
|
|
||||||
utils.print_list(resp,
|
|
||||||
['test', 'duration', 'result', 'report'],
|
|
||||||
obj_is_dict=True)
|
|
||||||
|
|
||||||
@cmd.args("--suite", dest="suite")
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
def run_suite(self, validation_plugin_name, suite):
|
|
||||||
"""
|
|
||||||
Run plugin suite
|
|
||||||
Required options:
|
|
||||||
--validation-plugin
|
|
||||||
--suite
|
|
||||||
"""
|
|
||||||
|
|
||||||
resp = self._client.suites.run_suite_tests(
|
|
||||||
suite, validation_plugin_name)
|
|
||||||
suite_test_reports = resp['report']
|
|
||||||
utils.print_list(suite_test_reports,
|
|
||||||
['test', 'duration', 'result', 'report'],
|
|
||||||
obj_is_dict=True)
|
|
||||||
|
|
||||||
@cmd.args("--validation-plugin-name", dest="validation_plugin_name")
|
|
||||||
@cmd.args("--test", dest="test")
|
|
||||||
def run_test(self, validation_plugin_name, test):
|
|
||||||
"""
|
|
||||||
Run plugin test
|
|
||||||
Required options:
|
|
||||||
--validation-plugin
|
|
||||||
--test
|
|
||||||
"""
|
|
||||||
|
|
||||||
resp = self._client.tests.run(test, validation_plugin_name)
|
|
||||||
utils.print_list(resp,
|
|
||||||
['test', 'duration', 'result', 'report'],
|
|
||||||
obj_is_dict=True)
|
|
||||||
|
|
||||||
|
|
||||||
CATS = {
|
|
||||||
'cloud-health-check': ClientV1Shell
|
|
||||||
}
|
|
||||||
|
|
||||||
category_opt = cfg.SubCommandOpt('category',
|
|
||||||
title='Command categories',
|
|
||||||
help='Available categories',
|
|
||||||
handler=cmd.add_command_parsers(CATS))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Parse options and call the appropriate class/method."""
|
|
||||||
cmd._main(CONF, config, category_opt, sys.argv)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@ -1,74 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import signal
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import flask
|
|
||||||
|
|
||||||
from flask.ext import restful
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg as config
|
|
||||||
from cloudv_ostf_adapter import wsgi
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
app = flask.Flask('cloudv_ostf_adapter')
|
|
||||||
api = restful.Api(app)
|
|
||||||
|
|
||||||
api.add_resource(wsgi.Plugins, '/v1/plugins')
|
|
||||||
api.add_resource(wsgi.PluginSuite,
|
|
||||||
'/v1/plugins/<plugin>/suites')
|
|
||||||
api.add_resource(wsgi.PluginTests,
|
|
||||||
'/v1/plugins/<plugin>/suites/tests')
|
|
||||||
api.add_resource(wsgi.Suites,
|
|
||||||
'/v1/plugins/<plugin>/suites/<suite>',
|
|
||||||
'/v1/plugins/<plugin>/suites/<suite>/tests')
|
|
||||||
api.add_resource(wsgi.Tests,
|
|
||||||
'/v1/plugins/<plugin>/suites/tests/<test>')
|
|
||||||
|
|
||||||
|
|
||||||
api.add_resource(wsgi.JobsCreation,
|
|
||||||
'/v1/jobs/create')
|
|
||||||
api.add_resource(wsgi.Jobs,
|
|
||||||
'/v1/jobs')
|
|
||||||
api.add_resource(wsgi.Execute,
|
|
||||||
'/v1/jobs/execute/<job_id>')
|
|
||||||
api.add_resource(wsgi.Job,
|
|
||||||
'/v1/jobs/<job_id>')
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
config.parse_args(sys.argv)
|
|
||||||
jobs_dir = CONF.rest.jobs_dir
|
|
||||||
if not os.path.exists(jobs_dir):
|
|
||||||
os.mkdir(jobs_dir)
|
|
||||||
|
|
||||||
host, port = CONF.rest.server_host, CONF.rest.server_port
|
|
||||||
try:
|
|
||||||
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
|
|
||||||
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
|
||||||
app.run(host=host, port=port,
|
|
||||||
debug=CONF.rest.debug,
|
|
||||||
use_reloader=True,
|
|
||||||
processes=100)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@ -1,120 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter import version
|
|
||||||
|
|
||||||
|
|
||||||
common_opts = [
|
|
||||||
cfg.StrOpt("health_check_config_path",
|
|
||||||
default='etc/cloudv_ostf_adapter/health_check.conf'),
|
|
||||||
cfg.StrOpt("enabled_validation_plugins", default=['fuel_health']),
|
|
||||||
cfg.StrOpt("nose_verbosity", default="-v")
|
|
||||||
]
|
|
||||||
|
|
||||||
cli_opts = [
|
|
||||||
cfg.BoolOpt('no-format', short='F', default=False, required=False),
|
|
||||||
cfg.BoolOpt('show-full-report', short='R', default=False, required=False)
|
|
||||||
]
|
|
||||||
|
|
||||||
sanity_group = cfg.OptGroup("sanity", "Sanity configuration group.")
|
|
||||||
smoke_group = cfg.OptGroup("smoke", "Smoke configuration group.")
|
|
||||||
platform_group = cfg.OptGroup("platform",
|
|
||||||
"Platform functional configuration group.")
|
|
||||||
rest_group = cfg.OptGroup("rest", "Cloudvalidation ReST API service options.")
|
|
||||||
ha_group = cfg.OptGroup("high_availability", "HA configuration group.")
|
|
||||||
|
|
||||||
|
|
||||||
sanity_opts = [
|
|
||||||
cfg.MultiStrOpt("enabled_tests", default=[
|
|
||||||
'fuel_health.tests.sanity.test_sanity_identity.SanityIdentityTest',
|
|
||||||
'fuel_health.tests.sanity.test_sanity_compute.SanityComputeTest',
|
|
||||||
'fuel_health.tests.sanity.test_sanity_heat.SanityHeatTest',
|
|
||||||
'fuel_health.tests.sanity.test_sanity_networking.NetworksTest:'
|
|
||||||
'test_list_networks_nova_network',
|
|
||||||
'fuel_health.tests.sanity.test_sanity_ceilometer.CeilometerApiTests',
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
smoke_opts = [
|
|
||||||
cfg.MultiStrOpt("enabled_tests", default=[
|
|
||||||
'fuel_health.tests.smoke.test_create_flavor.FlavorsAdminTest',
|
|
||||||
'fuel_health.tests.smoke.test_create_volume.VolumesTest',
|
|
||||||
'fuel_health.tests.smoke.test_neutron_actions.TestNeutron',
|
|
||||||
'fuel_health.tests.smoke.test_nova_create_instance_with_connectivity.'
|
|
||||||
'TestNovaNetwork',
|
|
||||||
'fuel_health.tests.smoke.test_nova_image_actions.TestImageAction',
|
|
||||||
'fuel_health.tests.smoke.test_user_create.TestUserTenantRole',
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
platform_opts = [
|
|
||||||
cfg.MultiStrOpt("enabled_tests", default=[]),
|
|
||||||
]
|
|
||||||
ha_opts = [
|
|
||||||
cfg.MultiStrOpt("enabled_tests", default=[]),
|
|
||||||
]
|
|
||||||
|
|
||||||
rest_opts = [
|
|
||||||
cfg.StrOpt('server_host',
|
|
||||||
default='127.0.0.1',
|
|
||||||
help="adapter host"),
|
|
||||||
cfg.IntOpt('server_port',
|
|
||||||
default=8777,
|
|
||||||
help="Port number"),
|
|
||||||
cfg.StrOpt('log_file',
|
|
||||||
default='/var/log/ostf.log',
|
|
||||||
help=""),
|
|
||||||
cfg.StrOpt('debug',
|
|
||||||
default=False,
|
|
||||||
help="Debug for REST API."),
|
|
||||||
cfg.StrOpt('jobs_dir',
|
|
||||||
default='/var/log/ostf',
|
|
||||||
help="Directory where jobs will be stored."),
|
|
||||||
]
|
|
||||||
|
|
||||||
rest_client_opts = [
|
|
||||||
cfg.StrOpt("host", default=os.environ.get("MCLOUDV_HOST", "localhost")),
|
|
||||||
cfg.IntOpt("port", default=os.environ.get("MCLOUDV_PORT", 8777)),
|
|
||||||
cfg.StrOpt("api_version", default=os.environ.get("MCLOUDV_API", "v1"))
|
|
||||||
]
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
CONF.register_opts(common_opts)
|
|
||||||
|
|
||||||
CONF.register_group(sanity_group)
|
|
||||||
CONF.register_group(smoke_group)
|
|
||||||
CONF.register_group(platform_group)
|
|
||||||
CONF.register_group(ha_group)
|
|
||||||
CONF.register_group(rest_group)
|
|
||||||
|
|
||||||
CONF.register_opts(sanity_opts, sanity_group)
|
|
||||||
CONF.register_opts(smoke_opts, smoke_group)
|
|
||||||
CONF.register_opts(platform_opts, platform_group)
|
|
||||||
CONF.register_opts(ha_opts, ha_group)
|
|
||||||
CONF.register_opts(rest_opts, rest_group)
|
|
||||||
|
|
||||||
#client opts
|
|
||||||
CONF.register_opts(rest_client_opts)
|
|
||||||
|
|
||||||
# CLI opts
|
|
||||||
CONF.register_cli_opts(cli_opts)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args(argv, default_config_files=None):
|
|
||||||
cfg.CONF(args=argv[1:],
|
|
||||||
project='cloudv_ostf_adapter',
|
|
||||||
version=version.version,
|
|
||||||
default_config_files=default_config_files)
|
|
@ -1,45 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
|
|
||||||
class BaseHTTPException(Exception):
|
|
||||||
|
|
||||||
def __init__(self, message=None,
|
|
||||||
http_code=400, **kwargs):
|
|
||||||
self.message = (message % kwargs
|
|
||||||
if message else self.message)
|
|
||||||
self.http_code = http_code
|
|
||||||
self.reason = "HTTP Code: %d." % http_code
|
|
||||||
super(BaseHTTPException, self).__init__(self.message + self.reason)
|
|
||||||
|
|
||||||
|
|
||||||
class BadRequest(BaseHTTPException):
|
|
||||||
http_code = 400
|
|
||||||
message = "Bad request. "
|
|
||||||
|
|
||||||
|
|
||||||
class NotFound(BaseHTTPException):
|
|
||||||
http_code = 404
|
|
||||||
message = "Not Found. "
|
|
||||||
|
|
||||||
|
|
||||||
class ConnectionRefused(BaseHTTPException):
|
|
||||||
http_code = 111
|
|
||||||
message = "Server shutdowned. "
|
|
||||||
|
|
||||||
exception_mapping = {
|
|
||||||
111: ConnectionRefused,
|
|
||||||
404: NotFound,
|
|
||||||
400: BadRequest
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from oslo_utils import importutils
|
|
||||||
|
|
||||||
|
|
||||||
class Test(object):
|
|
||||||
"""
|
|
||||||
This class represents siginificant information about test case
|
|
||||||
such as:
|
|
||||||
test
|
|
||||||
its execution report
|
|
||||||
its result
|
|
||||||
and its duration
|
|
||||||
|
|
||||||
"""
|
|
||||||
def safe_import(self):
|
|
||||||
"""
|
|
||||||
Performs safe import on demand of test class
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
importutils.import_class(self._test_class)
|
|
||||||
except ImportError:
|
|
||||||
print("Can't import test's class: %s. "
|
|
||||||
"It is not installed." % self._test_class)
|
|
||||||
|
|
||||||
def __init__(self, test_class):
|
|
||||||
"""
|
|
||||||
@param test_class: unit test case
|
|
||||||
@type test_class: basestring
|
|
||||||
"""
|
|
||||||
self._test_class = test_class
|
|
||||||
|
|
||||||
self._test_caption = test_class
|
|
||||||
|
|
||||||
self._duration = None
|
|
||||||
self._report = None
|
|
||||||
self._result = None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def result(self):
|
|
||||||
return self._result
|
|
||||||
|
|
||||||
@result.setter
|
|
||||||
def result(self, result):
|
|
||||||
self._result = result
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self):
|
|
||||||
"""
|
|
||||||
Extracts docstrings from test
|
|
||||||
:rtype: dict
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
'test': self._test_caption,
|
|
||||||
'report': self.report,
|
|
||||||
'result': self.result,
|
|
||||||
'duration': self.duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def duration(self):
|
|
||||||
"""
|
|
||||||
Test execution duration
|
|
||||||
"""
|
|
||||||
return self._duration
|
|
||||||
|
|
||||||
@duration.setter
|
|
||||||
def duration(self, duration):
|
|
||||||
self._duration = duration
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""
|
|
||||||
Returns nose test name
|
|
||||||
"""
|
|
||||||
return self._test_caption
|
|
||||||
|
|
||||||
@name.setter
|
|
||||||
def name(self, name):
|
|
||||||
self._test_caption = name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def report(self):
|
|
||||||
return self._report
|
|
||||||
|
|
||||||
@report.setter
|
|
||||||
def report(self, report):
|
|
||||||
self._report = report
|
|
@ -1,116 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import prettytable
|
|
||||||
import six
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg
|
|
||||||
from oslo.utils import encodeutils
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
def _print(pt, order):
|
|
||||||
if sys.version_info >= (3, 0):
|
|
||||||
print(pt.get_string(sortby=order))
|
|
||||||
else:
|
|
||||||
print(encodeutils.safe_encode(pt.get_string(sortby=order)))
|
|
||||||
|
|
||||||
|
|
||||||
def print_raw(d, verbose):
|
|
||||||
|
|
||||||
fn_filter = (lambda key: 0) if verbose else lambda key: key == 'report'
|
|
||||||
|
|
||||||
for row in six.iteritems(d):
|
|
||||||
if fn_filter(row[0]):
|
|
||||||
continue
|
|
||||||
|
|
||||||
print('[%s]:\t%s' % (row[0].upper(), row[1]))
|
|
||||||
print('')
|
|
||||||
|
|
||||||
|
|
||||||
def print_dict(d, verbose=False, property="Property"):
|
|
||||||
pt = prettytable.PrettyTable([property, 'Value'], caching=False)
|
|
||||||
pt.align = 'l'
|
|
||||||
|
|
||||||
fn_filter = (lambda key: 0) if verbose else lambda key: key == 'report'
|
|
||||||
|
|
||||||
[pt.add_row(list(r)) for r in six.iteritems(d) if not fn_filter(r[0])]
|
|
||||||
_print(pt, property)
|
|
||||||
|
|
||||||
|
|
||||||
def print_formatted(reports, raw_format, verbose):
|
|
||||||
|
|
||||||
if raw_format:
|
|
||||||
fn_print = print_raw
|
|
||||||
else:
|
|
||||||
fn_print = print_dict
|
|
||||||
|
|
||||||
for report in reports:
|
|
||||||
fn_print(report.description, verbose=verbose)
|
|
||||||
|
|
||||||
|
|
||||||
def print_list(objs, fields, formatters={}, order_by=None, obj_is_dict=False,
|
|
||||||
labels={}):
|
|
||||||
if not labels:
|
|
||||||
labels = {}
|
|
||||||
for field in fields:
|
|
||||||
if field not in labels:
|
|
||||||
# No underscores (use spaces instead) and uppercase any ID's
|
|
||||||
label = field.replace("_", " ").replace("id", "ID")
|
|
||||||
# Uppercase anything else that's less than 3 chars
|
|
||||||
if len(label) < 3:
|
|
||||||
label = label.upper()
|
|
||||||
# Capitalize each word otherwise
|
|
||||||
else:
|
|
||||||
label = ' '.join(word[0].upper() + word[1:]
|
|
||||||
for word in label.split())
|
|
||||||
labels[field] = label
|
|
||||||
|
|
||||||
pt = prettytable.PrettyTable(
|
|
||||||
[labels[field] for field in fields], caching=False)
|
|
||||||
# set the default alignment to left-aligned
|
|
||||||
align = dict((labels[field], 'l') for field in fields)
|
|
||||||
set_align = True
|
|
||||||
for obj in objs:
|
|
||||||
row = []
|
|
||||||
for field in fields:
|
|
||||||
if formatters and field in formatters:
|
|
||||||
row.append(formatters[field](obj))
|
|
||||||
elif obj_is_dict:
|
|
||||||
data = obj.get(field, '')
|
|
||||||
else:
|
|
||||||
data = getattr(obj, field, '')
|
|
||||||
row.append(data)
|
|
||||||
# set the alignment to right-aligned if it's a numeric
|
|
||||||
if set_align and hasattr(data, '__int__'):
|
|
||||||
align[labels[field]] = 'r'
|
|
||||||
set_align = False
|
|
||||||
pt.add_row(row)
|
|
||||||
pt._align = align
|
|
||||||
|
|
||||||
if not order_by:
|
|
||||||
order_by = fields[0]
|
|
||||||
order_by = labels[order_by]
|
|
||||||
_print(pt, order_by)
|
|
||||||
|
|
||||||
|
|
||||||
def poll_until(pollster, expected_result=None, sleep_time=5):
|
|
||||||
import time
|
|
||||||
if not callable(pollster):
|
|
||||||
raise Exception("%s is not callable" % pollster.__name__)
|
|
||||||
while pollster() != expected_result:
|
|
||||||
time.sleep(sleep_time)
|
|
@ -1,35 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from nose import loader
|
|
||||||
from oslo_utils import importutils
|
|
||||||
|
|
||||||
|
|
||||||
def do_test_discovery(classes):
|
|
||||||
"""
|
|
||||||
Will discover test cases from suites
|
|
||||||
"""
|
|
||||||
_tests = []
|
|
||||||
_loader = loader.TestLoader()
|
|
||||||
for classname in classes:
|
|
||||||
if ":" in classname:
|
|
||||||
cls, method = classname.split(":")
|
|
||||||
getattr(importutils.import_class(cls), method)
|
|
||||||
else:
|
|
||||||
suite_class = _loader.loadTestsFromTestClass(
|
|
||||||
importutils.import_class(classname))
|
|
||||||
for test_case in suite_class._precache:
|
|
||||||
_tests.append(classname + ':' + str(
|
|
||||||
test_case).split(' ')[0])
|
|
||||||
return _tests
|
|
@ -1,52 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import time
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from oslo_config import cfg as conf
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg
|
|
||||||
from cloudv_ostf_adapter.validation_plugin import base
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
_group = conf.OptGroup("fake")
|
|
||||||
CONF.register_group(_group)
|
|
||||||
_opts = [
|
|
||||||
conf.MultiStrOpt("enabled_tests", default=[
|
|
||||||
"cloudv_ostf_adapter.tests.unittests.fakes.fake_plugin."
|
|
||||||
"fake_plugin_tests.FakePluginTests"
|
|
||||||
])
|
|
||||||
]
|
|
||||||
CONF.register_opts(_opts, _group)
|
|
||||||
|
|
||||||
GROUP = 'fake'
|
|
||||||
CONF = cfg.CONF
|
|
||||||
TESTS = CONF.get(GROUP).enabled_tests
|
|
||||||
|
|
||||||
|
|
||||||
class FakePluginTests(testtools.TestCase):
|
|
||||||
|
|
||||||
def test_a(self):
|
|
||||||
time.sleep(5)
|
|
||||||
self.assertEqual("A", "A")
|
|
||||||
|
|
||||||
def test_b(self):
|
|
||||||
time.sleep(5)
|
|
||||||
self.assertIn("B", "ABCD")
|
|
||||||
|
|
||||||
|
|
||||||
def get_tests():
|
|
||||||
return base.SuiteDescriptor(GROUP, TESTS)
|
|
@ -1,51 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.validation_plugin import base
|
|
||||||
from cloudv_ostf_adapter.tests.unittests.fakes.fake_plugin import (
|
|
||||||
fake_plugin_tests)
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
SUITES = [fake_plugin_tests]
|
|
||||||
|
|
||||||
|
|
||||||
class FakeTest(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.description = {
|
|
||||||
"test": 'fake_test',
|
|
||||||
"report": "",
|
|
||||||
"result": "passed",
|
|
||||||
"duration": "0.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class FakeValidationPlugin(base.ValidationPlugin):
|
|
||||||
|
|
||||||
def __init__(self, load_tests=True):
|
|
||||||
name = 'fake'
|
|
||||||
self.test = FakeTest()
|
|
||||||
super(FakeValidationPlugin, self).__init__(
|
|
||||||
name, SUITES, load_tests=load_tests)
|
|
||||||
|
|
||||||
def run_suites(self):
|
|
||||||
return [self.test]
|
|
||||||
|
|
||||||
def run_suite(self, suite):
|
|
||||||
return [self.test]
|
|
||||||
|
|
||||||
def run_test(self, test):
|
|
||||||
return [self.test]
|
|
@ -1,323 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
import mock
|
|
||||||
from oslo_config import cfg
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.cmd import server
|
|
||||||
from cloudv_ostf_adapter.tests.unittests.fakes.fake_plugin import health_plugin
|
|
||||||
from cloudv_ostf_adapter import wsgi
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class TestServer(testtools.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.jobs_dir = '/tmp/ostf_tests_%s' % uuid.uuid1()
|
|
||||||
CONF.rest.jobs_dir = self.jobs_dir
|
|
||||||
if not os.path.exists(self.jobs_dir):
|
|
||||||
os.mkdir(self.jobs_dir)
|
|
||||||
self.plugin = health_plugin.FakeValidationPlugin()
|
|
||||||
server.app.config['TESTING'] = True
|
|
||||||
self.app = server.app.test_client()
|
|
||||||
self.actual_plugins = wsgi.validation_plugin.VALIDATION_PLUGINS
|
|
||||||
wsgi.validation_plugin.VALIDATION_PLUGINS = [self.plugin.__class__]
|
|
||||||
|
|
||||||
data = {'job': {'name': 'fake',
|
|
||||||
'tests': self.plugin.tests,
|
|
||||||
'description': 'description'}}
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/jobs/create', content_type='application/json',
|
|
||||||
data=json.dumps(data)).data
|
|
||||||
self.job_id = self._resp_to_dict(rv)['job']['id']
|
|
||||||
rv2 = self.app.post(
|
|
||||||
'/v1/jobs/create', content_type='application/json',
|
|
||||||
data=json.dumps(data)).data
|
|
||||||
self.job_id2 = self._resp_to_dict(rv2)['job']['id']
|
|
||||||
|
|
||||||
p = mock.patch('cloudv_ostf_adapter.wsgi.uuid.uuid4')
|
|
||||||
self.addCleanup(p.stop)
|
|
||||||
m = p.start()
|
|
||||||
m.return_value = 'fake_uuid'
|
|
||||||
execute = mock.patch('cloudv_ostf_adapter.wsgi.Execute._execute_job')
|
|
||||||
self.addCleanup(execute.stop)
|
|
||||||
execute.start()
|
|
||||||
super(TestServer, self).setUp()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
wsgi.validation_plugin.VALIDATION_PLUGINS = self.actual_plugins
|
|
||||||
shutil.rmtree(self.jobs_dir)
|
|
||||||
super(TestServer, self).tearDown()
|
|
||||||
|
|
||||||
def test_urlmap(self):
|
|
||||||
links = []
|
|
||||||
check_list = [
|
|
||||||
'/v1/plugins',
|
|
||||||
'/v1/plugins/<plugin>/suites/tests/<test>',
|
|
||||||
'/v1/plugins/<plugin>/suites/<suite>/tests',
|
|
||||||
'/v1/plugins/<plugin>/suites/tests',
|
|
||||||
'/v1/plugins/<plugin>/suites/<suite>',
|
|
||||||
'/v1/plugins/<plugin>/suites',
|
|
||||||
'/v1/plugins/<plugin>/suites/tests/<test>',
|
|
||||||
'/v1/jobs/create',
|
|
||||||
'/v1/jobs',
|
|
||||||
'/v1/jobs/execute/<job_id>',
|
|
||||||
'/v1/jobs/<job_id>'
|
|
||||||
]
|
|
||||||
for rule in server.app.url_map.iter_rules():
|
|
||||||
links.append(str(rule))
|
|
||||||
self.assertEqual(set(check_list) & set(links), set(check_list))
|
|
||||||
|
|
||||||
def _resp_to_dict(self, data):
|
|
||||||
if type(data) == bytes:
|
|
||||||
data = data.decode('utf-8')
|
|
||||||
return json.loads(data)
|
|
||||||
|
|
||||||
def test_plugins_no_load_tests(self):
|
|
||||||
rv = self.app.get('/v1/plugins').data
|
|
||||||
check = {
|
|
||||||
'plugins': [{'name': self.plugin.name,
|
|
||||||
'suites': self.plugin.suites,
|
|
||||||
'tests': []}]
|
|
||||||
}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_plugins_load_tests(self):
|
|
||||||
rv = self.app.get('/v1/plugins?load_tests=True').data
|
|
||||||
check = {
|
|
||||||
'plugins': [{'name': self.plugin.name,
|
|
||||||
'suites': self.plugin.suites,
|
|
||||||
'tests': self.plugin.tests}]
|
|
||||||
}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_plugin_not_found(self):
|
|
||||||
rv = self.app.get('/v1/plugins/fake2/suites').data
|
|
||||||
check = {"message": "Unsupported plugin fake2."}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_plugin_suites(self):
|
|
||||||
rv = self.app.get('/v1/plugins/fake/suites').data
|
|
||||||
check = {"plugin": {"name": self.plugin.name,
|
|
||||||
"suites": self.plugin.suites}}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_suite_plugin_not_found(self):
|
|
||||||
rv = self.app.get(
|
|
||||||
'/v1/plugins/fake2/suites/fake/tests').data
|
|
||||||
check = {u'message': u'Unsupported plugin fake2.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_suite_tests(self):
|
|
||||||
suite = self.plugin.suites[0]
|
|
||||||
url = '/v1/plugins/fake/suites/%s/tests' % suite
|
|
||||||
rv = self.app.get(url).data
|
|
||||||
tests = self.plugin.get_tests_by_suite(suite)
|
|
||||||
check = {
|
|
||||||
"plugin": {"name": self.plugin.name,
|
|
||||||
"suite": {"name": suite,
|
|
||||||
"tests": tests}}}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_suite_not_found(self):
|
|
||||||
rv = self.app.get(
|
|
||||||
'/v1/plugins/fake/suites/fake/tests').data
|
|
||||||
check = {u'message': u'Unknown suite fake.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_plugin_tests_not_found(self):
|
|
||||||
rv = self.app.get(
|
|
||||||
'/v1/plugins/fake2/suites/tests').data
|
|
||||||
check = {u'message': u'Unsupported plugin fake2.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_plugin_tests(self):
|
|
||||||
rv = self.app.get(
|
|
||||||
'/v1/plugins/fake/suites/tests').data
|
|
||||||
check = {"plugin": {"name": self.plugin.name,
|
|
||||||
"tests": self.plugin.tests}}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_run_suites(self):
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/plugins/fake/suites').data
|
|
||||||
check = {
|
|
||||||
u'plugin': {u'name': self.plugin.name,
|
|
||||||
u'report': [self.plugin.test.description]}}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_run_suites_plugin_not_found(self):
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/plugins/fake2/suites').data
|
|
||||||
check = {u'message': u'Unsupported plugin fake2.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_run_suite(self):
|
|
||||||
suite = self.plugin.suites[0]
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/plugins/fake/suites/%s' % suite).data
|
|
||||||
check = {
|
|
||||||
u'suite': {u'name': suite,
|
|
||||||
u'report': [self.plugin.test.description]}}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_run_suite_plugin_not_found(self):
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/plugins/fake2/suites/fake_suite').data
|
|
||||||
check = {u'message': u'Unsupported plugin fake2.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_run_suite_suite_not_found(self):
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/plugins/fake/suites/fake_suite').data
|
|
||||||
check = {u'message': u'Unknown suite fake_suite.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_run_test(self):
|
|
||||||
test = self.plugin.tests[0]
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/plugins/fake/suites/tests/%s' % test).data
|
|
||||||
check = {
|
|
||||||
u'plugin': {u'name': self.plugin.name,
|
|
||||||
u'test': test,
|
|
||||||
u'report': [self.plugin.test.description]}}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_run_test_plugin_not_found(self):
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/plugins/fake2/suites/tests/fake_test').data
|
|
||||||
check = {u'message': u'Unsupported plugin fake2.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_run_test_not_found(self):
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/plugins/fake/suites/tests/fake_test').data
|
|
||||||
check = {u'message': u'Test fake_test not found.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_job_create_json_not_found(self):
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/jobs/create').data
|
|
||||||
check = {u'message': u'JSON is missing.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_job_create_job_key_found(self):
|
|
||||||
data = {'fake': {}}
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/jobs/create', content_type='application/json',
|
|
||||||
data=json.dumps(data)).data
|
|
||||||
check = {u'message': u"JSON doesn't have `job` key."}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_job_create_fields_not_found(self):
|
|
||||||
data = {'job': {'name': 'fake'}}
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/jobs/create', content_type='application/json',
|
|
||||||
data=json.dumps(data)).data
|
|
||||||
check = {u'message': u'Fields description,tests are not specified.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_job_create_tests_not_found(self):
|
|
||||||
data = {'job': {'name': 'fake',
|
|
||||||
'tests': ['a', 'b'],
|
|
||||||
'description': 'description'}}
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/jobs/create', content_type='application/json',
|
|
||||||
data=json.dumps(data)).data
|
|
||||||
check = {u'message': u'Tests not found (a,b).'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_job_create(self):
|
|
||||||
data = {'job': {'name': 'fake',
|
|
||||||
'tests': self.plugin.tests,
|
|
||||||
'description': 'description'}}
|
|
||||||
rv = self.app.post(
|
|
||||||
'/v1/jobs/create', content_type='application/json',
|
|
||||||
data=json.dumps(data)).data
|
|
||||||
check = {u'job': {u'description': u'description',
|
|
||||||
u'id': u'fake_uuid',
|
|
||||||
u'name': u'fake',
|
|
||||||
u'status': u'CREATED',
|
|
||||||
u'tests': self.plugin.tests}}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_execute_job_not_found(self):
|
|
||||||
rv = self.app.post('/v1/jobs/execute/fake').data
|
|
||||||
check = {u'message': u'Job not found.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_execute_job(self):
|
|
||||||
rv = self.app.post('/v1/jobs/execute/%s' % self.job_id).data
|
|
||||||
check = {u'job': {u'description': u'description',
|
|
||||||
u'id': self.job_id,
|
|
||||||
u'name': u'fake',
|
|
||||||
u'report': [],
|
|
||||||
u'status': u'IN PROGRESS',
|
|
||||||
u'tests': self.plugin.tests}}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_get_list_jobs(self):
|
|
||||||
rv = self.app.get('/v1/jobs').data
|
|
||||||
resp_dict = self._resp_to_dict(rv)['jobs']
|
|
||||||
check = [
|
|
||||||
{u'description': u'description',
|
|
||||||
u'id': self.job_id,
|
|
||||||
u'name': u'fake',
|
|
||||||
u'status': u'CREATED',
|
|
||||||
u'tests': self.plugin.tests},
|
|
||||||
{u'description': u'description',
|
|
||||||
u'id': self.job_id2,
|
|
||||||
u'name': u'fake',
|
|
||||||
u'status': u'CREATED',
|
|
||||||
u'tests': self.plugin.tests}]
|
|
||||||
for job in resp_dict:
|
|
||||||
self.assertIn(job, check)
|
|
||||||
|
|
||||||
def test_get_job_not_found(self):
|
|
||||||
rv = self.app.get('/v1/jobs/fake').data
|
|
||||||
check = {u'message': u'Job not found.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_get_job(self):
|
|
||||||
rv = self.app.get('/v1/jobs/%s' % self.job_id).data
|
|
||||||
check = {'job': {u'description': u'description',
|
|
||||||
u'id': self.job_id,
|
|
||||||
u'name': u'fake',
|
|
||||||
u'status': u'CREATED',
|
|
||||||
u'tests': self.plugin.tests}}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_delete_job_not_found(self):
|
|
||||||
rv = self.app.delete('/v1/jobs/fake').data
|
|
||||||
check = {u'message': u'Job not found.'}
|
|
||||||
self.assertEqual(self._resp_to_dict(rv), check)
|
|
||||||
|
|
||||||
def test_delete_job(self):
|
|
||||||
before = self._resp_to_dict(
|
|
||||||
self.app.get('/v1/jobs').data)
|
|
||||||
jobs_id_before = [j['id'] for j in before['jobs']]
|
|
||||||
self.assertEqual(len(jobs_id_before), 2)
|
|
||||||
self.app.delete('/v1/jobs/%s' % self.job_id)
|
|
||||||
after = self._resp_to_dict(
|
|
||||||
self.app.get('/v1/jobs').data)
|
|
||||||
jobs_id_after = [j['id'] for j in after['jobs']]
|
|
||||||
self.assertEqual(len(jobs_id_after), 1)
|
|
@ -1,42 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter import validation_plugin
|
|
||||||
from cloudv_ostf_adapter.tests.unittests.fakes.fake_plugin import health_plugin
|
|
||||||
|
|
||||||
|
|
||||||
class TestWorkflow(testtools.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.plugin = health_plugin.FakeValidationPlugin()
|
|
||||||
validation_plugin.VALIDATION_PLUGINS.append(self.plugin.__class__)
|
|
||||||
super(TestWorkflow, self).setUp()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
validation_plugin.VALIDATION_PLUGINS.pop(
|
|
||||||
validation_plugin.VALIDATION_PLUGINS.index(
|
|
||||||
self.plugin.__class__))
|
|
||||||
super(TestWorkflow, self).tearDown()
|
|
||||||
|
|
||||||
def test_verify_plugins(self):
|
|
||||||
self.assertEqual(2, len(validation_plugin.VALIDATION_PLUGINS))
|
|
||||||
self.assertIn(self.plugin.__class__,
|
|
||||||
validation_plugin.VALIDATION_PLUGINS)
|
|
||||||
|
|
||||||
def test_verify_fake_plugin(self):
|
|
||||||
test_patsh = self.plugin._collect_test(self.plugin.tests)
|
|
||||||
self.assertEqual(2, len(test_patsh))
|
|
||||||
self.assertEqual(2, len(self.plugin.tests))
|
|
@ -1,19 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
from cloudv_ostf_adapter.validation_plugin import fuel_health
|
|
||||||
|
|
||||||
|
|
||||||
VALIDATION_PLUGINS = [
|
|
||||||
fuel_health.FuelHealthPlugin,
|
|
||||||
]
|
|
@ -1,151 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from oslo_utils import importutils
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg
|
|
||||||
from cloudv_ostf_adapter.common import utils
|
|
||||||
from cloudv_ostf_adapter.nose_plugin import discovery
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class SuiteDescriptor(object):
|
|
||||||
|
|
||||||
suite_attrs = ['test_group', 'tests']
|
|
||||||
test_attrs = ['tests']
|
|
||||||
|
|
||||||
def __init__(self, test_group_definition, tests):
|
|
||||||
"""
|
|
||||||
Describes test specific test group
|
|
||||||
@param test_group_definition: Test group definition
|
|
||||||
(i.e. sanity, smoke, HA, platform)
|
|
||||||
@type test_group_definition: basestring
|
|
||||||
@param tests: list of tests per test group
|
|
||||||
@type tests: list
|
|
||||||
"""
|
|
||||||
self.test_group = test_group_definition
|
|
||||||
self.tests = tests
|
|
||||||
|
|
||||||
def print_tests_description(self):
|
|
||||||
utils.print_list(self, self.test_attrs)
|
|
||||||
|
|
||||||
def print_description(self):
|
|
||||||
utils.print_dict(self)
|
|
||||||
|
|
||||||
|
|
||||||
class ValidationPlugin(object):
|
|
||||||
|
|
||||||
test_executor = "%(test_module_path)s:%(class)s.%(test)s"
|
|
||||||
|
|
||||||
def __init__(self, name, suites, load_tests=True):
|
|
||||||
__suites = []
|
|
||||||
for suite in suites:
|
|
||||||
__suites.extend(suite.TESTS)
|
|
||||||
|
|
||||||
self.name = name
|
|
||||||
self.suites = __suites
|
|
||||||
self._suites = suites
|
|
||||||
self.tests = (self.get_tests()
|
|
||||||
if load_tests else [])
|
|
||||||
|
|
||||||
def get_tests(self):
|
|
||||||
"""
|
|
||||||
Test collector
|
|
||||||
"""
|
|
||||||
tests = []
|
|
||||||
for suite in self._suites:
|
|
||||||
_tests = discovery.do_test_discovery(
|
|
||||||
suite.TESTS)
|
|
||||||
tests.extend(_tests)
|
|
||||||
return tests
|
|
||||||
|
|
||||||
def _collect_test(self, tests):
|
|
||||||
test_suites_paths = []
|
|
||||||
for test in tests:
|
|
||||||
classpath, test_method = test.split(":")
|
|
||||||
classname = classpath.split(".")[-1]
|
|
||||||
module = importutils.import_class(
|
|
||||||
classpath).__module__
|
|
||||||
test_module_path = os.path.abspath(
|
|
||||||
sys.modules[module].__file__)
|
|
||||||
if test_module_path.endswith("pyc"):
|
|
||||||
test_module_path = test_module_path[:-1]
|
|
||||||
test_suites_paths.append(
|
|
||||||
self.test_executor %
|
|
||||||
{
|
|
||||||
'test_module_path': test_module_path,
|
|
||||||
'class': classname,
|
|
||||||
'test': test_method
|
|
||||||
})
|
|
||||||
return test_suites_paths
|
|
||||||
|
|
||||||
def get_tests_by_suite(self, suite):
|
|
||||||
tests = []
|
|
||||||
for test in self.tests:
|
|
||||||
if suite in test:
|
|
||||||
tests.append(test)
|
|
||||||
return tests
|
|
||||||
|
|
||||||
def descriptor(self):
|
|
||||||
"""
|
|
||||||
Returns Plugin descriptor that contains:
|
|
||||||
- plugin name
|
|
||||||
- plugin suites
|
|
||||||
- plugin tests
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"name": self.name,
|
|
||||||
"suites": self.suites,
|
|
||||||
"tests": self.tests,
|
|
||||||
}
|
|
||||||
|
|
||||||
def run_suites(self):
|
|
||||||
"""
|
|
||||||
Runs all tests from all suites
|
|
||||||
"""
|
|
||||||
raise Exception("Plugin doesn't support suites execution.")
|
|
||||||
|
|
||||||
def run_suite(self, suite):
|
|
||||||
"""
|
|
||||||
Runs specific suite
|
|
||||||
"""
|
|
||||||
raise Exception("Plugin doesn't support suite execution.")
|
|
||||||
|
|
||||||
def run_test(self, test):
|
|
||||||
"""
|
|
||||||
Runs specific test
|
|
||||||
"""
|
|
||||||
raise Exception("Plugin doesn't support test execution.")
|
|
||||||
|
|
||||||
def run_suite_within_cli(self, suite):
|
|
||||||
"""
|
|
||||||
Runs test suite with view for CLI
|
|
||||||
"""
|
|
||||||
raise Exception("CLI execution is not supported.")
|
|
||||||
|
|
||||||
def run_suites_within_cli(self):
|
|
||||||
"""
|
|
||||||
Runs test suites with view for CLI
|
|
||||||
"""
|
|
||||||
raise Exception("CLI execution is not supported.")
|
|
||||||
|
|
||||||
def run_test_within_cli(self, test):
|
|
||||||
"""
|
|
||||||
Runs test suites with view for CLI
|
|
||||||
"""
|
|
||||||
raise Exception("CLI execution is not supported.")
|
|
@ -1,178 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
try:
|
|
||||||
from StringIO import StringIO
|
|
||||||
except ImportError:
|
|
||||||
from io import StringIO
|
|
||||||
|
|
||||||
from nose import core
|
|
||||||
from oslo_utils import importutils
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg
|
|
||||||
from cloudv_ostf_adapter.common import object_descriptors
|
|
||||||
from cloudv_ostf_adapter.validation_plugin import base
|
|
||||||
from cloudv_ostf_adapter.validation_plugin.fuel_health import sanity
|
|
||||||
from cloudv_ostf_adapter.validation_plugin.fuel_health import smoke
|
|
||||||
from cloudv_ostf_adapter.validation_plugin.fuel_health import high_availability
|
|
||||||
from cloudv_ostf_adapter.validation_plugin.fuel_health import platform
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
SUITES = [
|
|
||||||
sanity,
|
|
||||||
smoke,
|
|
||||||
high_availability,
|
|
||||||
platform,
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class FuelHealthPlugin(base.ValidationPlugin):
|
|
||||||
|
|
||||||
def setup_fuel_health_on_need(self):
|
|
||||||
FUEL_HEALTH_CONF = importutils.import_module("fuel_health.config")
|
|
||||||
|
|
||||||
@FUEL_HEALTH_CONF.process_singleton
|
|
||||||
class MonkeyPatchFuelHealthConf(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.register_opts()
|
|
||||||
self.compute = cfg.CONF.compute
|
|
||||||
self.identity = cfg.CONF.identity
|
|
||||||
self.network = cfg.CONF.network
|
|
||||||
self.volume = cfg.CONF.volume
|
|
||||||
self.murano = cfg.CONF.murano
|
|
||||||
self.heat = cfg.CONF.heat
|
|
||||||
self.sahara = cfg.CONF.sahara
|
|
||||||
|
|
||||||
def register_opts(self):
|
|
||||||
FUEL_HEALTH_CONF.register_compute_opts(CONF)
|
|
||||||
FUEL_HEALTH_CONF.register_identity_opts(CONF)
|
|
||||||
FUEL_HEALTH_CONF.register_network_opts(CONF)
|
|
||||||
FUEL_HEALTH_CONF.register_volume_opts(CONF)
|
|
||||||
FUEL_HEALTH_CONF.register_murano_opts(CONF)
|
|
||||||
FUEL_HEALTH_CONF.register_heat_opts(CONF)
|
|
||||||
FUEL_HEALTH_CONF.register_sahara_opts(CONF)
|
|
||||||
|
|
||||||
FUEL_HEALTH_CONF.FileConfig = MonkeyPatchFuelHealthConf
|
|
||||||
|
|
||||||
MonkeyPatchFuelHealthConf()
|
|
||||||
|
|
||||||
def __init__(self, load_tests=True):
|
|
||||||
self.setup_fuel_health_on_need()
|
|
||||||
super(FuelHealthPlugin, self).__init__(
|
|
||||||
'fuel_health', SUITES, load_tests=load_tests)
|
|
||||||
|
|
||||||
def get_tests(self):
|
|
||||||
try:
|
|
||||||
return super(FuelHealthPlugin, self).get_tests()
|
|
||||||
except Exception:
|
|
||||||
print("fuel_health is not installed.")
|
|
||||||
|
|
||||||
def _get_duration_from_report(self, report):
|
|
||||||
for line in report:
|
|
||||||
if line.startswith("Ran"):
|
|
||||||
return line.split(" ")[-1]
|
|
||||||
|
|
||||||
def _get_test_name_from_report(self, report):
|
|
||||||
if len(report) and not report[0].startswith('ERROR'):
|
|
||||||
return report[0]
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def _get_test_name_from_class(self, cls_name):
|
|
||||||
return cls_name.split(':')[1]
|
|
||||||
|
|
||||||
def _execute_and_report(self, test_suite_paths):
|
|
||||||
"""
|
|
||||||
Executes and assembles report right after each test execution
|
|
||||||
@param test_suite_paths: FS-based path to python module
|
|
||||||
@type test_suite_paths: list of basestring
|
|
||||||
@rtype: list of object_descriptors.Test
|
|
||||||
"""
|
|
||||||
reports = []
|
|
||||||
for test in test_suite_paths:
|
|
||||||
suites_report = StringIO()
|
|
||||||
sys.stderr = suites_report
|
|
||||||
result = core.TestProgram(
|
|
||||||
argv=["--tests", test, CONF.nose_verbosity],
|
|
||||||
exit=False).success
|
|
||||||
|
|
||||||
test_descr = object_descriptors.Test(test)
|
|
||||||
test_descr.report = "".join(suites_report.buflist)
|
|
||||||
|
|
||||||
# @TODO(okyrylchuk): there's no way to extract test
|
|
||||||
# description from report when test fails, so it should
|
|
||||||
# be implemented in a rather different way
|
|
||||||
|
|
||||||
_name = self._get_test_name_from_report(suites_report.buflist)
|
|
||||||
_name = _name or self._get_test_name_from_class(test_descr.name)
|
|
||||||
|
|
||||||
test_descr.name = _name
|
|
||||||
|
|
||||||
test_descr.duration = self._get_duration_from_report(
|
|
||||||
suites_report.buflist)
|
|
||||||
test_descr.result = "Passed" if result else "Failed"
|
|
||||||
|
|
||||||
reports.append(test_descr)
|
|
||||||
|
|
||||||
return reports
|
|
||||||
|
|
||||||
def run_suites(self):
|
|
||||||
safe_stderr = sys.stderr
|
|
||||||
test_suites_paths = self.setup_execution(self.tests)
|
|
||||||
reports = self._execute_and_report(test_suites_paths)
|
|
||||||
sys.stderr = safe_stderr
|
|
||||||
return reports
|
|
||||||
|
|
||||||
def setup_execution(self, tests):
|
|
||||||
test_suites_paths = self._collect_test(tests)
|
|
||||||
os.environ.update(
|
|
||||||
{"CUSTOM_FUEL_CONFIG": CONF.health_check_config_path})
|
|
||||||
return test_suites_paths
|
|
||||||
|
|
||||||
def run_suite(self, suite):
|
|
||||||
safe_stderr = sys.stderr
|
|
||||||
if ":" in suite:
|
|
||||||
raise Exception(
|
|
||||||
"%s is a test case, but not test suite." % suite)
|
|
||||||
else:
|
|
||||||
tests = self.get_tests_by_suite(suite)
|
|
||||||
test_suites_paths = self.setup_execution(tests)
|
|
||||||
reports = self._execute_and_report(test_suites_paths)
|
|
||||||
sys.stderr = safe_stderr
|
|
||||||
return reports
|
|
||||||
|
|
||||||
def run_suites_within_cli(self):
|
|
||||||
return self.run_suites()
|
|
||||||
|
|
||||||
def run_suite_within_cli(self, suite):
|
|
||||||
return self.run_suite(suite)
|
|
||||||
|
|
||||||
def run_test(self, test):
|
|
||||||
safe_stderr = sys.stderr
|
|
||||||
if ":" not in test:
|
|
||||||
raise Exception(
|
|
||||||
"%s is a test suite, but not test case." % test)
|
|
||||||
else:
|
|
||||||
test_suites_paths = self.setup_execution([test])
|
|
||||||
reports = self._execute_and_report(test_suites_paths)
|
|
||||||
sys.stderr = safe_stderr
|
|
||||||
return reports
|
|
||||||
|
|
||||||
def run_test_within_cli(self, test):
|
|
||||||
return self.run_test(test)
|
|
@ -1,24 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg
|
|
||||||
from cloudv_ostf_adapter.validation_plugin import base
|
|
||||||
|
|
||||||
GROUP = 'high_availability'
|
|
||||||
CONF = cfg.CONF
|
|
||||||
TESTS = CONF.get(GROUP).enabled_tests
|
|
||||||
|
|
||||||
|
|
||||||
def get_tests():
|
|
||||||
return base.SuiteDescriptor(GROUP, TESTS)
|
|
@ -1,24 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg
|
|
||||||
from cloudv_ostf_adapter.validation_plugin import base
|
|
||||||
|
|
||||||
GROUP = 'platform'
|
|
||||||
CONF = cfg.CONF
|
|
||||||
TESTS = CONF.get(GROUP).enabled_tests
|
|
||||||
|
|
||||||
|
|
||||||
def get_tests():
|
|
||||||
return base.SuiteDescriptor(GROUP, TESTS)
|
|
@ -1,24 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg
|
|
||||||
from cloudv_ostf_adapter.validation_plugin import base
|
|
||||||
|
|
||||||
GROUP = 'sanity'
|
|
||||||
CONF = cfg.CONF
|
|
||||||
TESTS = CONF.get(GROUP).enabled_tests
|
|
||||||
|
|
||||||
|
|
||||||
def get_tests():
|
|
||||||
return base.SuiteDescriptor(GROUP, TESTS)
|
|
@ -1,24 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter.common import cfg
|
|
||||||
from cloudv_ostf_adapter.validation_plugin import base
|
|
||||||
|
|
||||||
GROUP = 'smoke'
|
|
||||||
CONF = cfg.CONF
|
|
||||||
TESTS = CONF.get(GROUP).enabled_tests
|
|
||||||
|
|
||||||
|
|
||||||
def get_tests():
|
|
||||||
return base.SuiteDescriptor(GROUP, TESTS)
|
|
@ -1,16 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
release = "cloudv-ostf-adapter"
|
|
||||||
version = "2015.1"
|
|
@ -1,272 +0,0 @@
|
|||||||
# Copyright 2015 Mirantis, Inc
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import json
|
|
||||||
import multiprocessing
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from flask.ext import restful
|
|
||||||
from flask.ext.restful import abort
|
|
||||||
from flask.ext.restful import reqparse
|
|
||||||
from flask import request
|
|
||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from cloudv_ostf_adapter import validation_plugin
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
CREATED = 'CREATED'
|
|
||||||
IN_PROGRESS = 'IN PROGRESS'
|
|
||||||
COMPLETED = 'COMPLETED'
|
|
||||||
|
|
||||||
|
|
||||||
class BaseTests(restful.Resource):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(BaseTests, self).__init__(*args, **kwargs)
|
|
||||||
self.plugins = {}
|
|
||||||
for plugin in validation_plugin.VALIDATION_PLUGINS:
|
|
||||||
_plugin = plugin(load_tests=False)
|
|
||||||
self.plugins[_plugin.name] = _plugin
|
|
||||||
|
|
||||||
def load_tests(self):
|
|
||||||
for plugin in self.plugins.values():
|
|
||||||
plugin.tests = plugin.get_tests()
|
|
||||||
|
|
||||||
def get_plugin(self, **kwargs):
|
|
||||||
plugin = kwargs.pop('plugin', None)
|
|
||||||
if plugin is None or plugin not in self.plugins:
|
|
||||||
abort(404,
|
|
||||||
message='Unsupported plugin %s.' % plugin)
|
|
||||||
return self.plugins[plugin]
|
|
||||||
|
|
||||||
def get_suite(self, plugin, suite=None):
|
|
||||||
if suite not in plugin.suites:
|
|
||||||
abort(404,
|
|
||||||
message='Unknown suite %s.' % suite)
|
|
||||||
return suite
|
|
||||||
|
|
||||||
def path_from_job_name(self, job_id):
|
|
||||||
return '/'.join((CONF.rest.jobs_dir, job_id))
|
|
||||||
|
|
||||||
def get_job(self, **kwargs):
|
|
||||||
job_id = kwargs.pop('job_id', None)
|
|
||||||
if job_id is None:
|
|
||||||
abort(400,
|
|
||||||
message="Job id is missing.")
|
|
||||||
file_name = self.path_from_job_name(job_id)
|
|
||||||
if not os.path.exists(file_name):
|
|
||||||
abort(404,
|
|
||||||
message="Job not found.")
|
|
||||||
return (job_id, file_name)
|
|
||||||
|
|
||||||
|
|
||||||
class Plugins(BaseTests):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(Plugins, self).__init__(*args, **kwargs)
|
|
||||||
self.parser = reqparse.RequestParser()
|
|
||||||
self.parser.add_argument('load_tests',
|
|
||||||
type=str,
|
|
||||||
location='args',
|
|
||||||
required=False)
|
|
||||||
|
|
||||||
def get(self, **kwargs):
|
|
||||||
args = self.parser.parse_args()
|
|
||||||
load_tests = args.pop('load_tests', False)
|
|
||||||
if load_tests in ['True', 'true', '1']:
|
|
||||||
self.load_tests()
|
|
||||||
plugins = [
|
|
||||||
{'name': p.name,
|
|
||||||
'suites': p.suites,
|
|
||||||
'tests': p.tests}
|
|
||||||
for p in self.plugins.values()
|
|
||||||
]
|
|
||||||
return {'plugins': plugins}
|
|
||||||
|
|
||||||
|
|
||||||
class PluginSuite(BaseTests):
|
|
||||||
|
|
||||||
def get(self, **kwargs):
|
|
||||||
plugin = self.get_plugin(**kwargs)
|
|
||||||
return {'plugin': {'name': plugin.name,
|
|
||||||
'suites': plugin.suites}}
|
|
||||||
|
|
||||||
def post(self, **kwargs):
|
|
||||||
plugin = self.get_plugin(**kwargs)
|
|
||||||
self.load_tests()
|
|
||||||
reports = plugin.run_suites()
|
|
||||||
report = [r.description for r in reports]
|
|
||||||
return {"plugin": {"name": plugin.name,
|
|
||||||
"report": report}}
|
|
||||||
|
|
||||||
|
|
||||||
class PluginTests(BaseTests):
|
|
||||||
|
|
||||||
def get(self, **kwargs):
|
|
||||||
plugin = self.get_plugin(**kwargs)
|
|
||||||
self.load_tests()
|
|
||||||
return {'plugin': {'name': plugin.name,
|
|
||||||
'tests': plugin.tests}}
|
|
||||||
|
|
||||||
|
|
||||||
class Suites(BaseTests):
|
|
||||||
|
|
||||||
def get(self, **kwargs):
|
|
||||||
plugin = self.get_plugin(**kwargs)
|
|
||||||
_suite = kwargs.pop('suite', None)
|
|
||||||
suite = self.get_suite(plugin, suite=_suite)
|
|
||||||
self.load_tests()
|
|
||||||
tests = plugin.get_tests_by_suite(suite)
|
|
||||||
return {'plugin': {'name': plugin.name,
|
|
||||||
'suite': {'name': suite,
|
|
||||||
'tests': tests}}}
|
|
||||||
|
|
||||||
def post(self, **kwargs):
|
|
||||||
plugin = self.get_plugin(**kwargs)
|
|
||||||
_suite = kwargs.pop('suite', None)
|
|
||||||
suite = self.get_suite(plugin, suite=_suite)
|
|
||||||
self.load_tests()
|
|
||||||
reports = plugin.run_suite(suite)
|
|
||||||
report = [r.description for r in reports]
|
|
||||||
return {"suite": {"name": suite,
|
|
||||||
"report": report}}
|
|
||||||
|
|
||||||
|
|
||||||
class Tests(BaseTests):
|
|
||||||
|
|
||||||
def post(self, **kwargs):
|
|
||||||
plugin = self.get_plugin(**kwargs)
|
|
||||||
self.load_tests()
|
|
||||||
test = kwargs.pop('test', None)
|
|
||||||
if test is None or test not in plugin.tests:
|
|
||||||
abort(404,
|
|
||||||
message="Test %s not found." % test)
|
|
||||||
reports = plugin.run_test(test)
|
|
||||||
report = [r.description for r in reports]
|
|
||||||
return {"plugin": {"name": plugin.name,
|
|
||||||
"test": test,
|
|
||||||
"report": report}}
|
|
||||||
|
|
||||||
|
|
||||||
class JobsCreation(BaseTests):
|
|
||||||
|
|
||||||
def post(self, **kwargs):
|
|
||||||
try:
|
|
||||||
data = request.json
|
|
||||||
except Exception:
|
|
||||||
abort(400,
|
|
||||||
message="JSON is missing.")
|
|
||||||
if data is None:
|
|
||||||
abort(400,
|
|
||||||
message="JSON is missing.")
|
|
||||||
job = data.get('job', None)
|
|
||||||
if job is None:
|
|
||||||
abort(400,
|
|
||||||
message="JSON doesn't have `job` key.")
|
|
||||||
mandatory = ['name',
|
|
||||||
'tests',
|
|
||||||
'description']
|
|
||||||
missing = set(mandatory) - set(job.keys())
|
|
||||||
missing = list(missing)
|
|
||||||
missing.sort()
|
|
||||||
if missing:
|
|
||||||
abort(400,
|
|
||||||
message="Fields %s are not specified." % ','.join(missing))
|
|
||||||
self.load_tests()
|
|
||||||
filtered_tests = []
|
|
||||||
for p in self.plugins.values():
|
|
||||||
tests_in_plugin = set(p.tests) & set(job['tests'])
|
|
||||||
filtered_tests.extend(tests_in_plugin)
|
|
||||||
not_found = set(job['tests']) - set(filtered_tests)
|
|
||||||
not_found = list(not_found)
|
|
||||||
not_found.sort()
|
|
||||||
if not_found:
|
|
||||||
abort(400,
|
|
||||||
message="Tests not found (%s)." % ','.join(not_found))
|
|
||||||
job_uuid = str(uuid.uuid4())
|
|
||||||
file_name = self.path_from_job_name(job_uuid)
|
|
||||||
job['status'] = CREATED
|
|
||||||
with open(file_name, 'w') as f:
|
|
||||||
f.write(json.dumps(job))
|
|
||||||
job['id'] = job_uuid
|
|
||||||
return {'job': job}
|
|
||||||
|
|
||||||
|
|
||||||
class Execute(BaseTests):
|
|
||||||
|
|
||||||
def post(self, **kwargs):
|
|
||||||
job_id, file_name = self.get_job(**kwargs)
|
|
||||||
data = {}
|
|
||||||
with open(file_name, 'r') as f:
|
|
||||||
data = json.loads(f.read())
|
|
||||||
with open(file_name, 'w') as f:
|
|
||||||
data['status'] = IN_PROGRESS
|
|
||||||
data['report'] = []
|
|
||||||
f.write(json.dumps(data))
|
|
||||||
p = multiprocessing.Process(target=self._execute_job,
|
|
||||||
args=(data, job_id))
|
|
||||||
p.start()
|
|
||||||
job = data.copy()
|
|
||||||
job['id'] = job_id
|
|
||||||
return {'job': job}
|
|
||||||
|
|
||||||
def _execute_job(self, data, job_id):
|
|
||||||
tests = data['tests']
|
|
||||||
self.load_tests()
|
|
||||||
reports = []
|
|
||||||
for name, plugin in self.plugins.iteritems():
|
|
||||||
tests_in_plugin = set(plugin.tests) & set(tests)
|
|
||||||
for test in tests_in_plugin:
|
|
||||||
results = plugin.run_test(test)
|
|
||||||
report = [r.description for r in results].pop()
|
|
||||||
report['test'] = test
|
|
||||||
reports.append(report)
|
|
||||||
data['status'] = COMPLETED
|
|
||||||
data['report'] = reports
|
|
||||||
file_name = self.path_from_job_name(job_id)
|
|
||||||
with open(file_name, 'w') as f:
|
|
||||||
f.write(json.dumps(data))
|
|
||||||
|
|
||||||
|
|
||||||
class Job(BaseTests):
|
|
||||||
|
|
||||||
def get(self, **kwargs):
|
|
||||||
job_id, file_name = self.get_job(**kwargs)
|
|
||||||
data = {}
|
|
||||||
with open(file_name, 'r') as f:
|
|
||||||
data = json.loads(f.read())
|
|
||||||
job = data.copy()
|
|
||||||
job['id'] = job_id
|
|
||||||
return {'job': job}
|
|
||||||
|
|
||||||
def delete(self, **kwargs):
|
|
||||||
job_id, file_name = self.get_job(**kwargs)
|
|
||||||
os.remove(file_name)
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
class Jobs(BaseTests):
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
res = []
|
|
||||||
jobs = [f for (dp, dn, f) in os.walk(CONF.rest.jobs_dir)][0]
|
|
||||||
for job in jobs:
|
|
||||||
file_name = self.path_from_job_name(job)
|
|
||||||
with open(file_name, 'r') as f:
|
|
||||||
data = json.loads(f.read())
|
|
||||||
data['id'] = job
|
|
||||||
res.append(data)
|
|
||||||
return {'jobs': res}
|
|
@ -1,316 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
||||||
#sys.path.insert(0, os.path.abspath('.'))
|
|
||||||
|
|
||||||
# -- General configuration ----------------------------------------------------
|
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
|
||||||
#needs_sphinx = '1.0'
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
|
||||||
extensions = ['sphinx.ext.todo',
|
|
||||||
'sphinx.ext.viewcode',
|
|
||||||
'oslosphinx']
|
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
|
||||||
templates_path = ['_templates']
|
|
||||||
|
|
||||||
# The suffix of source filenames.
|
|
||||||
source_suffix = '.rst'
|
|
||||||
|
|
||||||
# The encoding of source files.
|
|
||||||
#source_encoding = 'utf-8-sig'
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = 'index'
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = u'cloudv-ostf-adapter'
|
|
||||||
copyright = u'2013, OpenStack Foundation'
|
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
|
||||||
# |version| and |release|, also used in various other places throughout the
|
|
||||||
# built documents.
|
|
||||||
#
|
|
||||||
# The short X.Y version.
|
|
||||||
from cloudv_ostf_adapter import version
|
|
||||||
# The full version, including alpha/beta/rc tags.
|
|
||||||
release = version.release
|
|
||||||
# The short X.Y version.
|
|
||||||
version = version.release
|
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
||||||
# for a list of supported languages.
|
|
||||||
#language = None
|
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
|
||||||
# non-false value, then it is used:
|
|
||||||
#today = ''
|
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
|
||||||
#today_fmt = '%B %d, %Y'
|
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
|
||||||
# directories to ignore when looking for source files.
|
|
||||||
exclude_patterns = ['_build']
|
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents
|
|
||||||
#default_role = None
|
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
|
||||||
add_function_parentheses = True
|
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
|
||||||
# unit titles (such as .. function::).
|
|
||||||
add_module_names = True
|
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
|
||||||
# output. They are ignored by default.
|
|
||||||
#show_authors = False
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'sphinx'
|
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
|
||||||
modindex_common_prefix = ['cloudv_ostf_adapter.']
|
|
||||||
|
|
||||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
|
||||||
#keep_warnings = False
|
|
||||||
|
|
||||||
# -- Options for HTML output --------------------------------------------------
|
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
||||||
# a list of builtin themes.
|
|
||||||
# html_theme_path = ["."]
|
|
||||||
# html_theme = '_theme'
|
|
||||||
# html_static_path = ['_static']
|
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
|
||||||
# further. For a list of options available for each theme, see the
|
|
||||||
# documentation.
|
|
||||||
#html_theme_options = {}
|
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
|
||||||
#html_theme_path = []
|
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
|
||||||
# "<project> v<release> documentation".
|
|
||||||
#html_title = None
|
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
|
||||||
#html_short_title = None
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
|
||||||
# of the sidebar.
|
|
||||||
#html_logo = None
|
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
|
||||||
# pixels large.
|
|
||||||
#html_favicon = None
|
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
||||||
# html_static_path = ['_static']
|
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
|
||||||
# using the given strftime format.
|
|
||||||
#html_last_updated_fmt = '%b %d, %Y'
|
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
|
||||||
# typographically correct entities.
|
|
||||||
#html_use_smartypants = True
|
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
|
||||||
#html_sidebars = {}
|
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
|
||||||
# template names.
|
|
||||||
#html_additional_pages = {}
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#html_domain_indices = True
|
|
||||||
|
|
||||||
# If false, no index is generated.
|
|
||||||
#html_use_index = True
|
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
|
||||||
#html_split_index = False
|
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
|
||||||
#html_show_sourcelink = True
|
|
||||||
|
|
||||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
|
||||||
#html_show_sphinx = True
|
|
||||||
|
|
||||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
|
||||||
#html_show_copyright = True
|
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
|
||||||
# base URL from which the finished HTML is served.
|
|
||||||
#html_use_opensearch = ''
|
|
||||||
|
|
||||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
|
||||||
#html_file_suffix = None
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = '%sdoc' % project
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output -------------------------------------------------
|
|
||||||
|
|
||||||
latex_elements = {
|
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
|
||||||
#'papersize': 'letterpaper',
|
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
|
||||||
#'pointsize': '10pt',
|
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
#'preamble': '',
|
|
||||||
}
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual])
|
|
||||||
latex_documents = [
|
|
||||||
(
|
|
||||||
'index',
|
|
||||||
'%s.tex' % project,
|
|
||||||
u'%s Documentation' % project,
|
|
||||||
u'OpenStack Foundation',
|
|
||||||
'manual'
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
|
||||||
# the title page.
|
|
||||||
#latex_logo = None
|
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
|
||||||
# not chapters.
|
|
||||||
#latex_use_parts = False
|
|
||||||
|
|
||||||
# If true, show page references after internal links.
|
|
||||||
#latex_show_pagerefs = False
|
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
|
||||||
#latex_show_urls = False
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
#latex_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#latex_domain_indices = True
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output -------------------------------------------
|
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
|
||||||
# (source start file, name, description, authors, manual section).
|
|
||||||
# man_pages = [
|
|
||||||
# (
|
|
||||||
# 'index',
|
|
||||||
# '%s' % project,
|
|
||||||
# u'%s Documentation' % project,
|
|
||||||
# u'OpenStack Foundation',
|
|
||||||
# 1
|
|
||||||
# ),
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
|
||||||
#man_show_urls = False
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output -----------------------------------------------
|
|
||||||
|
|
||||||
# Grouping the document tree into Texinfo files. List of tuples
|
|
||||||
# (source start file, target name, title, author,
|
|
||||||
# dir menu entry, description, category)
|
|
||||||
texinfo_documents = [
|
|
||||||
(
|
|
||||||
'index',
|
|
||||||
'%s' % project,
|
|
||||||
u'%s Documentation' % project,
|
|
||||||
u'OpenStack Foundation',
|
|
||||||
'%s' % project,
|
|
||||||
'Database as a service.',
|
|
||||||
'Miscellaneous'
|
|
||||||
'manual'
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
#texinfo_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#texinfo_domain_indices = True
|
|
||||||
|
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
|
||||||
#texinfo_show_urls = 'footnote'
|
|
||||||
|
|
||||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
|
||||||
#texinfo_no_detailmenu = False
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Epub output --------------------------------------------------
|
|
||||||
|
|
||||||
# Bibliographic Dublin Core info.
|
|
||||||
epub_title = u'%s' % project
|
|
||||||
epub_author = u'OpenStack Foundation'
|
|
||||||
epub_publisher = u'OpenStack Foundation'
|
|
||||||
epub_copyright = u'2013, OpenStack Foundation'
|
|
||||||
|
|
||||||
# The language of the text. It defaults to the language option
|
|
||||||
# or en if the language is not set.
|
|
||||||
#epub_language = ''
|
|
||||||
|
|
||||||
# The scheme of the identifier. Typical schemes are ISBN or URL.
|
|
||||||
#epub_scheme = ''
|
|
||||||
|
|
||||||
# The unique identifier of the text. This can be a ISBN number
|
|
||||||
# or the project homepage.
|
|
||||||
#epub_identifier = ''
|
|
||||||
|
|
||||||
# A unique identification for the text.
|
|
||||||
#epub_uid = ''
|
|
||||||
|
|
||||||
# A tuple containing the cover image and cover page html template filenames.
|
|
||||||
#epub_cover = ()
|
|
||||||
|
|
||||||
# A sequence of (type, uri, title) tuples for the guide element of content.opf.
|
|
||||||
#epub_guide = ()
|
|
||||||
|
|
||||||
# HTML files that should be inserted before the pages created by sphinx.
|
|
||||||
# The format is a list of tuples containing the path and title.
|
|
||||||
#epub_pre_files = []
|
|
||||||
|
|
||||||
# HTML files shat should be inserted after the pages created by sphinx.
|
|
||||||
# The format is a list of tuples containing the path and title.
|
|
||||||
#epub_post_files = []
|
|
||||||
|
|
||||||
# A list of files that should not be packed into the epub file.
|
|
||||||
#epub_exclude_files = []
|
|
||||||
|
|
||||||
# The depth of the table of contents in toc.ncx.
|
|
||||||
#epub_tocdepth = 3
|
|
||||||
|
|
||||||
# Allow duplicate toc entries.
|
|
||||||
#epub_tocdup = True
|
|
||||||
|
|
||||||
# Fix unsupported image types using the PIL.
|
|
||||||
#epub_fix_images = False
|
|
||||||
|
|
||||||
# Scale large images.
|
|
||||||
#epub_max_image_width = 0
|
|
||||||
|
|
||||||
# If 'no', URL addresses will not be shown.
|
|
||||||
#epub_show_urls = 'inline'
|
|
||||||
|
|
||||||
# If false, no index is generated.
|
|
||||||
#epub_use_index = True
|
|
@ -1,4 +0,0 @@
|
|||||||
====================
|
|
||||||
Cloudvalidation tool
|
|
||||||
====================
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
[DEFAULT]
|
|
||||||
health_check_config_path = /etc/cloudv-ostf-adapter/test.conf
|
|
||||||
|
|
||||||
[rest]
|
|
||||||
server_host=127.0.0.1
|
|
||||||
server_port=8777
|
|
||||||
log_file=/var/log/ostf.log
|
|
||||||
debug=False
|
|
||||||
jobs_dir=/var/log/ostf
|
|
||||||
|
|
||||||
[sanity]
|
|
||||||
|
|
||||||
[smoke]
|
|
||||||
|
|
||||||
[platform]
|
|
||||||
|
|
||||||
[high_availability]
|
|
@ -1,41 +0,0 @@
|
|||||||
[identity]
|
|
||||||
disable_ssl_certificate_validatio = True
|
|
||||||
uri = http://172.18.196.219:5000/v2.0/
|
|
||||||
url = http://172.18.196.219:5000/v2.0/
|
|
||||||
ubuntu_url = http://172.18.196.219:5000/v2.0/
|
|
||||||
strategy = keystone
|
|
||||||
admin_username = admin
|
|
||||||
admin_tenant_name = admin
|
|
||||||
admin_password = 3de4922d8b6ac5a1aad9
|
|
||||||
|
|
||||||
[compute]
|
|
||||||
flavor_ref = 2
|
|
||||||
compute_nodes = 172.18.196.219
|
|
||||||
online_computes = 172.18.196.219
|
|
||||||
deployment_os = Ubuntu
|
|
||||||
libvirt_type = qemu
|
|
||||||
image_name = mysql
|
|
||||||
online_controllers = 172.18.196.219
|
|
||||||
controller_node_ssh_key_path = /home/ubuntu/.ssh/id_rsa
|
|
||||||
controller_node_ssh_user = ubuntu
|
|
||||||
ssh_timeout = 700
|
|
||||||
controller_node_ssh_password = ''
|
|
||||||
image_ssh_user = ubuntu
|
|
||||||
image_alt_ssh_user = ubuntu
|
|
||||||
ssh_user = ubuntu
|
|
||||||
path_to_private_key = /home/ubuntu/.ssh/id_rsa
|
|
||||||
|
|
||||||
[network]
|
|
||||||
# tenant_network_cidr = ?
|
|
||||||
|
|
||||||
[volume]
|
|
||||||
backend1_name = lvmdriver-1
|
|
||||||
backend2_name = lvmdriver-1
|
|
||||||
|
|
||||||
[heat]
|
|
||||||
endpoint = http://172.18.196.219:8004/v1
|
|
||||||
|
|
||||||
[murano]
|
|
||||||
# Please note that this URLs are fake, the are needed to instantiate unnecessary/redundant muranoclient
|
|
||||||
api_url = http://172.18.196.219:8082/v1
|
|
||||||
api_url_management = http://172.18.196.219:8082/v1
|
|
@ -1,171 +0,0 @@
|
|||||||
[identity]
|
|
||||||
# This section contains configuration options that a variety of
|
|
||||||
# test clients use when authenticating with different user/tenant
|
|
||||||
# combinations
|
|
||||||
url = http://localhost/
|
|
||||||
# The type of endpoint for a Identity service. Unless you have a
|
|
||||||
# custom Keystone service catalog implementation, you probably want to leave
|
|
||||||
# this value as "identity"
|
|
||||||
catalog_type = identity
|
|
||||||
# Ignore SSL certificate validation failures? Use when in testing
|
|
||||||
# environments that have self-signed SSL certs.
|
|
||||||
disable_ssl_certificate_validation = False
|
|
||||||
# URL for where to find the OpenStack Identity API endpoint (Keystone)
|
|
||||||
uri = http://localhost:5000/v2.0/
|
|
||||||
# URL for where to find the OpenStack V3 Identity API endpoint (Keystone)
|
|
||||||
#uri_v3 = http://127.0.0.1:5000/v3/
|
|
||||||
# Should typically be left as keystone unless you have a non-Keystone
|
|
||||||
# authentication API service
|
|
||||||
strategy = keystone
|
|
||||||
# The identity region
|
|
||||||
region = RegionOne
|
|
||||||
|
|
||||||
# This should be the username of a user WITH administrative privileges
|
|
||||||
admin_username = nova
|
|
||||||
# The above administrative user's password
|
|
||||||
admin_password = nova
|
|
||||||
# The above administrative user's tenant name
|
|
||||||
admin_tenant_name = service
|
|
||||||
|
|
||||||
[compute]
|
|
||||||
# This section contains configuration options used when executing tests
|
|
||||||
# against the OpenStack Compute API.
|
|
||||||
|
|
||||||
#One of the controller nodes
|
|
||||||
controller_nodes = localhost
|
|
||||||
controller_nodes_name = controller
|
|
||||||
|
|
||||||
#Controller node user who able connect via ssh
|
|
||||||
controller_node_ssh_user = root
|
|
||||||
|
|
||||||
#Controller node ssh user's password
|
|
||||||
controller_node_ssh_password = r00tme
|
|
||||||
controller_node_ssh_key_path = /root/.ssh/id_rsa
|
|
||||||
|
|
||||||
#The list of the services should be enabled
|
|
||||||
enabled_services=nova-cert, nova-consoleauth, nova-scheduler, nova-conductor, nova-compute, nova-network, nova-compute, nova-network
|
|
||||||
|
|
||||||
# Allows test cases to create/destroy tenants and users. This option
|
|
||||||
# enables isolated test cases and better parallel execution,
|
|
||||||
# but also requires that OpenStack Identity API admin credentials
|
|
||||||
# are known.
|
|
||||||
allow_tenant_isolation = True
|
|
||||||
|
|
||||||
# Allows test cases to create/destroy tenants and users. This option
|
|
||||||
# enables isolated test cases and better parallel execution,
|
|
||||||
# but also requires that OpenStack Identity API admin credentials
|
|
||||||
# are known.
|
|
||||||
allow_tenant_reuse = true
|
|
||||||
|
|
||||||
# Reference data for tests. The ref and ref_alt should be
|
|
||||||
# distinct images/flavors.
|
|
||||||
image_name = TestVM
|
|
||||||
flavor_ref = 1
|
|
||||||
|
|
||||||
# User names used to authenticate to an instance for a given image.
|
|
||||||
image_ssh_user = cirros
|
|
||||||
image_alt_ssh_user = cirros
|
|
||||||
|
|
||||||
# Number of seconds to wait while looping to check the status of an
|
|
||||||
# instance that is building.
|
|
||||||
build_interval = 3
|
|
||||||
|
|
||||||
# Number of seconds to time out on waiting for an instance
|
|
||||||
# to build or reach an expected status
|
|
||||||
build_timeout = 300
|
|
||||||
|
|
||||||
# Run additional tests that use SSH for instance validation?
|
|
||||||
# This requires the instances be routable from the host
|
|
||||||
# executing the tests
|
|
||||||
run_ssh = false
|
|
||||||
|
|
||||||
# Number of seconds to wait to authenticate to an instance
|
|
||||||
ssh_timeout = 300
|
|
||||||
|
|
||||||
# Number of seconds to wait for output from ssh channel
|
|
||||||
ssh_channel_timeout = 60
|
|
||||||
|
|
||||||
# The type of endpoint for a Compute API service. Unless you have a
|
|
||||||
# custom Keystone service catalog implementation, you probably want to leave
|
|
||||||
# this value as "compute"
|
|
||||||
catalog_type = compute
|
|
||||||
|
|
||||||
# Does the Compute API support creation of images?
|
|
||||||
create_image_enabled = true
|
|
||||||
|
|
||||||
[image]
|
|
||||||
# This section contains configuration options used when executing tests
|
|
||||||
# against the OpenStack Images API
|
|
||||||
|
|
||||||
# The type of endpoint for an Image API service. Unless you have a
|
|
||||||
# custom Keystone service catalog implementation, you probably want to leave
|
|
||||||
# this value as "image"
|
|
||||||
catalog_type = image
|
|
||||||
|
|
||||||
# The version of the OpenStack Images API to use
|
|
||||||
api_version = 1
|
|
||||||
|
|
||||||
# HTTP image to use for glance http image testing
|
|
||||||
http_image = http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz
|
|
||||||
|
|
||||||
[network]
|
|
||||||
# This section contains configuration options used when executing tests
|
|
||||||
# against the OpenStack Network API.
|
|
||||||
|
|
||||||
# Version of the Quantum API
|
|
||||||
api_version = 2.0
|
|
||||||
# Catalog type of the Quantum Service
|
|
||||||
catalog_type = network
|
|
||||||
|
|
||||||
# A large private cidr block from which to allocate smaller blocks for
|
|
||||||
# tenant networks.
|
|
||||||
tenant_network_cidr = 10.13.0.0/16
|
|
||||||
|
|
||||||
# The mask bits used to partition the tenant block.
|
|
||||||
tenant_network_mask_bits = 28
|
|
||||||
|
|
||||||
# If tenant networks are reachable, connectivity checks will be
|
|
||||||
# performed directly against addresses on those networks.
|
|
||||||
tenant_networks_reachable = true
|
|
||||||
|
|
||||||
# Whether or not quantum is expected to be available
|
|
||||||
quantum_available = false
|
|
||||||
|
|
||||||
[volume]
|
|
||||||
# This section contains the configuration options used when executing tests
|
|
||||||
# against the OpenStack Block Storage API service
|
|
||||||
|
|
||||||
# The type of endpoint for a Cinder or Block Storage API service.
|
|
||||||
# Unless you have a custom Keystone service catalog implementation, you
|
|
||||||
# probably want to leave this value as "volume"
|
|
||||||
catalog_type = volume
|
|
||||||
# Number of seconds to wait while looping to check the status of a
|
|
||||||
# volume that is being made available
|
|
||||||
build_interval = 3
|
|
||||||
# Number of seconds to time out on waiting for a volume
|
|
||||||
# to be available or reach an expected status
|
|
||||||
build_timeout = 300
|
|
||||||
# Runs Cinder multi-backend tests (requires 2 backends declared in cinder.conf)
|
|
||||||
# They must have different volume_backend_name (backend1_name and backend2_name
|
|
||||||
# have to be different)
|
|
||||||
multi_backend_enabled = false
|
|
||||||
backend1_name = BACKEND_1
|
|
||||||
backend2_name = BACKEND_2
|
|
||||||
|
|
||||||
[object-storage]
|
|
||||||
# This section contains configuration options used when executing tests
|
|
||||||
# against the OpenStack Object Storage API.
|
|
||||||
|
|
||||||
# You can configure the credentials in the compute section
|
|
||||||
|
|
||||||
# The type of endpoint for an Object Storage API service. Unless you have a
|
|
||||||
# custom Keystone service catalog implementation, you probably want to leave
|
|
||||||
# this value as "object-store"
|
|
||||||
catalog_type = object-store
|
|
||||||
|
|
||||||
# Number of seconds to time on waiting for a container to container
|
|
||||||
# synchronization complete
|
|
||||||
container_sync_timeout = 120
|
|
||||||
# Number of seconds to wait while looping to check the status of a
|
|
||||||
# container to container synchronization
|
|
||||||
container_sync_interval = 5
|
|
@ -1,16 +0,0 @@
|
|||||||
# The order of packages is significant, because pip processes them in the order
|
|
||||||
# of appearance. Changing the order has an impact on the overall integration
|
|
||||||
# process, which may cause wedges in the gate later.
|
|
||||||
|
|
||||||
flask
|
|
||||||
flask-restful
|
|
||||||
nose
|
|
||||||
oslo.config>=1.6.0 # Apache-2.0
|
|
||||||
pbr>=0.6,!=0.7,<1.0
|
|
||||||
oslo.utils
|
|
||||||
PrettyTable>=0.7,<0.8
|
|
||||||
requests>=2.2.0,!=2.4.0
|
|
||||||
simplejson>=2.2.0
|
|
||||||
|
|
||||||
#TODO(???): move this fix into fuel-ostf
|
|
||||||
python-muranoclient
|
|
50
setup.cfg
50
setup.cfg
@ -1,50 +0,0 @@
|
|||||||
[metadata]
|
|
||||||
name = cloudv-ostf-adapter
|
|
||||||
version = 2015.1
|
|
||||||
summary = CloudValidation OSTF adapter
|
|
||||||
description-file =
|
|
||||||
README.rst
|
|
||||||
author = OpenStack
|
|
||||||
author-email = openstack-dev@lists.openstack.org
|
|
||||||
classifier =
|
|
||||||
Environment :: OpenStack
|
|
||||||
Intended Audience :: Information Technology
|
|
||||||
Intended Audience :: System Administrators
|
|
||||||
License :: OSI Approved :: Apache Software License
|
|
||||||
Operating System :: POSIX :: Linux
|
|
||||||
Programming Language :: Python
|
|
||||||
Programming Language :: Python :: 2
|
|
||||||
Programming Language :: Python :: 2.7
|
|
||||||
|
|
||||||
[files]
|
|
||||||
packages =
|
|
||||||
cloudv_ostf_adapter
|
|
||||||
cloudv_client
|
|
||||||
|
|
||||||
[compile_catalog]
|
|
||||||
domain = cloudv_ostf_adapter
|
|
||||||
|
|
||||||
[entry_points]
|
|
||||||
console_scripts =
|
|
||||||
cloudvalidation-cli = cloudv_ostf_adapter.cmd.cli:main
|
|
||||||
cloudvalidation-server = cloudv_ostf_adapter.cmd.server:main
|
|
||||||
cloudvalidation = cloudv_ostf_adapter.cmd.client:main
|
|
||||||
|
|
||||||
[global]
|
|
||||||
setup-hooks =
|
|
||||||
pbr.hooks.setup_hook
|
|
||||||
|
|
||||||
[build_sphinx]
|
|
||||||
all_files = 1
|
|
||||||
build-dir = doc/build
|
|
||||||
source-dir = doc/source
|
|
||||||
|
|
||||||
[nosetests]
|
|
||||||
match=^test
|
|
||||||
where=cloudv_ostf_adapter
|
|
||||||
nocapture=1
|
|
||||||
cover-package=cloudv_ostf_adapter
|
|
||||||
cover-erase=1
|
|
||||||
|
|
||||||
[wheel]
|
|
||||||
universal = 1
|
|
31
setup.py
31
setup.py
@ -1,31 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
|
||||||
import setuptools
|
|
||||||
|
|
||||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
|
||||||
# setuptools if some other modules registered functions in `atexit`.
|
|
||||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
|
||||||
try:
|
|
||||||
import multiprocessing # noqa
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
setuptools.setup(
|
|
||||||
setup_requires=['pbr'],
|
|
||||||
pbr=True
|
|
||||||
)
|
|
@ -1,11 +0,0 @@
|
|||||||
# The order of packages is significant, because pip processes them in the order
|
|
||||||
# of appearance. Changing the order has an impact on the overall integration
|
|
||||||
# process, which may cause wedges in the gate later.
|
|
||||||
|
|
||||||
# Hacking already pins down pep8, pyflakes and flake8
|
|
||||||
hacking>=0.8.0,<0.9
|
|
||||||
mock>=1.0
|
|
||||||
testtools>=0.9.36,!=1.2.0
|
|
||||||
testrepository>=0.0.18
|
|
||||||
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
|
|
||||||
oslosphinx>=2.2.0 # Apache-2.0
|
|
33
tox.ini
33
tox.ini
@ -1,33 +0,0 @@
|
|||||||
[tox]
|
|
||||||
envlist = py27
|
|
||||||
minversion = 1.6
|
|
||||||
skipsdist = True
|
|
||||||
|
|
||||||
[tox:jenkins]
|
|
||||||
sitepackages = True
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
setenv = VIRTUAL_ENV={envdir}
|
|
||||||
usedevelop = True
|
|
||||||
install_command = pip install -U {opts} {packages}
|
|
||||||
deps =
|
|
||||||
-r{toxinidir}/requirements.txt
|
|
||||||
-r{toxinidir}/test-requirements.txt
|
|
||||||
# https://github.com/stackforge/fuel-ostf/archive/6.0.tar.gz#egg=fuel-ostf==6.0
|
|
||||||
commands =
|
|
||||||
python setup.py testr --slowest
|
|
||||||
whitelist_externals = bash
|
|
||||||
|
|
||||||
[testenv:pep8]
|
|
||||||
commands =
|
|
||||||
flake8
|
|
||||||
|
|
||||||
[testenv:venv]
|
|
||||||
commands = {posargs}
|
|
||||||
|
|
||||||
[flake8]
|
|
||||||
show-source = True
|
|
||||||
ignore = F821,H301,H306,H404
|
|
||||||
builtins = _
|
|
||||||
exclude=.venv,.tox,dist,doc,openstack,*egg,rsdns,tools,etc,build,*.po,*.pot
|
|
||||||
filename=*.py
|
|
Loading…
x
Reference in New Issue
Block a user