Merge "tests: Add API sample tests for os-services API"

This commit is contained in:
Zuul
2025-12-01 21:52:12 +00:00
committed by Gerrit Code Review
29 changed files with 531 additions and 11 deletions

View File

@@ -0,0 +1,5 @@
{
"host": "host1",
"binary": "cinder-volume",
"disabled_reason": "test2"
}

View File

@@ -0,0 +1,8 @@
{
"binary": "cinder-volume",
"disabled": true,
"disabled_reason": "test2",
"host": "host1",
"service": "",
"status": "disabled"
}

View File

@@ -0,0 +1,4 @@
{
"host": "host1",
"binary": "cinder-volume"
}

View File

@@ -0,0 +1,7 @@
{
"binary": "cinder-volume",
"disabled": true,
"host": "host1",
"service": "",
"status": "disabled"
}

View File

@@ -0,0 +1,4 @@
{
"host": "host1",
"binary": "cinder-volume"
}

View File

@@ -0,0 +1,7 @@
{
"binary": "cinder-volume",
"disabled": false,
"host": "host1",
"service": "",
"status": "enabled"
}

View File

@@ -0,0 +1,3 @@
{
"host": "host1"
}

View File

@@ -0,0 +1,3 @@
{
"host": "host1"
}

View File

@@ -0,0 +1,3 @@
{
"host": "host1"
}

View File

@@ -0,0 +1,5 @@
{
"binary": "cinder-volume",
"server": "devstack@lvmdriver-1",
"prefix": "cinder.volume"
}

View File

@@ -0,0 +1,25 @@
{
"log_levels": [
{
"binary": "cinder-volume",
"host": "host2",
"levels": {
"cinder.volume.api": "DEBUG"
}
},
{
"binary": "cinder-volume",
"host": "host2",
"levels": {
"cinder.volume.api": "DEBUG"
}
},
{
"binary": "cinder-volume",
"host": "host2",
"levels": {
"cinder.volume.api": "DEBUG"
}
}
]
}

View File

@@ -0,0 +1,6 @@
{
"binary": "cinder-volume",
"server": "devstack@lvmdriver-1",
"prefix": "cinder.volume",
"level": "ERROR"
}

View File

@@ -0,0 +1,90 @@
{
"services": [
{
"binary": "cinder-scheduler",
"cluster": null,
"disabled_reason": "test1",
"host": "host1",
"state": "down",
"status": "disabled",
"updated_at": "2012-10-29T13:42:02.000000",
"zone": "cinder"
},
{
"active_backend_id": null,
"backend_state": null,
"binary": "cinder-volume",
"cluster": null,
"disabled_reason": "test2",
"frozen": false,
"host": "host1",
"replication_status": null,
"state": "down",
"status": "disabled",
"updated_at": "2012-10-29T13:42:05.000000",
"zone": "cinder"
},
{
"binary": "cinder-scheduler",
"cluster": "cluster1",
"disabled_reason": "",
"host": "host2",
"state": "down",
"status": "enabled",
"updated_at": "2012-09-19T06:55:34.000000",
"zone": "cinder"
},
{
"active_backend_id": null,
"backend_state": null,
"binary": "cinder-volume",
"cluster": "cluster1",
"disabled_reason": "test4",
"frozen": false,
"host": "host2",
"replication_status": null,
"state": "down",
"status": "disabled",
"updated_at": "2012-09-18T08:03:38.000000",
"zone": "cinder"
},
{
"active_backend_id": null,
"backend_state": null,
"binary": "cinder-volume",
"cluster": "cluster2",
"disabled_reason": "test5",
"frozen": false,
"host": "host2",
"replication_status": null,
"state": "down",
"status": "disabled",
"updated_at": "2012-10-29T13:42:05.000000",
"zone": "cinder"
},
{
"active_backend_id": null,
"backend_state": null,
"binary": "cinder-volume",
"cluster": "cluster2",
"disabled_reason": "",
"frozen": false,
"host": "host2",
"replication_status": null,
"state": "down",
"status": "enabled",
"updated_at": "2012-09-18T08:03:38.000000",
"zone": "cinder"
},
{
"binary": "cinder-scheduler",
"cluster": null,
"disabled_reason": "",
"host": "host2",
"state": "down",
"status": "enabled",
"updated_at": null,
"zone": "cinder"
}
]
}

