Add support for using Falcon 2.0.0

Falcon 2.0.0 introduces some breaking changes. The relevant ones here are:

- falcon.testing.TestCase.api property was removed
- falcon.testing.TestBase class was removed

Additionally, the default behaviour for handling trailing slashes on
URIs also changed:

https://falcon.readthedocs.io/en/latest/user/faq.html#how-does-falcon-
handle-a-trailing-slash-in-the-request-path

This commit adds support for using the new release. It currently makes
no effort to be backwards compatible with older releases.

The change also updates the requirements for influxdb and sphinx
libraries to match global requirements.

Until monasca-log-api implementation is not updated to support the new
version of Falcon, `monascalog-python3-tempest` is marked to be
non-voting as agreed in the team meeting.

Story: 2005695
Task: 31015
Change-Id: I03bc8d502a333a7a71d9c12b8ddc7c5dc0a4f588
This commit is contained in:
Doug Szumski 2019-05-13 17:54:26 +01:00 committed by Witek Bedyk
parent 26133aefe4
commit a9cc4bb482
11 changed files with 216 additions and 224 deletions

View File

@ -153,7 +153,8 @@
- monasca-tempest-python3-cassandra
- monasca-tempest-python2-java-cassandra
- monasca-tempest-python3-java-cassandra
- monascalog-python3-tempest
- monascalog-python3-tempest:
voting: false
- build-monasca-docker-image
gate:
queue: monasca
@ -165,7 +166,6 @@
- monasca-tempest-python3-cassandra
- monasca-tempest-python2-java-cassandra
- monasca-tempest-python3-java-cassandra
- monascalog-python3-tempest
post:
jobs:
- publish-monasca-api-docker-image

View File

@ -19,7 +19,7 @@ docutils==0.11
dulwich==0.15.0
eventlet==0.18.2
extras==1.0.0
falcon==1.0.0
falcon==2.0.0
fasteners==0.7.0
fixtures==3.0.0
flake8==2.5.5

View File

@ -33,6 +33,10 @@ def launch(conf):
config.parse_args()
app = falcon.API(request_type=request.Request)
# NOTE(dszumski): Falcon 2.0.0 switches the default for this from True
# to False so we explicitly set it here to prevent the behaviour
# changing between versions.
app.req_options.strip_url_path_trailing_slash = True
versions = simport.load(cfg.CONF.dispatcher.versions)()
app.add_route("/", versions)

View File

@ -34,24 +34,6 @@ from monasca_api import policies
policy.POLICIES = policies
class MockedAPI(falcon.API):
"""MockedAPI
Subclasses :py:class:`falcon.API` in order to overwrite
request_type property with custom :py:class:`request.Request`
"""
def __init__(self):
super(MockedAPI, self).__init__(
media_type=falcon.DEFAULT_MEDIA_TYPE,
request_type=request.Request,
response_type=falcon.Response,
middleware=None,
router=None
)
class ConfigFixture(oo_cfg.Config):
"""Mocks configuration"""
@ -96,8 +78,18 @@ class BaseTestCase(oslotest_base.BaseTestCase):
cfg.CONF.set_default(k, v, group)
class BaseApiTestCase(BaseTestCase, testing.TestBase):
api_class = MockedAPI
class BaseApiTestCase(BaseTestCase, testing.TestCase):
def setUp(self):
super(BaseApiTestCase, self).setUp()
# TODO(dszumski): Loading the app from api/server.py seems to make
# more sense here so that we don't have to manually keep the tests in
# sync with it.
self.app = falcon.API(request_type=request.Request)
# NOTE(dszumski): Falcon 2.0.0 switches the default for this from True
# to False so we explicitly set it here to prevent the behaviour
# changing between versions.
self.app.req_options.strip_url_path_trailing_slash = True
@staticmethod
def create_environ(*args, **kwargs):

View File

