Files
blazar/blazar/tests/api/v1/oshosts/test_v1_0.py
Mark Powers f5e6d24826 Add resource properties discovery API
This allows users to query for resource properties of hosts,
allowing them to be used in creating leases more effectively. Two new
API endpoints are added for hosts, ``/properties`` and
``/properties/<property_name>``, which allow for listing available
properties, and updating a property respectively. Properties can be
listed with detail, showing possible values and visibility. Admins can
list public and private properties.

A new database table is added ``resource_properties``, which stores
property names and resource types. Resource specific property tables
(e.g. ``computehost_extra_capabilities``) entries store a foreign key to
``resource_properties``, rather than the capability name.

Implements blueprint resource-properties-discovery-api

Change-Id: Ib9f1140c44c5e4fbef6e019c48a842869368cb21
2022-02-10 17:23:15 +00:00

261 lines
11 KiB
Python

# Copyright (c) 2013 Bull.
#
# 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 ddt
import flask
from oslo_utils import uuidutils
from testtools import matchers
from oslo_middleware import request_id as id
from blazar.api import context as api_context
from blazar.api.v1 import api_version_request
from blazar.api.v1.oshosts import service as service_api
from blazar.api.v1.oshosts import v1_0 as hosts_api_v1_0
from blazar.api.v1 import request_id
from blazar.api.v1 import request_log
from blazar import context
from blazar import tests
def make_app():
"""App builder (wsgi).
Entry point for Blazar REST API server.
"""
app = flask.Flask('blazar.api')
app.register_blueprint(hosts_api_v1_0.rest, url_prefix='/v1')
app.wsgi_app = request_id.BlazarReqIdMiddleware(app.wsgi_app)
app.wsgi_app = request_log.RequestLog(app.wsgi_app)
return app
def fake_computehost(**kw):
return {
'id': kw.get('id', '1'),
'hypervisor_hostname': kw.get('hypervisor_hostname', 'host01'),
'hypervisor_type': kw.get('hypervisor_type', 'QEMU'),
'vcpus': kw.get('vcpus', 1),
'hypervisor_version': kw.get('hypervisor_version', 1000000),
'trust_id': kw.get('trust_id',
'35b17138-b364-4e6a-a131-8f3099c5be68'),
'memory_mb': kw.get('memory_mb', 8192),
'local_gb': kw.get('local_gb', 50),
'cpu_info': kw.get('cpu_info',
"{\"vendor\": \"Intel\", \"model\": \"qemu32\", "
"\"arch\": \"x86_64\", \"features\": [],"
" \"topology\": {\"cores\": 1}}",
),
'extra_capas': kw.get('extra_capas',
{'vgpus': 2, 'fruits': 'bananas'})
}
def fake_computehost_request_body(include=None, **kw):
computehost_body = fake_computehost(**kw)
computehost_body['name'] = kw.get('name',
computehost_body['hypervisor_hostname'])
default_include = set(['name', 'extra_capas'])
include = include or set()
include |= default_include
return dict((key, computehost_body[key])
for key in computehost_body if key in include)
@ddt.ddt
class OsHostAPITestCase(tests.TestCase):
def setUp(self):
super(OsHostAPITestCase, self).setUp()
self.app = make_app()
self.headers = {'Accept': 'application/json',
'OpenStack-API-Version': 'reservation 1.0'}
self.host_id = str('1')
self.mock_ctx = self.patch(api_context, 'ctx_from_headers')
self.mock_ctx.return_value = context.BlazarContext(
user_id='fake', project_id='fake', roles=['member'])
self.get_computehosts = self.patch(service_api.API,
'get_computehosts')
self.create_computehost = self.patch(service_api.API,
'create_computehost')
self.get_computehost = self.patch(service_api.API, 'get_computehost')
self.update_computehost = self.patch(service_api.API,
'update_computehost')
self.delete_computehost = self.patch(service_api.API,
'delete_computehost')
self.list_allocations = self.patch(service_api.API,
'list_allocations')
self.get_allocations = self.patch(service_api.API, 'get_allocations')
self.list_resource_properties = self.patch(service_api.API,
'list_resource_properties')
self.update_resource_property = self.patch(service_api.API,
'update_resource_property')
def _assert_response(self, actual_resp, expected_status_code,
expected_resp_body, key='host',
expected_api_version='reservation 1.0'):
res_id = actual_resp.headers.get(id.HTTP_RESP_HEADER_REQUEST_ID)
api_version = actual_resp.headers.get(
api_version_request.API_VERSION_REQUEST_HEADER)
self.assertIn(id.HTTP_RESP_HEADER_REQUEST_ID,
actual_resp.headers)
self.assertIn(api_version_request.API_VERSION_REQUEST_HEADER,
actual_resp.headers)
self.assertIn(api_version_request.VARY_HEADER, actual_resp.headers)
self.assertThat(res_id, matchers.StartsWith('req-'))
self.assertEqual(expected_status_code, actual_resp.status_code)
self.assertEqual(expected_resp_body, actual_resp.get_json()[key])
self.assertEqual(expected_api_version, api_version)
self.assertEqual('OpenStack-API-Version', actual_resp.headers.get(
api_version_request.VARY_HEADER))
def test_list(self):
with self.app.test_client() as c:
self.get_computehosts.return_value = []
res = c.get('/v1', headers=self.headers)
self._assert_response(res, 200, [], key='hosts')
def test_list_with_non_acceptable_version(self):
headers = {'Accept': 'application/json',
'OpenStack-API-Version': 'reservation 1.2'}
with self.app.test_client() as c:
res = c.get('/v1', headers=headers)
self.assertEqual(406, res.status_code)
def test_create(self):
with self.app.test_client() as c:
self.create_computehost.return_value = fake_computehost(
id=self.host_id)
res = c.post('/v1', json=fake_computehost_request_body(
id=self.host_id), headers=self.headers)
self._assert_response(res, 201, fake_computehost(
id=self.host_id))
def test_create_with_bad_api_version(self):
headers = {'Accept': 'application/json',
'OpenStack-API-Version': 'reservation 1.a'}
with self.app.test_client() as c:
res = c.post('/v1', json=fake_computehost_request_body(
id=self.host_id), headers=headers)
self.assertEqual(400, res.status_code)
def test_get(self):
with self.app.test_client() as c:
self.get_computehost.return_value = fake_computehost(
id=self.host_id)
res = c.get('/v1/{0}'.format(self.host_id), headers=self.headers)
self._assert_response(res, 200, fake_computehost(id=self.host_id))
def test_get_with_latest_api_version(self):
headers = {'Accept': 'application/json',
'OpenStack-API-Version': 'reservation latest'}
with self.app.test_client() as c:
self.get_computehost.return_value = fake_computehost(
id=self.host_id)
res = c.get('/v1/{0}'.format(self.host_id), headers=headers)
self._assert_response(res, 200, fake_computehost(id=self.host_id),
expected_api_version='reservation 1.0')
def test_update(self):
headers = {'Accept': 'application/json'}
with self.app.test_client() as c:
self.fake_computehost = fake_computehost(id=self.host_id,
name='updated')
self.fake_computehost_body = fake_computehost_request_body(
id=self.host_id,
name='updated'
)
self.update_computehost.return_value = self.fake_computehost
res = c.put('/v1/{0}'.format(self.host_id),
json=self.fake_computehost_body, headers=headers)
self._assert_response(res, 200, self.fake_computehost, 'host')
def test_update_with_no_service_type_in_header(self):
headers = {'Accept': 'application/json',
'OpenStack-API-Version': '1.0'}
with self.app.test_client() as c:
self.fake_computehost = fake_computehost(id=self.host_id,
name='updated')
self.fake_computehost_body = fake_computehost_request_body(
id=self.host_id,
name='updated'
)
self.update_computehost.return_value = self.fake_computehost
res = c.put('/v1/{0}'.format(self.host_id),
json=self.fake_computehost_body, headers=headers)
self._assert_response(res, 200, self.fake_computehost, 'host')
def test_delete(self):
with self.app.test_client() as c:
self.get_computehosts.return_value = fake_computehost(
id=self.host_id)
res = c.delete('/v1/{0}'.format(self.host_id),
headers=self.headers)
res_id = res.headers.get(id.HTTP_RESP_HEADER_REQUEST_ID)
self.assertEqual(204, res.status_code)
self.assertIn(id.HTTP_RESP_HEADER_REQUEST_ID, res.headers)
self.assertThat(res_id, matchers.StartsWith('req-'))
def test_allocation_list(self):
with self.app.test_client() as c:
self.list_allocations.return_value = []
res = c.get('/v1/allocations', headers=self.headers)
self._assert_response(res, 200, [], key='allocations')
def test_allocation_get(self):
with self.app.test_client() as c:
self.get_allocations.return_value = {}
res = c.get('/v1/{0}/allocation'.format(self.host_id),
headers=self.headers)
self._assert_response(res, 200, {}, key='allocation')
@ddt.data({'lease_id': str(uuidutils.generate_uuid()),
'reservation_id': str(uuidutils.generate_uuid())})
def test_allocation_list_with_query_params(self, query_params):
with self.app.test_client() as c:
res = c.get('/v1/allocations?{0}'.format(query_params),
headers=self.headers)
self._assert_response(res, 200, {}, key='allocations')
@ddt.data({'lease_id': str(uuidutils.generate_uuid()),
'reservation_id': str(uuidutils.generate_uuid())})
def test_allocation_get_with_query_params(self, query_params):
with self.app.test_client() as c:
res = c.get('/v1/{0}/allocation?{1}'.format(
self.host_id, query_params), headers=self.headers)
self._assert_response(res, 200, {}, key='allocation')
def test_resource_properties_list(self):
with self.app.test_client() as c:
self.list_resource_properties.return_value = []
res = c.get('/v1/properties', headers=self.headers)
self._assert_response(res, 200, [], key='resource_properties')
def test_resource_property_update(self):
resource_property = 'fake_property'
resource_property_body = {'private': True}
with self.app.test_client() as c:
res = c.patch('/v1/properties/{0}'.format(resource_property),
json=resource_property_body,
headers=self.headers)
self._assert_response(res, 200, {}, 'resource_property')