Merge "Server support for display snapshot of zun stats"
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
"container:top": "rule:admin_or_user",
|
"container:top": "rule:admin_or_user",
|
||||||
"container:get_archive": "rule:admin_or_user",
|
"container:get_archive": "rule:admin_or_user",
|
||||||
"container:put_archive": "rule:admin_or_user",
|
"container:put_archive": "rule:admin_or_user",
|
||||||
|
"container:stats": "rule:admin_or_user",
|
||||||
|
|
||||||
"image:pull": "rule:default",
|
"image:pull": "rule:default",
|
||||||
"image:get_all": "rule:default",
|
"image:get_all": "rule:default",
|
||||||
|
@@ -93,6 +93,7 @@ class ContainersController(base.Controller):
|
|||||||
'top': ['GET'],
|
'top': ['GET'],
|
||||||
'get_archive': ['GET'],
|
'get_archive': ['GET'],
|
||||||
'put_archive': ['POST'],
|
'put_archive': ['POST'],
|
||||||
|
'stats': ['GET'],
|
||||||
}
|
}
|
||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
@@ -528,3 +529,15 @@ class ContainersController(base.Controller):
|
|||||||
compute_api = pecan.request.compute_api
|
compute_api = pecan.request.compute_api
|
||||||
compute_api.container_put_archive(context, container,
|
compute_api.container_put_archive(context, container,
|
||||||
kw['path'], kw['data'])
|
kw['path'], kw['data'])
|
||||||
|
|
||||||
|
@pecan.expose('json')
|
||||||
|
@exception.wrap_pecan_controller_exception
|
||||||
|
def stats(self, container_id):
|
||||||
|
container = _get_container(container_id)
|
||||||
|
check_policy_on_container(container.as_dict(), "container:stats")
|
||||||
|
utils.validate_container_state(container, 'stats')
|
||||||
|
LOG.debug('Calling compute.container_stats with %s'
|
||||||
|
% (container.uuid))
|
||||||
|
context = pecan.request.context
|
||||||
|
compute_api = pecan.request.compute_api
|
||||||
|
return compute_api.container_stats(context, container)
|
||||||
|
@@ -57,6 +57,7 @@ VALID_STATES = {
|
|||||||
consts.STOPPED],
|
consts.STOPPED],
|
||||||
'logs': [consts.CREATED, consts.ERROR, consts.PAUSED, consts.RUNNING,
|
'logs': [consts.CREATED, consts.ERROR, consts.PAUSED, consts.RUNNING,
|
||||||
consts.STOPPED, consts.UNKNOWN],
|
consts.STOPPED, consts.UNKNOWN],
|
||||||
|
'stats': [consts.RUNNING],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -109,6 +109,9 @@ class API(object):
|
|||||||
def container_put_archive(self, context, container, *args):
|
def container_put_archive(self, context, container, *args):
|
||||||
return self.rpcapi.container_put_archive(context, container, *args)
|
return self.rpcapi.container_put_archive(context, container, *args)
|
||||||
|
|
||||||
|
def container_stats(self, context, container):
|
||||||
|
return self.rpcapi.container_stats(context, container)
|
||||||
|
|
||||||
def image_pull(self, context, image, *args):
|
def image_pull(self, context, image, *args):
|
||||||
return self.rpcapi.image_pull(context, image, *args)
|
return self.rpcapi.image_pull(context, image, *args)
|
||||||
|
|
||||||
|
@@ -462,6 +462,19 @@ class Manager(object):
|
|||||||
LOG.exception("Unexpected exception: %s", six.text_type(e))
|
LOG.exception("Unexpected exception: %s", six.text_type(e))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@translate_exception
|
||||||
|
def container_stats(self, context, container):
|
||||||
|
LOG.debug('Displaying stats of the container: %s', container.uuid)
|
||||||
|
try:
|
||||||
|
return self.driver.stats(container)
|
||||||
|
except exception.DockerError as e:
|
||||||
|
LOG.error("Error occurred while calling Docker stats API: %s",
|
||||||
|
six.text_type(e))
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception("Unexpected exception: %s", six.text_type(e))
|
||||||
|
raise
|
||||||
|
|
||||||
def image_pull(self, context, image):
|
def image_pull(self, context, image):
|
||||||
utils.spawn_n(self._do_image_pull, context, image)
|
utils.spawn_n(self._do_image_pull, context, image)
|
||||||
|
|
||||||
|
@@ -141,6 +141,11 @@ class API(rpc_service.API):
|
|||||||
return self._call(container.host, 'container_put_archive',
|
return self._call(container.host, 'container_put_archive',
|
||||||
container=container, path=path, data=data)
|
container=container, path=path, data=data)
|
||||||
|
|
||||||
|
@check_container_host
|
||||||
|
def container_stats(self, context, container):
|
||||||
|
return self._call(container.host, 'container_stats',
|
||||||
|
container=container)
|
||||||
|
|
||||||
def image_pull(self, context, image):
|
def image_pull(self, context, image):
|
||||||
# NOTE(hongbin): Image API doesn't support multiple compute nodes
|
# NOTE(hongbin): Image API doesn't support multiple compute nodes
|
||||||
# scenario yet, so we temporarily set host to None and rpc will
|
# scenario yet, so we temporarily set host to None and rpc will
|
||||||
|
@@ -463,6 +463,12 @@ class DockerDriver(driver.ContainerDriver):
|
|||||||
with docker_utils.docker_client() as docker:
|
with docker_utils.docker_client() as docker:
|
||||||
docker.put_archive(container.container_id, path, data)
|
docker.put_archive(container.container_id, path, data)
|
||||||
|
|
||||||
|
@check_container_id
|
||||||
|
def stats(self, container):
|
||||||
|
with docker_utils.docker_client() as docker:
|
||||||
|
return docker.stats(container.container_id, decode=False,
|
||||||
|
stream=False)
|
||||||
|
|
||||||
def _encode_utf8(self, value):
|
def _encode_utf8(self, value):
|
||||||
if six.PY2 and not isinstance(value, unicode):
|
if six.PY2 and not isinstance(value, unicode):
|
||||||
value = unicode(value)
|
value = unicode(value)
|
||||||
|
@@ -141,6 +141,10 @@ class ContainerDriver(object):
|
|||||||
"""copy resource to a container."""
|
"""copy resource to a container."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def stats(self, container):
|
||||||
|
"""Display stats of the container(s)."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
def create_sandbox(self, context, container, **kwargs):
|
def create_sandbox(self, context, container, **kwargs):
|
||||||
"""Create a sandbox."""
|
"""Create a sandbox."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
@@ -1197,6 +1197,23 @@ class TestContainerController(api_base.FunctionalTest):
|
|||||||
self.app.post('/v1/containers/%s/%s/' % (test_object.uuid,
|
self.app.post('/v1/containers/%s/%s/' % (test_object.uuid,
|
||||||
'put_archive'))
|
'put_archive'))
|
||||||
|
|
||||||
|
@patch('zun.common.utils.validate_container_state')
|
||||||
|
@patch('zun.compute.api.API.container_stats')
|
||||||
|
@patch('zun.objects.Container.get_by_uuid')
|
||||||
|
def test_stats_container_by_uuid(self, mock_get_by_uuid,
|
||||||
|
mock_container_stats, mock_validate):
|
||||||
|
mock_container_stats.return_value = ""
|
||||||
|
test_container = utils.get_test_container()
|
||||||
|
test_container_obj = objects.Container(self.context, **test_container)
|
||||||
|
mock_get_by_uuid.return_value = test_container_obj
|
||||||
|
|
||||||
|
container_uuid = test_container.get('uuid')
|
||||||
|
url = '/v1/containers/%s/stats'\
|
||||||
|
% container_uuid
|
||||||
|
response = self.app.get(url)
|
||||||
|
self.assertEqual(200, response.status_int)
|
||||||
|
self.assertTrue(mock_container_stats.called)
|
||||||
|
|
||||||
|
|
||||||
class TestContainerEnforcement(api_base.FunctionalTest):
|
class TestContainerEnforcement(api_base.FunctionalTest):
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user