Skip to content

Commit 5877208

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add support for cache clean and prune APIs"
2 parents 2d3562f + a96909d commit 5877208

5 files changed

Lines changed: 195 additions & 1 deletion

File tree

glanceclient/tests/unit/v2/test_cache.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,21 @@
7070
['http://node1.example', 'http://node2.example'],
7171
),
7272
},
73+
'/v2/cache/clean': {
74+
'POST': (
75+
{},
76+
'',
77+
),
78+
},
79+
'/v2/cache/prune': {
80+
'POST': (
81+
{},
82+
{
83+
'total_files_pruned': 5,
84+
'total_bytes_pruned': 104857600,
85+
},
86+
),
87+
},
7388
}
7489

7590

@@ -176,3 +191,31 @@ def test_list_cached_nodes_not_supported(self, mock_has_version):
176191
self.assertRaises(exc.HTTPNotImplemented,
177192
self.controller.list_cached_nodes,
178193
'3a4560a1-e585-443e-9b39-553b46ec92d1')
194+
195+
@mock.patch.object(common_utils, 'has_version')
196+
def test_cache_clean(self, mock_has_version):
197+
mock_has_version.return_value = True
198+
self.controller.clean()
199+
expect = [('POST', '/v2/cache/clean', {}, None)]
200+
self.assertEqual(expect, self.api.calls)
201+
202+
@mock.patch.object(common_utils, 'has_version')
203+
def test_cache_prune(self, mock_has_version):
204+
mock_has_version.return_value = True
205+
result = self.controller.prune()
206+
expect = [('POST', '/v2/cache/prune', {}, None)]
207+
self.assertEqual(expect, self.api.calls)
208+
self.assertEqual(5, result['total_files_pruned'])
209+
self.assertEqual(104857600, result['total_bytes_pruned'])
210+
211+
@mock.patch.object(common_utils, 'has_version')
212+
def test_cache_clean_not_supported(self, mock_has_version):
213+
mock_has_version.return_value = False
214+
self.assertRaises(exc.HTTPNotImplemented,
215+
self.controller.clean)
216+
217+
@mock.patch.object(common_utils, 'has_version')
218+
def test_cache_prune_not_supported(self, mock_has_version):
219+
mock_has_version.return_value = False
220+
self.assertRaises(exc.HTTPNotImplemented,
221+
self.controller.prune)

glanceclient/tests/unit/v2/test_shell_v2.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4031,3 +4031,91 @@ def test_do_cache_clear_endpoint_not_provided(self):
40314031
mock_exit.assert_called_once_with(
40324032
'Direct server endpoint needs to be provided. Do '
40334033
'not use loadbalanced or catalog endpoints.')
4034+
4035+
def _test_cache_clean(self, supported=True, forbidden=False):
4036+
args = self._make_args({})
4037+
with mock.patch.object(self.gc.cache, 'clean') as mocked_cache_clean:
4038+
if supported:
4039+
mocked_cache_clean.return_value = None
4040+
else:
4041+
mocked_cache_clean.side_effect = exc.HTTPNotImplemented
4042+
if forbidden:
4043+
mocked_cache_clean.side_effect = exc.HTTPForbidden
4044+
4045+
test_shell.do_cache_clean(self.gc, args)
4046+
if supported:
4047+
mocked_cache_clean.assert_called_once_with()
4048+
4049+
def test_do_cache_clean(self):
4050+
self._test_cache_clean()
4051+
4052+
def test_do_cache_clean_unsupported(self):
4053+
with mock.patch(
4054+
'glanceclient.common.utils.print_err') as mock_print_err:
4055+
self._test_cache_clean(supported=False)
4056+
mock_print_err.assert_called_once_with(
4057+
"'HTTP HTTPNotImplemented': Unable to clean the image cache.")
4058+
4059+
def test_do_cache_clean_forbidden(self):
4060+
with mock.patch(
4061+
'glanceclient.common.utils.print_err') as mock_print_err:
4062+
self._test_cache_clean(forbidden=True)
4063+
mock_print_err.assert_called_once_with(
4064+
"You are not permitted to clean the image cache.")
4065+
4066+
def test_do_cache_clean_endpoint_not_provided(self):
4067+
args = self._make_args({})
4068+
self.gc.endpoint_provided = False
4069+
with mock.patch('glanceclient.common.utils.exit') as mock_exit:
4070+
test_shell.do_cache_clean(self.gc, args)
4071+
mock_exit.assert_called_once_with(
4072+
'Direct server endpoint needs to be provided. Do '
4073+
'not use loadbalanced or catalog endpoints.')
4074+
4075+
def _test_cache_prune(self, supported=True, forbidden=False):
4076+
args = self._make_args({})
4077+
with mock.patch.object(self.gc.cache, 'prune') as mocked_cache_prune:
4078+
if supported:
4079+
mocked_cache_prune.return_value = {
4080+
'total_files_pruned': 5,
4081+
'total_bytes_pruned': 104857600,
4082+
}
4083+
else:
4084+
mocked_cache_prune.side_effect = exc.HTTPNotImplemented
4085+
if forbidden:
4086+
mocked_cache_prune.side_effect = exc.HTTPForbidden
4087+
4088+
with mock.patch('builtins.print') as mock_print:
4089+
test_shell.do_cache_prune(self.gc, args)
4090+
if supported and not forbidden:
4091+
mocked_cache_prune.assert_called_once_with()
4092+
mock_print.assert_called_once_with(
4093+
'Pruned 5 file(s), 104857600 byte(s).')
4094+
4095+
def test_do_cache_prune(self):
4096+
self._test_cache_prune()
4097+
4098+
def test_do_cache_prune_unsupported(self):
4099+
with mock.patch(
4100+
'glanceclient.common.utils.print_err') as mock_print_err:
4101+
self._test_cache_prune(supported=False)
4102+
mock_print_err.assert_called_once_with(
4103+
"'HTTP HTTPNotImplemented': Unable to prune the image cache.")
4104+
4105+
def test_do_cache_prune_forbidden(self):
4106+
with mock.patch(
4107+
'glanceclient.common.utils.print_err') as mock_print_err:
4108+
self._test_cache_prune(forbidden=True)
4109+
mock_print_err.assert_called_once_with(
4110+
"You are not permitted to prune the image cache.")
4111+
4112+
def test_do_cache_prune_endpoint_not_provided(self):
4113+
args = self._make_args({})
4114+
self.gc.endpoint_provided = False
4115+
with mock.patch('glanceclient.common.utils.exit') as mock_exit:
4116+
mock_exit.side_effect = SystemExit
4117+
self.assertRaises(SystemExit,
4118+
test_shell.do_cache_prune, self.gc, args)
4119+
mock_exit.assert_called_once_with(
4120+
'Direct server endpoint needs to be provided. Do not use '
4121+
'loadbalanced or catalog endpoints.')