@ -27,7 +27,6 @@ import testtools.matchers as matchers
from mock import Mock
import oslo_config.fixture
from oslo_serialization import jsonutils
import six
from monasca_api.common.repositories.model import sub_alarm_definition
@ -124,10 +123,7 @@ class RESTResponseEquals(object):
return 'RESTResponseEquals(%s)' % (self.expected,)
def match(self, actual):
if len(actual) != 1:
return matchers.Mismatch("Response contains <> 1 item: %r" % actual)
response_data = jsonutils.loads(actual[0])
response_data = actual.json
if u"links" in response_data:
del response_data[u"links"]
@ -176,10 +172,10 @@ class TestAlarmsStateHistory(AlarmTestBase):
'metrics_repository.client.InfluxDBClient'))
self.alarms_resource = alarms.AlarmsStateHistory()
self.api.add_route(
self.app.add_route(
'/v2.0/alarms/{alarm_id}/state-history/', self.alarms_resource)
self.api.add_route(
self.app.add_route(
'/v2.0/alarms/state-history/', self.alarms_resource)
def test_alarm_state_history(self):
@ -188,26 +184,25 @@ class TestAlarmsStateHistory(AlarmTestBase):
del (expected_elements[u"elements"][0][u"sub_alarms"][0]
[u"sub_alarm_expression"][u"metric_definition"])
del expected_elements[u"elements"][0][u"tenant_id"]
response = self.simulate_request(
u'/v2.0/alarms/%s/state-history/' % ALARM_HISTORY[u"alarm_id"],
path=u'/v2.0/alarms/%s/state-history/' % ALARM_HISTORY[u"alarm_id"],
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID,
})
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
def test_alarm_state_history_no_alarm_id(self):
expected_elements = {u'elements': []}
response = self.simulate_request(
u'/v2.0/alarms/state-history/',
path=u'/v2.0/alarms/state-history/',
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID,
})
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
@ -220,8 +215,7 @@ class TestAlarmsCount(AlarmTestBase):
)).mock
self.alarms_count_resource = alarms.AlarmsCount()
self.api.add_route('/v2.0/alarms/count',
self.app.add_route('/v2.0/alarms/count',
self.alarms_count_resource)
def test_get_alarm_count(self):
@ -230,13 +224,13 @@ class TestAlarmsCount(AlarmTestBase):
return_value.get_alarms_count.return_value = [{'count': 4}]
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
def test_get_alarm_count_state_parameter(self):
@ -245,14 +239,14 @@ class TestAlarmsCount(AlarmTestBase):
return_value.get_alarms_count.return_value = [{'count': 4}]
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='state=OK')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
def test_get_alarm_count_severity_parameter(self):
@ -261,14 +255,14 @@ class TestAlarmsCount(AlarmTestBase):
return_value.get_alarms_count.return_value = [{'count': 4}]
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='severity=LOW')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
def test_get_alarm_count_group_by_parameter(self):
@ -280,14 +274,14 @@ class TestAlarmsCount(AlarmTestBase):
return_value.get_alarms_count.return_value = [{'metric_name': u'cpu.idle_perc', 'count': 2},
{'metric_name': u'cpu.sys_mem', 'count': 1}]
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='group_by=metric_name')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
expected_elements = {'columns': ['count', 'metric_name', 'dimension_name'],
@ -301,14 +295,14 @@ class TestAlarmsCount(AlarmTestBase):
'dimension_name': 'hostname',
'count': 1}]
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='group_by=metric_name,dimension_name')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
def test_get_alarm_count_incorrect_group_by_parameter(self):
@ -317,13 +311,14 @@ class TestAlarmsCount(AlarmTestBase):
return_value.get_alarms_count.return_value = [{'metric_name': u'cpu.idle_perc', 'count': 2},
{'metric_name': u'cpu.sys_mem', 'count': 1}]
self.simulate_request('/v2.0/alarms/count',
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='group_by=hahahah')
response = self.simulate_request(
path='/v2.0/alarms/count',
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='group_by=hahahah')
self.assertEqual(self.srmock.status, falcon.HTTP_422)
self.assertEqual(response.status, falcon.HTTP_422)
def test_get_alarm_count_offset(self):
return_value = self.alarms_get_alarms_count_mock.return_value
@ -332,13 +327,13 @@ class TestAlarmsCount(AlarmTestBase):
return_value.get_alarms_count.return_value = [{'metric_name': u'cpu.idle_perc', 'count': 2}]
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='group_by=metric_name&offset=1')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
def test_get_alarm_count_incorrect_offset(self):
@ -348,13 +343,13 @@ class TestAlarmsCount(AlarmTestBase):
return_value.get_alarms_count.return_value = [{'metric_name': u'cpu.idle_perc', 'count': 2}]
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='group_by=metric_name&offset=hahahah')
self.assertEqual(self.srmock.status, falcon.HTTP_422)
self.assertEqual(response.status, falcon.HTTP_422)
self.assertThat(response, RESTResponseEquals(expected_elements))
def test_get_alarm_count_limit_parameter(self):
@ -363,27 +358,27 @@ class TestAlarmsCount(AlarmTestBase):
return_value.get_alarms_count.return_value = [{'count': 4}]
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='limit=1')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
return_value.get_alarms_count.return_value = [{'count': 4}]
expected_elements = {'counts': [], 'columns': ['count']}
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='limit=0')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
def test_get_alarm_count_when_count_is_zero(self):
@ -392,24 +387,24 @@ class TestAlarmsCount(AlarmTestBase):
return_value.get_alarms_count.return_value = [{'count': 0}]
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='group_by=metric_name')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
expected_elements = {'columns': ['count'], 'counts': [[0]]}
response = self.simulate_request('/v2.0/alarms/count',
response = self.simulate_request(path='/v2.0/alarms/count',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_elements))
@ -422,10 +417,9 @@ class TestAlarms(AlarmTestBase):
)).mock
self.alarms_resource = alarms.Alarms()
self.api.add_route('/v2.0/alarms',
self.app.add_route('/v2.0/alarms',
self.alarms_resource)
self.api.add_route('/v2.0/alarms/{alarm_id}',
self.app.add_route('/v2.0/alarms/{alarm_id}',
self.alarms_resource)
def test_alarms_get_alarms(self):
@ -469,13 +463,13 @@ class TestAlarms(AlarmTestBase):
'state_updated_timestamp': '2015-03-14T09:26:53Z',
'updated_timestamp': '2015-03-14T09:26:53Z'}]}
response = self.simulate_request('/v2.0/alarms',
response = self.simulate_request(path='/v2.0/alarms',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_alarms))
def test_alarms_get_alarm(self):
@ -517,13 +511,13 @@ class TestAlarms(AlarmTestBase):
'state_updated_timestamp': '2015-03-14T09:26:53Z',
'updated_timestamp': '2015-03-14T09:26:53Z'}
response = self.simulate_request('/v2.0/alarms/1',
response = self.simulate_request(path='/v2.0/alarms/1',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_alarms))
def test_alarms_get_alarms_state_parameter(self):
@ -566,14 +560,14 @@ class TestAlarms(AlarmTestBase):
'state': 'OK',
'state_updated_timestamp': '2015-03-14T09:26:53Z',
'updated_timestamp': '2015-03-14T09:26:53Z'}]}
response = self.simulate_request('/v2.0/alarms',
response = self.simulate_request(path='/v2.0/alarms',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='state=OK')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_alarms))
def test_alarms_get_alarms_severity_parameter(self):
@ -616,14 +610,14 @@ class TestAlarms(AlarmTestBase):
'state': 'OK',
'state_updated_timestamp': '2015-03-14T09:26:53Z',
'updated_timestamp': '2015-03-14T09:26:53Z'}]}
response = self.simulate_request('/v2.0/alarms',
response = self.simulate_request(path='/v2.0/alarms',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='severity=LOW')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_alarms))
def test_alarms_get_alarms_with_offset(self):
@ -666,14 +660,14 @@ class TestAlarms(AlarmTestBase):
'state': 'OK',
'state_updated_timestamp': '2015-03-14T09:26:53Z',
'updated_timestamp': '2015-03-14T09:26:53Z'}]}
response = self.simulate_request('/v2.0/alarms',
response = self.simulate_request(path='/v2.0/alarms',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='offset=1')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_alarms))
def test_alarms_get_alarms_with_incorrect_offset(self):
@ -692,13 +686,14 @@ class TestAlarms(AlarmTestBase):
'alarm_id': '1',
'lifecycle_state': 'OPEN'}]
self.simulate_request('/v2.0/alarms',
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='offset=ninccorect_offset')
response = self.simulate_request(
path='/v2.0/alarms',
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='offset=ninccorect_offset')
self.assertEqual(self.srmock.status, falcon.HTTP_422)
self.assertEqual(response.status, falcon.HTTP_422)
def test_alarms_get_alarms_sort_by_parameter(self):
return_value = self.alarms_repo_mock.return_value
@ -740,14 +735,14 @@ class TestAlarms(AlarmTestBase):
'state': 'OK',
'state_updated_timestamp': '2015-03-14T09:26:53Z',
'updated_timestamp': '2015-03-14T09:26:53Z'}]}
response = self.simulate_request('/v2.0/alarms',
response = self.simulate_request(path='/v2.0/alarms',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='sort_by=alarm_id')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_alarms))
def test_alarms_get_alarms_incorrect_sort_by_parameter(self):
@ -766,14 +761,14 @@ class TestAlarms(AlarmTestBase):
'alarm_id': '1',
'lifecycle_state': 'OPEN'}]
self.simulate_request('/v2.0/alarms',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='sort_by=random_string')
response = self.simulate_request(path='/v2.0/alarms',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='GET',
query_string='sort_by=random_string')
self.assertEqual(self.srmock.status, falcon.HTTP_422)
self.assertEqual(response.status, falcon.HTTP_422)
def test_alarms_delete_alarms(self):
return_value = self.alarms_repo_mock.return_value
@ -786,13 +781,12 @@ class TestAlarms(AlarmTestBase):
'alarm_id': u'2',
'expression': u'avg(cpu.idle_perc{instance_id=123, service=monitoring}) > 10',
'alarm_definition_id': u'1'}]
self.simulate_request('/v2.0/alarms/2',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='DELETE')
self.assertEqual(self.srmock.status, falcon.HTTP_204)
response = self.simulate_request(path='/v2.0/alarms/2',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='DELETE')
self.assertEqual(response.status, falcon.HTTP_204)
def test_alarms_put(self):
return_value = self.alarms_repo_mock.return_value
@ -857,14 +851,14 @@ class TestAlarms(AlarmTestBase):
u'state_updated_timestamp': u'2019-02-22T12:44:25.850947Z',
u'updated_timestamp': u'2019-02-22T12:44:25.850947Z'}
response = self.simulate_request('/v2.0/alarms/2',
response = self.simulate_request(path='/v2.0/alarms/2',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='PUT',
body=json.dumps(alarm_new_fields))
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_alarm))
def test_alarms_put_without_link(self):
@ -872,14 +866,14 @@ class TestAlarms(AlarmTestBase):
'lifecycle_state': 'OPEN'}
expected_response = {u'description': u"Field 'link' is required",
u'title': u'Unprocessable Entity'}
response = self.simulate_request('/v2.0/alarms/2',
response = self.simulate_request(path='/v2.0/alarms/2',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='PUT',
body=json.dumps(alarm_new_fields))
self.assertEqual(self.srmock.status, falcon.HTTP_422)
self.assertEqual(response.status, falcon.HTTP_422)
self.assertThat(response, RESTResponseEquals(expected_response))
def test_alarms_put_without_lifecycle_state(self):
@ -887,14 +881,14 @@ class TestAlarms(AlarmTestBase):
'link': 'http://somesite.com/this-alarm-info'}
expected_response = {u'description': u"Field 'lifecycle_state' is required",
u'title': u'Unprocessable Entity'}
response = self.simulate_request('/v2.0/alarms/2',
response = self.simulate_request(path='/v2.0/alarms/2',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='PUT',
body=json.dumps(alarm_new_fields))
self.assertEqual(self.srmock.status, falcon.HTTP_422)
self.assertEqual(response.status, falcon.HTTP_422)
self.assertThat(response, RESTResponseEquals(expected_response))
def test_alarms_put_without_state(self):
@ -902,14 +896,14 @@ class TestAlarms(AlarmTestBase):
'link': 'http://somesite.com/this-alarm-info'}
expected_response = {u'description': u"Field 'state' is required",
u'title': u'Unprocessable Entity'}
response = self.simulate_request('/v2.0/alarms/2',
response = self.simulate_request(path='/v2.0/alarms/2',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='PUT',
body=json.dumps(alarm_new_fields))
self.assertEqual(self.srmock.status, falcon.HTTP_422)
self.assertEqual(response.status, falcon.HTTP_422)
self.assertThat(response, RESTResponseEquals(expected_response))
def test_alarms_patch(self):
@ -975,14 +969,14 @@ class TestAlarms(AlarmTestBase):
u'state_updated_timestamp': u'2019-02-22T12:44:25.850947Z',
u'updated_timestamp': u'2019-02-22T12:44:25.850947Z'}
response = self.simulate_request('/v2.0/alarms/2',
response = self.simulate_request(path='/v2.0/alarms/2',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='PATCH',
body=json.dumps(alarm_new_fields))
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_alarm))
def test_alarms_patch_without_new_fields(self):
@ -1046,14 +1040,14 @@ class TestAlarms(AlarmTestBase):
u'state_updated_timestamp': u'2019-02-22T12:44:25.850947Z',
u'updated_timestamp': u'2019-02-22T12:44:25.850947Z'}
response = self.simulate_request('/v2.0/alarms/2',
response = self.simulate_request(path='/v2.0/alarms/2',
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='PATCH',
body=json.dumps(alarm_new_fields))
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_alarm))
@ -1070,9 +1064,9 @@ class TestAlarmDefinition(AlarmTestBase):
self.alarm_definition_resource.send_event = Mock()
self._send_event = self.alarm_definition_resource.send_event
self.api.add_route("/v2.0/alarm-definitions/",
self.app.add_route("/v2.0/alarm-definitions/",
self.alarm_definition_resource)
self.api.add_route("/v2.0/alarm-definitions/{alarm_definition_id}",
self.app.add_route("/v2.0/alarm-definitions/{alarm_definition_id}",
self.alarm_definition_resource)
def test_alarm_definition_create(self):
@ -1099,14 +1093,14 @@ class TestAlarmDefinition(AlarmTestBase):
u'severity': u'LOW',
}
response = self.simulate_request("/v2.0/alarm-definitions/",
response = self.simulate_request(path="/v2.0/alarm-definitions/",
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="POST",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_201)
self.assertEqual(response.status, falcon.HTTP_201)
self.assertThat(response, RESTResponseEquals(expected_data))
def test_alarm_definition_create_with_valid_expressions(self):
@ -1157,14 +1151,14 @@ class TestAlarmDefinition(AlarmTestBase):
for expression in valid_expressions:
alarm_def[u'expression'] = expression
expected_data[u'expression'] = expression
response = self.simulate_request("/v2.0/alarm-definitions/",
response = self.simulate_request(path="/v2.0/alarm-definitions/",
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="POST",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_201,
self.assertEqual(response.status, falcon.HTTP_201,
u'Expression {} should have passed'.format(expression))
self.assertThat(response, RESTResponseEquals(expected_data))
@ -1183,13 +1177,14 @@ class TestAlarmDefinition(AlarmTestBase):
for expression in bad_expressions:
alarm_def[u'expression'] = expression
self.simulate_request("/v2.0/alarm-definitions/",
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="POST",
body=json.dumps(alarm_def))
response = self.simulate_request(
path="/v2.0/alarm-definitions/",
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="POST",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, '422 Unprocessable Entity',
self.assertEqual(response.status, '422 Unprocessable Entity',
u'Expression {} should have failed'.format(expression))
def test_alarm_definition_create_with_occupied_alarm_definition_name(self):
@ -1209,12 +1204,13 @@ class TestAlarmDefinition(AlarmTestBase):
u'name': u'Test Definition',
u'expression': u'max(test.metric{hostname=host}) gte 1'
}
self.simulate_request("/v2.0/alarm-definitions/",
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="POST",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_409)
response = self.simulate_request(
path="/v2.0/alarm-definitions/",
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="POST",
body=json.dumps(alarm_def))
self.assertEqual(response.status, falcon.HTTP_409)
def test_alarm_definition_update(self):
self.alarm_def_repo_mock.return_value.get_alarm_definitions.return_value = []
@ -1285,15 +1281,15 @@ class TestAlarmDefinition(AlarmTestBase):
u'severity': u'LOW',
}
result = self.simulate_request("/v2.0/alarm-definitions/%s" % expected_def[u'id'],
result = self.simulate_request(path="/v2.0/alarm-definitions/%s" % expected_def[u'id'],
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PUT",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_200)
result_def = jsonutils.loads(result[0])
self.assertEqual(result.status, falcon.HTTP_200)
result_def = result.json
self.assertEqual(result_def, expected_def)
def test_alarm_definition_patch_incorrect_id(self):
@ -1312,15 +1308,15 @@ class TestAlarmDefinition(AlarmTestBase):
alarm_def = {
u'name': u'Test Alarm Definition Updated',
}
self.simulate_request(
"/v2.0/alarm-definitions/9999999-0001-0001-0001-000000000001",
response = self.simulate_request(
path="/v2.0/alarm-definitions/9999999-0001-0001-0001-000000000001",
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PATCH",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_409)
self.assertEqual(response.status, falcon.HTTP_409)
def test_alarm_definition_put_incorrect_period_value(self):
self.alarm_def_repo_mock.return_value.get_alarm_definitions.return_value = []
@ -1338,43 +1334,44 @@ class TestAlarmDefinition(AlarmTestBase):
u'severity': u'LOW',
}
self.simulate_request("/v2.0/alarm-definitions/00000001-0001-0001-0001-000000000001",
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PUT",
body=json.dumps(alarm_def))
response = self.simulate_request(
path="/v2.0/alarm-definitions/00000001-0001-0001-0001-000000000001",
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PUT",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_422)
self.assertEqual(response.status, falcon.HTTP_422)
def test_alarm_definition_patch_no_id(self):
alarm_def = {
u'name': u'Test Alarm Definition Updated',
}
self.simulate_request(
"/v2.0/alarm-definitions/",
response = self.simulate_request(
path="/v2.0/alarm-definitions/",
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PATCH",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_400)
self.assertEqual(response.status, falcon.HTTP_400)
def test_alarm_definition_update_no_id(self):
alarm_def = {
u'name': u'Test Alarm Definition Updated',
}
self.simulate_request(
"/v2.0/alarm-definitions/",
response = self.simulate_request(
path="/v2.0/alarm-definitions/",
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PUT",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_400)
self.assertEqual(response.status, falcon.HTTP_400)
def test_alarm_definition_delete(self):
@ -1400,14 +1397,14 @@ class TestAlarmDefinition(AlarmTestBase):
'sub_alarm_id': '43'}]
self.alarm_def_repo_mock.return_value.delete_alarm_definition.return_value = True
self.simulate_request(
'/v2.0/alarm-definitions/00000001-0001-0001-0001-000000000001',
response = self.simulate_request(
path='/v2.0/alarm-definitions/00000001-0001-0001-0001-000000000001',
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='DELETE')
self.assertEqual(self.srmock.status, falcon.HTTP_204)
self.assertEqual(response.status, falcon.HTTP_204)
def test_alarm_definition_delete_alarm_definition_not_exist(self):
self.alarm_def_repo_mock.return_value.get_get_sub_alarm_definitions.return_value = []
@ -1415,25 +1412,25 @@ class TestAlarmDefinition(AlarmTestBase):
self.alarm_def_repo_mock.return_value.get_sub_alarms.return_value = []
self.alarm_def_repo_mock.return_value.delete_alarm_definition.return_value = False
self.simulate_request(
'/v2.0/alarm-definitions/00000001-0001-0001-0001-000000000001',
response = self.simulate_request(
path='/v2.0/alarm-definitions/00000001-0001-0001-0001-000000000001',
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method='DELETE')
self.assertEqual(self.srmock.status, falcon.HTTP_404)
self.assertEqual(response.status, falcon.HTTP_404)
def test_alarm_definition_delete_no_id(self):
self.simulate_request(
"/v2.0/alarm-definitions/",
response = self.simulate_request(
path="/v2.0/alarm-definitions/",
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="DELETE")
self.assertEqual(self.srmock.status, falcon.HTTP_400)
self.assertEqual(response.status, falcon.HTTP_400)
def test_alarm_definition_patch(self):
self.alarm_def_repo_mock.return_value.get_alarm_definitions.return_value = []
@ -1502,15 +1499,15 @@ class TestAlarmDefinition(AlarmTestBase):
u'name': u'Test Alarm Updated',
}
result = self.simulate_request("/v2.0/alarm-definitions/%s" % expected_def[u'id'],
result = self.simulate_request(path="/v2.0/alarm-definitions/%s" % expected_def[u'id'],
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PATCH",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_200)
result_def = jsonutils.loads(result[0])
self.assertEqual(result.status, falcon.HTTP_200)
result_def = result.json
self.assertEqual(result_def, expected_def)
# If the alarm-definition-updated event does not have all of the
# fields set, the Threshold Engine will get confused. For example,
@ -1612,26 +1609,28 @@ class TestAlarmDefinition(AlarmTestBase):
u'severity': u'LOW'
}
result = self.simulate_request("/v2.0/alarm-definitions/%s" % expected_def[u'id'],
result = self.simulate_request(path="/v2.0/alarm-definitions/%s" % expected_def[u'id'],
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PUT",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, falcon.HTTP_200)
result_def = jsonutils.loads(result[0])
self.assertEqual(result.status, falcon.HTTP_200)
result_def = result.json
self.assertEqual(result_def, expected_def)
for key, value in alarm_def.items():
del alarm_def[key]
self.simulate_request("/v2.0/alarm-definitions/%s" % expected_def[u'id'],
headers={'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PUT",
body=json.dumps(alarm_def))
self.assertEqual(self.srmock.status, "422 Unprocessable Entity",
response = self.simulate_request(
path="/v2.0/alarm-definitions/%s" % expected_def[u'id'],
headers={'X-Roles':
CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID},
method="PUT",
body=json.dumps(alarm_def))
self.assertEqual(response.status, "422 Unprocessable Entity",
u"should have failed without key {}".format(key))
alarm_def[key] = value
@ -1668,13 +1667,13 @@ class TestAlarmDefinition(AlarmTestBase):
}
response = self.simulate_request(
'/v2.0/alarm-definitions/%s' % (expected_data[u'id']),
path='/v2.0/alarm-definitions/%s' % (expected_data[u'id']),
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID,
})
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_data))
def test_alarm_definition_get_specific_alarm_description_none(self):
@ -1707,13 +1706,13 @@ class TestAlarmDefinition(AlarmTestBase):
}
response = self.simulate_request(
'/v2.0/alarm-definitions/%s' % (expected_data[u'id']),
path='/v2.0/alarm-definitions/%s' % (expected_data[u'id']),
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID,
})
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_data))
def test_get_alarm_definitions_with_multibyte_character(self):
@ -1745,14 +1744,13 @@ class TestAlarmDefinition(AlarmTestBase):
}
response = self.simulate_request(
'/v2.0/alarm-definitions/%s' % (expected_data[u'id']),
path='/v2.0/alarm-definitions/%s' % (expected_data[u'id']),
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID,
}
)
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_data))
def test_alarm_definition_get_alarm_definition_list(self):
@ -1789,56 +1787,56 @@ class TestAlarmDefinition(AlarmTestBase):
}
response = self.simulate_request(
'/v2.0/alarm-definitions',
path='/v2.0/alarm-definitions',
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID
},
query_string='name=Test Alarm')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_data))
response = self.simulate_request(
'/v2.0/alarm-definitions',
path='/v2.0/alarm-definitions',
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID
},
query_string='sort_by=name')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_data))
response = self.simulate_request(
'/v2.0/alarm-definitions',
path='/v2.0/alarm-definitions',
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID
},
query_string='severity=LOW')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_data))
response = self.simulate_request(
'/v2.0/alarm-definitions',
path='/v2.0/alarm-definitions',
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID
},
query_string='offset=1')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
self.assertEqual(response.status, falcon.HTTP_200)
self.assertThat(response, RESTResponseEquals(expected_data))
def test_alarm_definition_get_alarm_definition_list_incorrect(self):
self.simulate_request(
'/v2.0/alarm-definitions',
response = self.simulate_request(
path='/v2.0/alarm-definitions',
headers={
'X-Roles': CONF.security.default_authorized_roles[0],
'X-Tenant-Id': TENANT_ID
},
query_string='offset=definitelyNotINT')
self.assertEqual(self.srmock.status, falcon.HTTP_422)
self.assertEqual(response.status, falcon.HTTP_422)
def test_alarm_definition_get_query_alarm_definition_name(self):
alarm_def = {

View File

@ -19,7 +19,7 @@ from monasca_api import config
from monasca_api.healthcheck import base
from monasca_api import healthchecks
from monasca_api.tests import base as test_base
from monasca_common.rest import utils
CONF = config.CONF
ENDPOINT = '/healthcheck'
@ -32,9 +32,10 @@ class TestHealthChecks(test_base.BaseApiTestCase):
if hasattr(CONF, 'sql_engine'):
delattr(CONF, 'sql_engine')
def set_route(self):
def setUp(self):
super(TestHealthChecks, self).setUp()
self.resources = healthchecks.HealthChecks()
self.api.add_route(
self.app.add_route(
ENDPOINT,
self.resources
)
@ -43,9 +44,8 @@ class TestHealthChecks(test_base.BaseApiTestCase):
@mock.patch(
'monasca_api.healthcheck.metrics_db_check.MetricsDbCheck')
def test_should_return_200_for_head(self, metrics_db_check, _):
self.set_route()
self.simulate_request(ENDPOINT, method='HEAD')
self.assertEqual(falcon.HTTP_NO_CONTENT, self.srmock.status)
result = self.simulate_request(path=ENDPOINT, method='HEAD')
self.assertEqual(falcon.HTTP_NO_CONTENT, result.status)
@mock.patch('monasca_api.healthcheck.kafka_check.KafkaHealthCheck')
@mock.patch(
@ -63,20 +63,18 @@ class TestHealthChecks(test_base.BaseApiTestCase):
'OK')
metrics_db_check.health_check.return_value = base.CheckResult(True,
'OK')
self.set_route()
self.resources._kafka_check = kafka_check
self.resources._alarm_db_check = alarms_db_check
self.resources._metrics_db_check = metrics_db_check
response = self.simulate_request(ENDPOINT,
response = self.simulate_request(path=ENDPOINT,
headers={
'Content-Type': 'application/json'
},
decode='utf8',
method='GET')
self.assertEqual(falcon.HTTP_OK, self.srmock.status)
self.assertEqual(falcon.HTTP_OK, response.status)
response = utils.from_json(response)
response = response.json
self.assertIn('kafka', response)
self.assertIn('alarms_database', response)
self.assertIn('metrics_database', response)
@ -118,20 +116,19 @@ class TestHealthChecks(test_base.BaseApiTestCase):
service['alarms_db']['healthy'], service['alarms_db']['message'])
metrics_db_check.health_check.return_value = base.CheckResult(
service['netrics_db']['healthy'], service['netrics_db']['message'])
self.set_route()
self.resources._kafka_check = kafka_check
self.resources._alarm_db_check = alarms_db_check
self.resources._metrics_db_check = metrics_db_check
response = self.simulate_request(ENDPOINT,
response = self.simulate_request(path=ENDPOINT,
headers={
'Content-Type': 'application/json'
},
decode='utf8',
method='GET')
self.assertEqual(falcon.HTTP_SERVICE_UNAVAILABLE, self.srmock.status)
self.assertEqual(falcon.HTTP_SERVICE_UNAVAILABLE,
response.status)
response = utils.from_json(response)
response = response.json
self.assertIn('kafka', response)
self.assertIn('alarms_database', response)
self.assertIn('metrics_database', response)

View File

@ -14,7 +14,6 @@
# under the License.
import datetime
import json
import falcon
@ -24,15 +23,15 @@ from monasca_api.v2.reference import versions
class TestVersions(base.BaseApiTestCase):
def before(self):
self.versions_resource = versions.Versions()
self.api.add_route('/versions', self.versions_resource)
self.api.add_route('/versions/{version_id}', self.versions_resource)
def setUp(self):
super(TestVersions, self).setUp()
self.app.add_route('/', versions.Versions())
self.app.add_route('/{version_id}', versions.Versions())
def test_list_versions(self):
result = self.simulate_request('/versions')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
response = json.loads(result[0].decode('utf-8'))
result = self.simulate_request(path='/')
self.assertEqual(result.status, falcon.HTTP_200)
response = result.json
self.assertIsInstance(response, dict)
self.assertTrue(set(['links', 'elements']) ==
set(response))
@ -42,12 +41,12 @@ class TestVersions(base.BaseApiTestCase):
self.assertTrue(set(['rel', 'href']) ==
set(link))
self.assertEqual(link['rel'], u'self')
self.assertTrue(link['href'].endswith('versions'))
self.assertTrue(link['href'].endswith('/'))
def test_valid_version_id(self):
result = self.simulate_request('/versions/v2.0')
self.assertEqual(self.srmock.status, falcon.HTTP_200)
response = json.loads(result[0].decode('utf-8'))
result = self.simulate_request(path='/v2.0')
self.assertEqual(result.status, falcon.HTTP_200)
response = result.json
self.assertIsInstance(response, dict)
version = response
self.assertTrue(set(['id', 'links', 'status', 'updated']) ==
@ -63,8 +62,8 @@ class TestVersions(base.BaseApiTestCase):
self.assertTrue(set(['rel', 'href']) ==
set(link))
self.assertEqual(link['rel'], u'self')
self.assertTrue(link['href'].endswith('/versions/v2.0'))
self.assertTrue(link['href'].endswith('/v2.0'))
def test_invalid_version_id(self):
self.simulate_request('/versions/v1.0')
self.assertEqual(self.srmock.status, '422 Unprocessable Entity')
result = self.simulate_request(path='/v1.0')
self.assertEqual(result.status, '422 Unprocessable Entity')

View File

@ -40,8 +40,7 @@ def from_json(req):
:raises falcon.HTTPBadRequest:
"""
try:
msg = req.stream.read()
return rest_utils.from_json(msg)
return req.media
except Exception as ex:
LOG.exception(ex)
raise falcon.HTTPBadRequest('Bad request',

View File

@ -13,7 +13,7 @@ oslo.utils>=3.33.0 # Apache-2.0
python-keystoneclient>=3.8.0 # Apache-2.0
falcon>=1.0.0 # Apache-2.0
falcon>=2.0.0 # Apache-2.0
keystonemiddleware>=4.17.0 # Apache-2.0
PasteDeploy>=1.5.0 # MIT
pbr!=2.1.0,>=2.0.0 # Apache-2.0

View File

@ -29,7 +29,8 @@ data_files =
[extras]
influxdb =
influxdb>=2.9.2 # MIT
influxdb>=2.9.2,!=5.2.0,!=5.2.1,!=5.2.2;python_version<'3.0' # MIT
influxdb>=2.9.2;python_version>='3.0' # MIT
cassandra =
cassandra-driver!=3.6.0,>=3.3.0 # Apache-2.0

View File

@ -10,7 +10,8 @@ coverage!=4.4,>=4.0 # Apache-2.0
cassandra-driver!=3.6.0,>=3.3.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
httplib2>=0.9.1 # MIT
influxdb>=2.9.2 # MIT
influxdb>=2.9.2,!=5.2.0,!=5.2.1,!=5.2.2;python_version<'3.0' # MIT
influxdb>=2.9.2;python_version>='3.0' # MIT
mock>=2.0.0 # BSD
funcsigs>=1.0.0;python_version=='2.7' or python_version=='2.6' # Apache-2.0
oslotest>=3.2.0 # Apache-2.0
@ -25,7 +26,8 @@ tempest>=17.1.0 # Apache-2.0
# documentation
doc8>=0.6.0 # Apache-2.0
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
sphinx!=1.6.6,!=1.6.7,>=1.6.2;python_version>='3.4' # BSD
os-api-ref>=1.4.0 # Apache-2.0
reno>=2.5.0 # Apache-2.0
openstackdocstheme>=1.18.1 # Apache-2.0