View File

@@ -127,6 +127,8 @@ class ServiceController(wsgi.Controller):
except exception.ServiceNotFound as ex:
raise exception.InvalidInput(ex.msg)
# TODO: This currently returns HTTP 200 but it should return HTTP 204 since
# there's no content
@validation.schema(os_services.freeze_and_thaw)
def _freeze(self, req, context, body):
cluster_name, host = common.get_cluster_host(
@@ -134,6 +136,8 @@ class ServiceController(wsgi.Controller):
return self._volume_api_proxy(self.volume_api.freeze_host, context,
host, cluster_name)
# TODO: This currently returns HTTP 200 but it should return HTTP 204 since
# there's no content
@validation.schema(os_services.freeze_and_thaw)
def _thaw(self, req, context, body):
cluster_name, host = common.get_cluster_host(

View File

@@ -0,0 +1,5 @@
{
"host": "%(host)s",
"binary": "%(binary)s",
"disabled_reason": "%(disabled_reason)s"
}

View File

@@ -0,0 +1,8 @@
{
"binary": "cinder-volume",
"disabled": true,
"disabled_reason": "test2",
"host": "host1",
"service": "",
"status": "disabled"
}

View File

@@ -0,0 +1,4 @@
{
"host": "%(host)s",
"binary": "%(binary)s"
}

View File

@@ -0,0 +1,7 @@
{
"binary": "cinder-volume",
"disabled": true,
"host": "host1",
"service": "",
"status": "disabled"
}

View File

@@ -0,0 +1,4 @@
{
"host": "%(host)s",
"binary": "%(binary)s"
}

View File

@@ -0,0 +1,7 @@
{
"binary": "cinder-volume",
"disabled": false,
"host": "host1",
"service": "",
"status": "enabled"
}

View File

@@ -0,0 +1,3 @@
{
"host": "%(host)s"
}

View File

@@ -0,0 +1,3 @@
{
"host": "%(host)s"
}

View File

@@ -0,0 +1,3 @@
{
"host": "%(host)s"
}

View File

@@ -0,0 +1,5 @@
{
"binary": "cinder-volume",
"server": "%(host)s",
"prefix": "cinder.volume"
}

View File

@@ -0,0 +1,25 @@
{
"log_levels": [
{
"binary": "cinder-volume",
"host": "host2",
"levels": {
"cinder.volume.api": "DEBUG"
}
},
{
"binary": "cinder-volume",
"host": "host2",
"levels": {
"cinder.volume.api": "DEBUG"
}
},
{
"binary": "cinder-volume",
"host": "host2",
"levels": {
"cinder.volume.api": "DEBUG"
}
}
]
}

View File

@@ -0,0 +1,6 @@
{
"binary": "cinder-volume",
"server": "devstack@lvmdriver-1",
"prefix": "cinder.volume",
"level": "ERROR"
}

View File

@@ -0,0 +1,90 @@
{
"services": [
{
"binary": "cinder-scheduler",
"cluster": null,
"disabled_reason": "test1",
"host": "host1",
"state": "down",
"status": "disabled",
"updated_at": "2012-10-29T13:42:02.000000",
"zone": "cinder"
},
{
"active_backend_id": null,
"backend_state": null,
"binary": "cinder-volume",
"cluster": null,
"disabled_reason": "test2",
"frozen": false,
"host": "host1",
"replication_status": null,
"state": "down",
"status": "disabled",
"updated_at": "2012-10-29T13:42:05.000000",
"zone": "cinder"
},
{
"binary": "cinder-scheduler",
"cluster": "cluster1",
"disabled_reason": "",
"host": "host2",
"state": "down",
"status": "enabled",
"updated_at": "2012-09-19T06:55:34.000000",
"zone": "cinder"
},
{
"active_backend_id": null,
"backend_state": null,
"binary": "cinder-volume",
"cluster": "cluster1",
"disabled_reason": "test4",
"frozen": false,
"host": "host2",
"replication_status": null,
"state": "down",
"status": "disabled",
"updated_at": "2012-09-18T08:03:38.000000",
"zone": "cinder"
},
{
"active_backend_id": null,
"backend_state": null,
"binary": "cinder-volume",
"cluster": "cluster2",
"disabled_reason": "test5",
"frozen": false,
"host": "host2",
"replication_status": null,
"state": "down",
"status": "disabled",
"updated_at": "2012-10-29T13:42:05.000000",
"zone": "cinder"
},
{
"active_backend_id": null,
"backend_state": null,
"binary": "cinder-volume",
"cluster": "cluster2",
"disabled_reason": "",
"frozen": false,
"host": "host2",
"replication_status": null,
"state": "down",
"status": "enabled",
"updated_at": "2012-09-18T08:03:38.000000",
"zone": "cinder"
},
{
"binary": "cinder-scheduler",
"cluster": null,
"disabled_reason": "",
"host": "host2",
"state": "down",
"status": "enabled",
"updated_at": null,
"zone": "cinder"
}
]
}

View File

@@ -0,0 +1,159 @@
# 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 cinder.api import microversions as mv
from cinder import context
from cinder import objects
from cinder.tests.functional import api_samples_test_base as test_base
from cinder.tests.unit.api.contrib import test_services
def fake_volume_api_freeze_host(*args, **kwargs):
pass
def fake_volume_api_thaw_host(*args, **kwargs):
pass
def fake_volume_api_failover(*args, **kwargs):
pass
def fake_volume_rpc_api_get_log_levels(*args, **kwargs):
fake_context = context.RequestContext('user', 'project')
return objects.LogLevelList(
fake_context,
objects=[
objects.LogLevel(
fake_context, prefix="cinder.volume.api", level='DEBUG',
)
],
)
class ServicesSampleJsonTest(test_base.ApiSampleTestBase):
sample_dir = 'os-services'
def setUp(self):
super().setUp()
self.stub_out(
'cinder.db.sqlalchemy.api.service_get_all',
test_services.fake_db_api_service_get_all,
)
self.stub_out(
'cinder.db.sqlalchemy.api.service_get',
test_services.fake_db_api_service_get,
)
self.stub_out(
'cinder.db.sqlalchemy.api.service_update',
test_services.fake_db_api_service_update,
)
self.stub_out(
'cinder.volume.api.API.freeze_host',
fake_volume_api_freeze_host,
)
self.stub_out(
'cinder.volume.api.API.thaw_host',
fake_volume_api_thaw_host,
)
self.stub_out(
'cinder.volume.api.API.failover',
fake_volume_api_failover,
)
self.stub_out(
'cinder.volume.rpcapi.VolumeAPI.get_log_levels',
fake_volume_rpc_api_get_log_levels,
)
self.subs = {}
@test_base.VolumesSampleBase.override_mv(mv.BACKEND_STATE_REPORT)
def test_service_list(self):
response = self._do_get('os-services')
self._verify_response(
'services-list-response', {}, response, 200
)
def test_service_enable(self):
subs = {'host': 'host1', 'binary': 'cinder-volume'}
response = self._do_put(
'os-services/enable', 'service-enable-request', subs
)
self._verify_response(
'service-enable-response', subs, response, 200
)
def test_service_disable(self):
subs = {'host': 'host1', 'binary': 'cinder-volume'}
response = self._do_put(
'os-services/disable', 'service-disable-request', subs
)
self._verify_response(
'service-disable-response', subs, response, 200
)
def test_service_disable_log_reason(self):
subs = {
'host': 'host1',
'binary': 'cinder-volume',
'disabled_reason': 'test2',
}
response = self._do_put(
'os-services/disable-log-reason',
'service-disable-log-reason-request',
subs
)
self._verify_response(
'service-disable-log-reason-response', subs, response, 200
)
def test_service_freeze(self):
subs = {'host': 'host1'}
response = self._do_put(
'os-services/freeze', 'service-freeze-request', subs
)
self.assertEqual(200, response.status_code)
self.assertEqual('', response.text)
def test_service_thaw(self):
subs = {'host': 'host1'}
response = self._do_put(
'os-services/thaw', 'service-thaw-request', subs
)
self.assertEqual(200, response.status_code)
self.assertEqual('', response.text)
def test_service_failover_host(self):
subs = {'host': 'host1'}
response = self._do_put(
'os-services/failover_host', 'service-failover-host-request', subs
)
self.assertEqual(202, response.status_code)
self.assertEqual('', response.text)
@test_base.VolumesSampleBase.override_mv(mv.LOG_LEVEL)
def test_service_set_log(self):
response = self._do_put(
'os-services/set-log', 'service-set-log-request'
)
self.assertEqual(202, response.status_code)
self.assertEqual('', response.text)
@test_base.VolumesSampleBase.override_mv(mv.LOG_LEVEL)
def test_service_get_log(self):
subs = {'host': 'host2'}
response = self._do_put(
'os-services/get-log', 'service-get-log-request', subs
)
self._verify_response(
'service-get-log-response', {}, response, 200
)

View File

@@ -145,12 +145,26 @@ class FakeRequestWithHostBinary(FakeRequestWithBinary):
super(FakeRequestWithHostBinary, self).__init__(**kwargs)
def fake_service_get_all(context, **filters):
def fake_db_api_service_get_all(context, backend_match_level=None, **filters):
result = []
host = filters.pop('host', None)
host_or_cluster = filters.pop('host_or_cluster', None)
filters.pop('is_up', None)
for service in fake_services_list:
if (host and service['host'] != host and
not service['host'].startswith(host + '@')):
if (
host and service['host'] != host and
not service['host'].startswith(host + '@')
):
continue
if (
host_or_cluster and (
(
service['host'] != host_or_cluster and
not service['host'].startswith(host_or_cluster + '@')
) and service['cluster_name'] != host_or_cluster
)
):
continue
if all(v is None or service.get(k) == v for k, v in filters.items()):
@@ -158,22 +172,24 @@ def fake_service_get_all(context, **filters):
return result
def fake_service_get(context, service_id=None, **filters):
result = fake_service_get_all(context, id=service_id, **filters)
def fake_db_api_service_get(
context, service_id=None, backend_match_level=None, **filters
):
result = fake_db_api_service_get_all(context, id=service_id, **filters)
if not result:
raise exception.ServiceNotFound(service_id=service_id)
return result[0]
def fake_service_get_by_id(value):
def fake_db_api_service_get_by_id(value):
for service in fake_services_list:
if service['id'] == value:
return service
return None
def fake_service_update(context, service_id, values, retry=True):
service = fake_service_get_by_id(service_id)
def fake_db_api_service_update(context, service_id, values, retry=True):
service = fake_db_api_service_get_by_id(service_id)
if service is None:
raise exception.ServiceNotFound(service_id=service_id)
else:
@@ -198,10 +214,11 @@ def fake_get_pools(ctxt, filters=None):
@ddt.ddt
@mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.get_pools', fake_get_pools)
@mock.patch('cinder.db.service_get_all', fake_service_get_all)
@mock.patch('cinder.db.service_get', fake_service_get)
@mock.patch('cinder.db.service_get_all', fake_db_api_service_get_all)
@mock.patch('cinder.db.service_get', fake_db_api_service_get)
@mock.patch('oslo_utils.timeutils.utcnow', fake_utcnow)
@mock.patch('cinder.db.sqlalchemy.api.service_update', fake_service_update)
@mock.patch(
'cinder.db.sqlalchemy.api.service_update', fake_db_api_service_update)
@mock.patch('cinder.policy.authorize', fake_policy_authorize)
class ServicesTest(test.TestCase):