glanceclient/v2/cache.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def is_supported(self, version):
2828
return True
2929
else:
3030
raise exc.HTTPNotImplemented(
31-
'Glance does not support image caching API (v2.14)')
31+
'Glance does not support image caching API (%s)' % version)
3232

3333
@utils.add_req_id_to_object()
3434
def list(self):
@@ -67,3 +67,17 @@ def list_cached_nodes(self, image_id):
6767
url = '/v2/cache/nodes/%s' % image_id
6868
resp, body = self.http_client.get(url)
6969
return body, resp
70+
71+
@utils.add_req_id_to_object()
72+
def clean(self):
73+
if self.is_supported('v2.18'):
74+
url = '/v2/cache/clean'
75+
resp, body = self.http_client.post(url)
76+
return body, resp
77+
78+
@utils.add_req_id_to_object()
79+
def prune(self):
80+
if self.is_supported('v2.18'):
81+
url = '/v2/cache/prune'
82+
resp, body = self.http_client.post(url)
83+
return body, resp

glanceclient/v2/shell.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,41 @@ def do_cache_delete(gc, args):
16431643
utils.print_err(msg)
16441644

16451645

1646+
def do_cache_clean(gc, args):
1647+
"""Clean invalid and stalled cached images."""
1648+
if not gc.endpoint_provided:
1649+
utils.exit("Direct server endpoint needs to be provided. Do not use "
1650+
"loadbalanced or catalog endpoints.")
1651+
try:
1652+
gc.cache.clean()
1653+
except exc.HTTPForbidden:
1654+
msg = _("You are not permitted to clean the image cache.")
1655+
utils.print_err(msg)
1656+
except exc.HTTPException as e:
1657+
msg = _("'%s': Unable to clean the image cache." % e)
1658+
utils.print_err(msg)
1659+
1660+
1661+
def do_cache_prune(gc, args):
1662+
"""Prune cached images to reduce cache size."""
1663+
if not gc.endpoint_provided:
1664+
utils.exit("Direct server endpoint needs to be provided. Do not use "
1665+
"loadbalanced or catalog endpoints.")
1666+
try:
1667+
result = gc.cache.prune()
1668+
if result:
1669+
print(_("Pruned %(files)d file(s), %(bytes)d byte(s).") % {
1670+
'files': result.get('total_files_pruned', 0),
1671+
'bytes': result.get('total_bytes_pruned', 0),
1672+
})
1673+
except exc.HTTPForbidden:
1674+
msg = _("You are not permitted to prune the image cache.")
1675+
utils.print_err(msg)
1676+
except exc.HTTPException as e:
1677+
msg = _("'%s': Unable to prune the image cache." % e)
1678+
utils.print_err(msg)
1679+
1680+
16461681
def do_cache_list(gc, args):
16471682
"""Get cache state."""
16481683
if not gc.endpoint_provided:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
features:
3+
- |
4+
Client support has been added for the Glance Image API v2.18 cache
5+
maintenance endpoints. The following commands have been added to the
6+
command line interface:
7+
8+
* ``cache-clean`` - Clean invalid cache entries and stalled incomplete
9+
images
10+
* ``cache-prune`` - Prune cached images when the cache size exceeds the
11+
maximum configured size
12+
13+
These commands require a direct glance-api server endpoint and are only
14+
available when image caching is enabled on the Glance service.

0 commit comments

Comments
 (0)