Port from Python2 to Python3

node_check fails to evaluate down hypervisors
Switch to stestr

Change-Id: I99bdc541f28781665b751b05de5605a54f766497
This commit is contained in:
Sawan Choudhary 2020-05-26 20:36:55 -07:00
parent 0a2caaa3fe
commit 2c8a5af1b5
27 changed files with 100 additions and 105 deletions

2
.gitignore vendored
View File

@ -25,7 +25,7 @@ pip-log.txt
.coverage .coverage
.tox .tox
nosetests.xml nosetests.xml
.testrepository .stestr/
.venv .venv
# Translations # Translations

3
.stestr.conf Normal file
View File

@ -0,0 +1,3 @@
[DEFAULT]
test_path=${OS_TEST_PATH:-./cloudpulse/tests/unit}
top_dir=./

View File

@ -2,6 +2,8 @@
check: check:
jobs: jobs:
- openstack-tox-pep8 - openstack-tox-pep8
- openstack-tox-py36
gate: gate:
jobs: jobs:
- openstack-tox-pep8 - openstack-tox-pep8
- openstack-tox-py36

View File

@ -100,7 +100,7 @@ class Periodic_TestManager(os_service.Service):
def start(self): def start(self):
tasks = CONF.periodic_tests tasks = CONF.periodic_tests
for key in tasks.keys(): for key in list(tasks.keys()):
interval, task_name = tasks[key], key interval, task_name = tasks[key], key
if int(interval) > 0: if int(interval) > 0:
period_task = Periodic_Task(task_name) period_task = Periodic_Task(task_name)

View File

@ -37,7 +37,10 @@ def validate_limit(limit):
if limit is not None and limit <= 0: if limit is not None and limit <= 0:
raise wsme.exc.ClientSideError(_("Limit must be positive")) raise wsme.exc.ClientSideError(_("Limit must be positive"))
return min(CONF.api.max_limit, limit) or CONF.api.max_limit if limit is not None:
return min(CONF.api.max_limit, limit)
else:
return CONF.api.max_limit
def validate_sort_dir(sort_dir): def validate_sort_dir(sort_dir):

View File

@ -49,8 +49,8 @@ class AuthTokenMiddleware(auth_token.AuthProtocol):
# The information whether the API call is being performed against the # The information whether the API call is being performed against the
# public API is required for some other components. Saving it to the # public API is required for some other components. Saving it to the
# WSGI environment is reasonable thereby. # WSGI environment is reasonable thereby.
env['is_public_api'] = any(map(lambda pattern: re.match(pattern, path), env['is_public_api'] = any(
self.public_api_routes)) [re.match(pattern, path) for pattern in self.public_api_routes])
if env['is_public_api']: if env['is_public_api']:
return self._app(env, start_response) return self._app(env, start_response)

View File

