diff --git a/api-ref/source/hosts.inc b/api-ref/source/hosts.inc index c4ecafa99..4a3696316 100644 --- a/api-ref/source/hosts.inc +++ b/api-ref/source/hosts.inc @@ -43,3 +43,44 @@ Response Example .. literalinclude:: samples/host-get-all-resp.json :language: javascript + +Show details of a host +=========================== + +.. rest_method:: GET /v1/hosts/{host_ident} + +Get all information of a host in Zun. + +Response Codes +-------------- + +.. rest_status_code:: success status.yaml + + - 200 + +.. rest_status_code:: error status.yaml + + - 401 + - 403 + - 404 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - host_ident: host_ident + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - uuid: uuid + - hostname: hostname + +Response Example +---------------- + +.. literalinclude:: samples/host-get-resp.json + :language: javascript diff --git a/api-ref/source/samples/host-get-resp.json b/api-ref/source/samples/host-get-resp.json new file mode 100644 index 000000000..75f749080 --- /dev/null +++ b/api-ref/source/samples/host-get-resp.json @@ -0,0 +1,19 @@ +{ + "hostname": "test", + "uuid": "d0405f06-101e-4340-8735-d1bf9fa8b8ad", + "links": [{ + "href": "http://192.168.2.22:9517/v1/hosts/d0405f06-101e-4340-8735-d1bf9fa8b8ad", + "rel": "self" + }, + {"href": "http://192.168.2.22:9517/hosts/d0405f06-101e-4340-8735-d1bf9fa8b8ad", + "rel": "bookmark"} + ], + "kernel_version": "3.10.0-123.el7.x86_64", + "labels": {"type": "test"}, + "cpus": 48, + "mem_total": 128446, + "total_containers": 3, + "os_type": "linux", + "os": "CentOS Linux 7 (Core)", + "architecture": "x86_64" +} diff --git a/etc/zun/policy.json b/etc/zun/policy.json index c24e1d842..1d056acbe 100644 --- a/etc/zun/policy.json +++ b/etc/zun/policy.json @@ -40,5 +40,6 @@ "zun-service:force_down": "rule:admin_api", "zun-service:get_all": "rule:admin_api", - "host:get_all": "rule:admin_api" + "host:get_all": "rule:admin_api", + "host:get": "rule:admin_api" } diff --git a/zun/api/controllers/v1/hosts.py b/zun/api/controllers/v1/hosts.py index cafce6050..13f7d7b11 100644 --- a/zun/api/controllers/v1/hosts.py +++ b/zun/api/controllers/v1/hosts.py @@ -11,6 +11,7 @@ # under the License. from oslo_log import log as logging +from oslo_utils import uuidutils import pecan from zun.api.controllers import base @@ -24,6 +25,25 @@ from zun import objects LOG = logging.getLogger(__name__) +def _get_host(host_id): + host = None + if uuidutils.is_uuid_like(host_id): + host = objects.ComputeNode.get_by_uuid(pecan.request.context, host_id) + else: + host = objects.ComputeNode.get_by_hostname(pecan.request.context, + host_id) + if not host: + pecan.abort(404, ('Not found; the host you requested ' + 'does not exist.')) + + return host + + +def check_policy_on_host(host, action): + context = pecan.request.context + policy.enforce(context, action, host, action=action) + + class HostCollection(collection.Collection): """API representation of a collection of hosts.""" @@ -83,3 +103,16 @@ class HostController(base.Controller): expand=expand, sort_key=sort_key, sort_dir=sort_dir) + + @pecan.expose('json') + @base.Controller.api_version("1.4") + @exception.wrap_pecan_controller_exception + def get_one(self, host_id): + """Retrieve information about the given host. + + :param host_ident: UUID or name of a host. + """ + context = pecan.request.context + policy.enforce(context, "host:get", action="host:get") + host = _get_host(host_id) + return view.format_host(pecan.request.host_url, host) diff --git a/zun/api/controllers/v1/views/hosts_view.py b/zun/api/controllers/v1/views/hosts_view.py index 36032bd40..ad245f4d5 100644 --- a/zun/api/controllers/v1/views/hosts_view.py +++ b/zun/api/controllers/v1/views/hosts_view.py @@ -21,7 +21,11 @@ _basic_keys = ( 'hostname', 'mem_total', 'cpus', + 'total_containers', + 'architecture', + 'os_type', 'os', + 'kernel_version', 'labels' ) diff --git a/zun/api/controllers/versions.py b/zun/api/controllers/versions.py index 4f06a7c37..6745ab8f3 100644 --- a/zun/api/controllers/versions.py +++ b/zun/api/controllers/versions.py @@ -36,7 +36,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: * 1.1 - Initial version * 1.2 - Support user specify pre created networks * 1.3 - Add auto_remove to container - * 1.4 - Support list all container host + * 1.4 - Support list all container host and show a container host """ BASE_VER = '1.1' diff --git a/zun/api/rest_api_version_history.rst b/zun/api/rest_api_version_history.rst index 045ddcc75..7ab297980 100644 --- a/zun/api/rest_api_version_history.rst +++ b/zun/api/rest_api_version_history.rst @@ -48,3 +48,5 @@ user documentation. Add host list api. Users can use this api to list all the zun compute hosts. + Add get host api + Users can use this api to get details of a zun compute host. diff --git a/zun/tests/unit/api/controllers/v1/test_hosts.py b/zun/tests/unit/api/controllers/v1/test_hosts.py index 4374908be..0fc722a9c 100644 --- a/zun/tests/unit/api/controllers/v1/test_hosts.py +++ b/zun/tests/unit/api/controllers/v1/test_hosts.py @@ -33,7 +33,7 @@ class TestHostController(api_base.FunctionalTest): extra_environ = {'HTTP_ACCEPT': 'application/json'} headers = {'OpenStack-API-Version': 'container 1.4'} - response = self.app.get('/v1/hosts/', extra_environ=extra_environ, + response = self.app.get('/v1/hosts', extra_environ=extra_environ, headers=headers) mock_host_list.assert_called_once_with(mock.ANY, @@ -59,7 +59,7 @@ class TestHostController(api_base.FunctionalTest): mock_host_list.return_value = host_list[-1:] extra_environ = {'HTTP_ACCEPT': 'application/json'} headers = {'OpenStack-API-Version': 'container 1.4'} - response = self.app.get('/v1/hosts/?limit=3&marker=%s' + response = self.app.get('/v1/hosts?limit=3&marker=%s' % host_list[2].uuid, extra_environ=extra_environ, headers=headers) @@ -69,6 +69,25 @@ class TestHostController(api_base.FunctionalTest): self.assertEqual(host_list[-1].uuid, actual_hosts[0].get('uuid')) + @patch('zun.objects.ComputeNode.get_by_uuid') + def test_get_one_host(self, mock_get_by_uuid): + test_host = utils.get_test_compute_node() + numat = numa.NUMATopology._from_dict(test_host['numa_topology']) + test_host['numa_topology'] = numat + test_host_obj = objects.ComputeNode(self.context, **test_host) + mock_get_by_uuid.return_value = test_host_obj + extra_environ = {'HTTP_ACCEPT': 'application/json'} + headers = {'OpenStack-API-Version': 'container 1.4'} + response = self.app.get('/v1/hosts/%s' % test_host['uuid'], + extra_environ=extra_environ, + headers=headers) + mock_get_by_uuid.assert_called_once_with( + mock.ANY, + test_host['uuid']) + self.assertEqual(200, response.status_int) + self.assertEqual(test_host['uuid'], + response.json['uuid']) + class TestHostEnforcement(api_base.FunctionalTest): @@ -85,5 +104,12 @@ class TestHostEnforcement(api_base.FunctionalTest): extra_environ = {'HTTP_ACCEPT': 'application/json'} headers = {'OpenStack-API-Version': 'container 1.4'} self._common_policy_check( - 'host:get_all', self.get_json, '/hosts/', + 'host:get_all', self.get_json, '/hosts', + expect_errors=True, extra_environ=extra_environ, headers=headers) + + def test_pollicy_disallow_get_one(self): + extra_environ = {'HTTP_ACCEPT': 'application/json'} + headers = {'OpenStack-API-Version': 'container 1.4'} + self._common_policy_check( + 'host:get', self.get_json, '/hosts/%s' % '12345678', expect_errors=True, extra_environ=extra_environ, headers=headers)