@ -18,9 +18,7 @@ response with one formatted so the client can parse it.
Based on pecan.middleware.errordocument Based on pecan.middleware.errordocument
""" """
import json from oslo_serialization import jsonutils
# from xml import etree as et
from cloudpulse.openstack.common._i18n import _ from cloudpulse.openstack.common._i18n import _
@ -61,7 +59,8 @@ class ParsableErrorMiddleware(object):
app_iter = self.app(environ, replacement_start_response) app_iter = self.app(environ, replacement_start_response)
if (state['status_code'] // 100) not in (2, 3): if (state['status_code'] // 100) not in (2, 3):
body = [json.dumps({'error_message': '\n'.join(app_iter)})] body = [jsonutils.dump_as_bytes(
{'error_message': b'\n'.join(app_iter)})]
state['headers'].append(('Content-Type', 'application/json')) state['headers'].append(('Content-Type', 'application/json'))
state['headers'].append(('Content-Length', str(len(body[0])))) state['headers'].append(('Content-Length', str(len(body[0]))))
else: else:

View File

@ -200,7 +200,7 @@ class CloudpulseException(Exception):
# kwargs doesn't match a variable in the message # kwargs doesn't match a variable in the message
# log the issue and the kwargs # log the issue and the kwargs
LOG.exception(_LE('Exception in string format operation')) LOG.exception(_LE('Exception in string format operation'))
for name, value in kwargs.iteritems(): for name, value in kwargs.items():
LOG.error(_LE("%(name)s: %(value)s") % LOG.error(_LE("%(name)s: %(value)s") %
{'name': name, 'value': value}) {'name': name, 'value': value})
try: try:

View File

@ -38,13 +38,13 @@ def getcallargs(function, *args, **kwargs):
if 'self' in argnames[0] or 'cls' == argnames[0]: if 'self' in argnames[0] or 'cls' == argnames[0]:
# The function may not actually be a method or have im_self. # The function may not actually be a method or have im_self.
# Typically seen when it's stubbed with mox. # Typically seen when it's stubbed with mox.
if inspect.ismethod(function) and hasattr(function, 'im_self'): if inspect.ismethod(function) and hasattr(function, '__self__'):
keyed_args[argnames[0]] = function.im_self keyed_args[argnames[0]] = function.__self__
else: else:
keyed_args[argnames[0]] = None keyed_args[argnames[0]] = None
remaining_argnames = filter(lambda x: x not in keyed_args, argnames) remaining_argnames = [x for x in argnames if x not in keyed_args]
keyed_args.update(dict(zip(remaining_argnames, args))) keyed_args.update(dict(list(zip(remaining_argnames, args))))
if defaults: if defaults:
num_defaults = len(defaults) num_defaults = len(defaults)

View File

@ -65,7 +65,7 @@ testthreads = []
def delete_old_entries(): def delete_old_entries():
tasks = CONF.periodic_tests tasks = CONF.periodic_tests
num_tests = CONF.database.max_db_entries num_tests = CONF.database.max_db_entries
num_range = len([key for key in tasks.keys() if int(tasks[key]) > 0]) num_range = len([key for key in list(tasks.keys()) if int(tasks[key]) > 0])
conn = dbapi.get_backend() conn = dbapi.get_backend()
conn.delete_old_tests(num_range, num_tests) conn.delete_old_tests(num_range, num_tests)

View File

@ -26,7 +26,7 @@ import pecan
import random import random
import re import re
import shutil import shutil
import sys import six
import tempfile import tempfile
import uuid import uuid
@ -36,11 +36,6 @@ from oslo_config import cfg
from oslo_utils import excutils from oslo_utils import excutils
import paramiko import paramiko
if sys.version_info.major == 3:
from past.builtins import basestring
import six
from cloudpulse.common import exception from cloudpulse.common import exception
from cloudpulse.openstack.common._i18n import _ from cloudpulse.openstack.common._i18n import _
from cloudpulse.openstack.common._i18n import _LE from cloudpulse.openstack.common._i18n import _LE
@ -376,14 +371,14 @@ def temporary_mutation(obj, **kwargs):
NOT_PRESENT = object() NOT_PRESENT = object()
old_values = {} old_values = {}
for attr, new_value in kwargs.items(): for attr, new_value in list(kwargs.items()):
old_values[attr] = get(obj, attr, NOT_PRESENT) old_values[attr] = get(obj, attr, NOT_PRESENT)
set_value(obj, attr, new_value) set_value(obj, attr, new_value)
try: try:
yield yield
finally: finally:
for attr, old_value in old_values.items(): for attr, old_value in list(old_values.items()):
if old_value is NOT_PRESENT: if old_value is NOT_PRESENT:
delete(obj, attr) delete(obj, attr)
else: else:
@ -582,7 +577,7 @@ def allow_logical_names():
def raise_exception_invalid_scheme(url): def raise_exception_invalid_scheme(url):
valid_schemes = ['http', 'https'] valid_schemes = ['http', 'https']
if not isinstance(url, basestring): if not isinstance(url, str):
raise exception.Urllib2InvalidScheme(url=url) raise exception.Urllib2InvalidScheme(url=url)
scheme = url.split(':')[0] scheme = url.split(':')[0]

View File

@ -16,9 +16,6 @@ from cloudpulse import objects
import contextlib import contextlib
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils from oslo_utils import excutils
import sys
if sys.version_info.major == 3:
from past.builtins import xrange
import time import time
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -31,7 +28,7 @@ class CpulseLock(object):
self.conductor_id = conductor_id self.conductor_id = conductor_id
def acquire(self, retry=True, times=10): def acquire(self, retry=True, times=10):
for num in xrange(0, times): for num in range(0, times):
lock_id = objects.CpulseLock.create(self.cpulse_test.name, lock_id = objects.CpulseLock.create(self.cpulse_test.name,
self.conductor_id) self.conductor_id)
if lock_id is None: if lock_id is None:

View File

@ -16,7 +16,7 @@
# W0621: Redefining name %s from outer scope # W0621: Redefining name %s from outer scope
# pylint: disable=W0603,W0621 # pylint: disable=W0603,W0621
from __future__ import print_function
import getpass import getpass
import inspect import inspect

View File

@ -14,7 +14,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from __future__ import print_function
import copy import copy
import errno import errno
@ -80,7 +80,7 @@ def _print_greenthreads():
def _print_nativethreads(): def _print_nativethreads():
for threadId, stack in sys._current_frames().items(): for threadId, stack in list(sys._current_frames().items()):
print(threadId) print(threadId)
traceback.print_stack(stack) traceback.print_stack(stack)
print() print()

View File

@ -457,7 +457,7 @@ def _find_facility_from_conf():
facility = facility_names.get(CONF.syslog_log_facility) facility = facility_names.get(CONF.syslog_log_facility)
if facility is None: if facility is None:
valid_facilities = facility_names.keys() valid_facilities = list(facility_names.keys())
consts = ['LOG_AUTH', 'LOG_AUTHPRIV', 'LOG_CRON', 'LOG_DAEMON', consts = ['LOG_AUTH', 'LOG_AUTHPRIV', 'LOG_CRON', 'LOG_DAEMON',
'LOG_FTP', 'LOG_KERN', 'LOG_LPR', 'LOG_MAIL', 'LOG_NEWS', 'LOG_FTP', 'LOG_KERN', 'LOG_LPR', 'LOG_MAIL', 'LOG_NEWS',
'LOG_AUTH', 'LOG_SYSLOG', 'LOG_USER', 'LOG_UUCP', 'LOG_AUTH', 'LOG_SYSLOG', 'LOG_USER', 'LOG_UUCP',
@ -659,7 +659,7 @@ class ContextFormatter(logging.Formatter):
context = getattr(local.store, 'context', None) context = getattr(local.store, 'context', None)
if context: if context:
d = _dictify_context(context) d = _dictify_context(context)
for k, v in d.items(): for k, v in list(d.items()):
setattr(record, k, v) setattr(record, k, v)
# NOTE(sdague): default the fancier formatting params # NOTE(sdague): default the fancier formatting params

View File

@ -156,7 +156,7 @@ class _PeriodicTasksMeta(type):
except AttributeError: except AttributeError:
cls._periodic_spacing = {} cls._periodic_spacing = {}
for value in cls.__dict__.values(): for value in list(cls.__dict__.values()):
if getattr(value, '_periodic_task', False): if getattr(value, '_periodic_task', False):
cls._add_periodic_task(value) cls._add_periodic_task(value)

View File

@ -391,7 +391,7 @@ class ProcessLauncher(object):
cfg.CONF.reload_config_files() cfg.CONF.reload_config_files()
for service in set( for service in set(
[wrap.service for wrap in self.children.values()]): [wrap.service for wrap in list(self.children.values())]):
service.reset() service.reset()
for pid in self.children: for pid in self.children:

View File

@ -12,7 +12,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from hostObj import HostObject from cloudpulse.operator.ansible.hostObj import HostObject
import os import os
import yaml import yaml
@ -39,7 +39,7 @@ class os_cfg_reader(object):
def setOpenstackNodeIp(self): def setOpenstackNodeIp(self):
# print self.hostYamlObj # print self.hostYamlObj
for key in self.hostYamlObj.keys(): for key in list(self.hostYamlObj.keys()):
name = key name = key
ip = self.hostYamlObj[key]["ip"] ip = self.hostYamlObj[key]["ip"]
hostname = key hostname = key
@ -81,8 +81,8 @@ class os_cfg_reader(object):
obj.getPassword()) obj.getPassword())
f.write('\n') f.write('\n')
f.close() f.close()
"""
"""
def update_ansible_playbook(self): def update_ansible_playbook(self):
f = open('testcase-configs/ansible-playbook.yaml') f = open('testcase-configs/ansible-playbook.yaml')
f1 = open('testcase-configs/ansible-playbook_update.yaml', "w") f1 = open('testcase-configs/ansible-playbook_update.yaml', "w")
@ -93,7 +93,7 @@ class os_cfg_reader(object):
f1.write(line) f1.write(line)
f.close() f.close()
f1.close() f1.close()
""" """
if __name__ == '__main__': if __name__ == '__main__':
yhp = os_cfg_reader() yhp = os_cfg_reader()
yhp.setOpenstackNodeIp() yhp.setOpenstackNodeIp()

View File

@ -13,8 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from __future__ import print_function from cloudpulse.operator.ansible.openstack_node import openstack_node_obj
from openstack_node import openstack_node_obj
import yaml import yaml
@ -35,7 +34,7 @@ class openstack_node_info_reader(object):
def get_host_list(self): def get_host_list(self):
openstack_host_list = [] openstack_host_list = []
for key in self.hostYamlObj.keys(): for key in list(self.hostYamlObj.keys()):
name = key name = key
ip = self.hostYamlObj[key]["ip"] ip = self.hostYamlObj[key]["ip"]
hostname = key hostname = key
@ -60,8 +59,8 @@ class openstack_node_info_reader(object):
def get_galera_details(self): def get_galera_details(self):
galera = {} galera = {}
print(self.hostYamlObj) print(self.hostYamlObj)
for key in self.hostYamlObj.keys(): for key in list(self.hostYamlObj.keys()):
if 'galerauser' in self.hostYamlObj[key].keys(): if 'galerauser' in list(self.hostYamlObj[key].keys()):
galera['username'] = self.hostYamlObj[key]['galerauser'] galera['username'] = self.hostYamlObj[key]['galerauser']
galera['password'] = self.hostYamlObj[key]['galerapassword'] galera['password'] = self.hostYamlObj[key]['galerapassword']

View File

@ -10,7 +10,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from __future__ import print_function
from cloudpulse.openstack.api import keystone_session from cloudpulse.openstack.api import keystone_session
from cloudpulse.openstack.api.nova_api import NovaHealth from cloudpulse.openstack.api.nova_api import NovaHealth
from cloudpulse.operator.ansible.openstack_node_info_reader import \ from cloudpulse.operator.ansible.openstack_node_info_reader import \
@ -272,7 +272,7 @@ class operator_scenario(base.Scenario):
docker_failed = "" docker_failed = ""
res['output'] = res['output'].split('\n') res['output'] = res['output'].split('\n')
output = filter(lambda x: not re.match(r'^\s*$', x), res['output']) output = [x for x in res['output'] if not re.match(r'^\s*$', x)]
for line in output: for line in output:
line = line.split('|') line = line.split('|')
@ -348,7 +348,7 @@ class operator_scenario(base.Scenario):
# Handle ceph status in luminous, result should be picked form # Handle ceph status in luminous, result should be picked form
# 'status' instead of 'overall_status' # 'status' instead of 'overall_status'
if len(ceph_json['health']['summary']) and \ if len(ceph_json['health']['summary']) and \
'summary' in ceph_json['health']['summary'][0].keys() \ 'summary' in list(ceph_json['health']['summary'][0].keys()) \
and 'mon health preluminous compat warning' in \ and 'mon health preluminous compat warning' in \
ceph_json['health']['summary'][0]['summary']: ceph_json['health']['summary'][0]['summary']:
overall_status = ceph_json['health']['status'] overall_status = ceph_json['health']['status']
@ -373,48 +373,44 @@ class operator_scenario(base.Scenario):
@base.scenario(admin_only=False, operator=True) @base.scenario(admin_only=False, operator=True)
def node_check(self): def node_check(self):
failed_hosts = None failed_hosts = []
self.load() self.load()
nodes_from_ansible_config = [node.name.lower() nodes_from_ansible_config = [node.name for node in
for node in
self.os_node_info_obj.get_host_list() self.os_node_info_obj.get_host_list()
if "compute" in node.role.split()] if "compute" in node.role.split()]
nova_hypervisor_list = self._get_nova_hypervior_list() nova_hypervisor_list = self._get_nova_hypervior_list()
if nova_hypervisor_list[0] != 200: if nova_hypervisor_list[0] != 200:
return (404, ("Cannot get hypervisor list from " return (404, ("Cannot get hypervisor list from "
"Nova reason-%sa") % nova_hypervisor_list[1]) "Nova reason-%sa") % nova_hypervisor_list[1])
nodes_from_nova = [node.lower() for node in nova_hypervisor_list[2]] nodes_from_nova = [node for node in nova_hypervisor_list[2]]
extra_nodes_nova = set(
nodes_from_nova) - set(nodes_from_ansible_config)
extra_nodes_ansible = set(
nodes_from_ansible_config) - set(nodes_from_nova)
if extra_nodes_nova:
return (404, ("Hypervisors in nova hypervisor list are more"
" than configured.nova hypervisor list = %s") %
nodes_from_nova)
if extra_nodes_ansible:
return (404, ("Hypervisors in nova hypervisor list are less"
" than configured.nova hypervisor list = %s") %
nodes_from_nova)
anscmd = ("ansible -o all -i '%s' -m ping -u root" % anscmd = ("ansible -o all -i '%s' -m ping -u root" %
','.join(nova_hypervisor_list[2])) ','.join(nodes_from_ansible_config))
res = execute(anscmd) res = execute(anscmd)
res['output'] = res['output'].split('\n') res['output'] = res['output'].split('\n')
output = filter(lambda x: not re.match(r'^\s*$', x), res['output']) output = filter(lambda x: not re.match(r'^\s*$', x), res['output'])
for line in output: for line in output:
if "SUCCESS" not in line: if "SUCCESS" not in line:
failed_hosts = failed_hosts + line.split('|')[0] failed_hosts.append(line.split('|')[0].strip())
if not res['status']: # Check if ansible ping cmd failed with reason other than unreachable
return (200, "All nodes are up.nova hypervisor list = %s" % # nodes
nodes_from_nova) if res['status'] and not failed_hosts:
return (404, "Unable to perform ping test on nodes, ansible cmd: "
"'%s' failed" % anscmd)
# Check if nova also recognizes that passed nodes were up
nova_failed_hosts = [node for node in nodes_from_ansible_config if
node not in nodes_from_nova]
failed_hosts = list(set(nova_failed_hosts + failed_hosts))
if not failed_hosts:
return (200, "All nodes are up. nova hypervisor list = %s" %
', '.join(nodes_from_nova))
else: else:
msg = "Some nodes are not up" msg = ("The following nodes are down: %s. nova hypervisor list = "
if failed_hosts: "%s" % (', '.join(failed_hosts),
msg = ("The following nodes are not up: %s." ', '.join(nodes_from_nova)))
"nova hypervisor list = %s" %
(str(failed_hosts[0]), nodes_from_nova))
return (404, msg) return (404, msg)
@base.scenario(admin_only=False, operator=True) @base.scenario(admin_only=False, operator=True)

View File

@ -88,7 +88,7 @@ class TestCase(base.BaseTestCase):
def config(self, **kw): def config(self, **kw):
"""Override config options for a test.""" """Override config options for a test."""
group = kw.pop('group', None) group = kw.pop('group', None)
for k, v in kw.iteritems(): for k, v in kw.items():
CONF.set_override(k, v, group) CONF.set_override(k, v, group)
def path_get(self, project_file=None): def path_get(self, project_file=None):

View File

@ -12,14 +12,14 @@
import mock import mock
fakeAuthTokenHeaders = {'X-User-Id': u'773a902f022949619b5c2f32cd89d419', fakeAuthTokenHeaders = {'X-User-Id': '773a902f022949619b5c2f32cd89d419',
'X-Roles': u'admin, ResellerAdmin, _member_', 'X-Roles': 'admin, ResellerAdmin, _member_',
'X-Project-Id': u'5588aebbcdc24e17a061595f80574376', 'X-Project-Id': '5588aebbcdc24e17a061595f80574376',
'X-Project-Name': 'test', 'X-Project-Name': 'test',
'X-User-Name': 'test', 'X-User-Name': 'test',
'X-Auth-Token': u'5588aebbcdc24e17a061595f80574376', 'X-Auth-Token': '5588aebbcdc24e17a061595f80574376',
'X-Forwarded-For': u'10.10.10.10, 11.11.11.11', 'X-Forwarded-For': '10.10.10.10, 11.11.11.11',
'X-Service-Catalog': u'{test: 12345}', 'X-Service-Catalog': '{test: 12345}',
'X-Auth-Url': 'fake_auth_url', 'X-Auth-Url': 'fake_auth_url',
'X-Identity-Status': 'Confirmed', 'X-Identity-Status': 'Confirmed',
'X-User-Domain-Name': 'domain', 'X-User-Domain-Name': 'domain',

View File

@ -66,11 +66,11 @@ class TestContextHook(base.TestCase):
class TestNoExceptionTracebackHook(api_base.FunctionalTest): class TestNoExceptionTracebackHook(api_base.FunctionalTest):
TRACE = [u'Traceback (most recent call last):', TRACE = ['Traceback (most recent call last):',
u' File "/opt/stack/cloudpulse/cloudpulse/openstack', ' File "/opt/stack/cloudpulse/cloudpulse/openstack',
u'/common/rpc/amqp.py",', '/common/rpc/amqp.py",',
' line 434, in _process_data\\n **args)', ' line 434, in _process_data\\n **args)',
u' File "/opt/stack/cloudpulse/cloudpulse/openstack/common/rpc/' ' File "/opt/stack/cloudpulse/cloudpulse/openstack/common/rpc/'
'dispatcher.py", line 172, in dispatch\\n result =' 'dispatcher.py", line 172, in dispatch\\n result ='
' getattr(proxyobj, method)(context, **kwargs)'] ' getattr(proxyobj, method)(context, **kwargs)']
MSG_WITHOUT_TRACE = "Test exception message." MSG_WITHOUT_TRACE = "Test exception message."
@ -103,7 +103,7 @@ class TestNoExceptionTracebackHook(api_base.FunctionalTest):
# rare thing (happens due to wrong deserialization settings etc.) # rare thing (happens due to wrong deserialization settings etc.)
# we don't care about this garbage. # we don't care about this garbage.
expected_msg = ("Remote error: %s %s" expected_msg = ("Remote error: %s %s"
% (test_exc_type, self.MSG_WITHOUT_TRACE) + "\n[u'") % (test_exc_type, self.MSG_WITHOUT_TRACE) + "\n['")
actual_msg = json.loads(response.json['error_message'])['faultstring'] actual_msg = json.loads(response.json['error_message'])['faultstring']
self.assertEqual(expected_msg, actual_msg) self.assertEqual(expected_msg, actual_msg)

View File

@ -37,8 +37,8 @@ source_suffix = '.rst'
master_doc = 'index' master_doc = 'index'
# General information about the project. # General information about the project.
project = u'cloudpulse' project = 'cloudpulse'
copyright = u'2013, OpenStack Foundation' copyright = '2013, OpenStack Foundation'
# If true, '()' will be appended to :func: etc. cross-reference text. # If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True add_function_parentheses = True
@ -67,8 +67,8 @@ htmlhelp_basename = '%sdoc' % project
latex_documents = [ latex_documents = [
('index', ('index',
'%s.tex' % project, '%s.tex' % project,
u'%s Documentation' % project, '%s Documentation' % project,
u'OpenStack Foundation', 'manual'), 'OpenStack Foundation', 'manual'),
] ]
# Example configuration for intersphinx: refer to the Python standard library. # Example configuration for intersphinx: refer to the Python standard library.

View File

@ -7,20 +7,19 @@ author = OpenStack
author-email = openstack-dev@lists.openstack.org author-email = openstack-dev@lists.openstack.org
home-page = https://git.openstack.org/cgit/openstack/cloudpulse home-page = https://git.openstack.org/cgit/openstack/cloudpulse
classifier = classifier =
Environment :: OpenStack Environment :: OpenStack
Intended Audience :: Information Technology Intended Audience :: Information Technology
Intended Audience :: System Administrators Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux Operating System :: POSIX :: Linux
Programming Language :: Python Programming Language :: Python
Programming Language :: Python :: 2 Programming Language :: Python :: 3
Programming Language :: Python :: 2.7 Programming Language :: Python :: 3.6
Programming Language :: Python :: 3 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.3
[files] [files]
packages = packages =
cloudpulse cloudpulse
[build_sphinx] [build_sphinx]
source-dir = doc/source source-dir = doc/source
@ -29,7 +28,7 @@ all_files = 1
[entry_points] [entry_points]
console_scripts = console_scripts =
cloudpulse-server = cloudpulse.cmd.api:main cloudpulse-server = cloudpulse.cmd.api:main
oslo.config.opts = oslo.config.opts =
cloudpulse = cloudpulse.opts:list_opts cloudpulse = cloudpulse.opts:list_opts

View File

@ -11,6 +11,6 @@ python-subunit>=0.0.18
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
oslosphinx>=2.5.0 # Apache-2.0 oslosphinx>=2.5.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0
testrepository>=0.0.18 stestr>=2.0.0
testscenarios>=0.4 testscenarios>=0.4
testtools>=1.4.0 testtools>=1.4.0

View File

@ -1,6 +1,6 @@
[tox] [tox]
minversion = 2.0 minversion = 3.1.1
envlist = pep8 envlist = py36,pep8
skipsdist = True skipsdist = True
[testenv] [testenv]
@ -16,11 +16,13 @@ deps =
-r{toxinidir}/requirements.txt -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
passenv = TEMPEST_* OS_TEST_* passenv = TEMPEST_* OS_TEST_*
whitelist_externals = find, stestr
commands = commands =
find . -type f -name "*.py[c|o]" -delete find . -type f -name "*.py[c|o]" -delete
stestr run {posargs} stestr run {posargs}
[testenv:pep8] [testenv:pep8]
whitelist_externals = bash
commands = commands =
bash tools/flake8wrap.sh {posargs} bash tools/flake8wrap.sh {posargs}