Skip to content

Commit 71c29a1

Browse files
committed
Add aggregate-cache-images command and client routines
This adds the ability to request image precache support for an aggregate in support of the matching server feature. Related to blueprint image-precache-support Depends-On: https://review.opendev.org/#/c/687140 Change-Id: Id354ccfa99e500a598685e6b794c12160ea2a990
1 parent e1bb437 commit 71c29a1

File tree

9 files changed

+144
-1
lines changed

9 files changed

+144
-1
lines changed

doc/source/cli/nova.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ nova usage
7373
``aggregate-add-host``
7474
Add the host to the specified aggregate.
7575

76+
``aggregate-cache-images``
77+
Request images be pre-cached on hosts within an aggregate.
78+
7679
``aggregate-create``
7780
Create a new aggregate with the specified
7881
details.
@@ -756,6 +759,25 @@ Add the host to the specified aggregate.
756759
``<host>``
757760
The host to add to the aggregate.
758761

762+
.. _nova_aggregate-cache-images:
763+
764+
nova aggregate-cache-images
765+
---------------------------
766+
767+
.. code-block:: console
768+
769+
usage: nova aggregate-cache-images <aggregate> <image> [<image> ..]
770+
771+
Request image(s) be pre-cached on hosts within the aggregate.
772+
773+
**Positional arguments:**
774+
775+
``<aggregate>``
776+
Name or ID of aggregate.
777+
778+
``<image>``
779+
Name or ID of image(s) to cache.
780+
759781
.. _nova_aggregate-create:
760782

761783
nova aggregate-create

novaclient/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@
2525
# when client supported the max version, and bumped sequentially, otherwise
2626
# the client may break due to server side new version may include some
2727
# backward incompatible change.
28-
API_MAX_VERSION = api_versions.APIVersion("2.80")
28+
API_MAX_VERSION = api_versions.APIVersion("2.81")

novaclient/tests/unit/fixture_data/aggregates.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,10 @@ def setUp(self):
5151

5252
self.requests_mock.delete(self.url(1), status_code=202,
5353
headers=self.json_headers)
54+
55+
self.requests_mock.register_uri('POST', self.url(1),
56+
json={},
57+
headers=self.json_headers)
58+
self.requests_mock.post(self.url(1, 'images'),
59+
json={},
60+
headers=self.json_headers)

novaclient/tests/unit/v2/fakes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,9 @@ def post_os_aggregates_3_action(self, body, **kw):
17341734
def delete_os_aggregates_1(self, **kw):
17351735
return (202, {}, None)
17361736

1737+
def post_os_aggregates_1_images(self, body, **kw):
1738+
return (202, {}, None)
1739+
17371740
#
17381741
# Services
17391742
#

novaclient/tests/unit/v2/test_aggregates.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313
# License for the specific language governing permissions and limitations
1414
# under the License.
1515

16+
from novaclient import api_versions
17+
from novaclient import exceptions
1618
from novaclient.tests.unit.fixture_data import aggregates as data
1719
from novaclient.tests.unit.fixture_data import client
1820
from novaclient.tests.unit import utils
1921
from novaclient.tests.unit.v2 import fakes
2022
from novaclient.v2 import aggregates
23+
from novaclient.v2 import images
2124

2225

2326
class AggregatesTest(utils.FixturedTestCase):
@@ -161,3 +164,40 @@ def test_delete_aggregate(self):
161164
result3 = self.cs.aggregates.delete(aggregate)
162165
self.assert_request_id(result3, fakes.FAKE_REQUEST_ID_LIST)
163166
self.assert_called('DELETE', '/os-aggregates/1')
167+
168+
169+
class AggregatesV281Test(utils.FixturedTestCase):
170+
api_version = "2.81"
171+
data_fixture_class = data.Fixture
172+
173+
scenarios = [('original', {'client_fixture_class': client.V1}),
174+
('session', {'client_fixture_class': client.SessionV1})]
175+
176+
def setUp(self):
177+
super(AggregatesV281Test, self).setUp()
178+
self.cs.api_version = api_versions.APIVersion(self.api_version)
179+
180+
def test_cache_images(self):
181+
aggregate = self.cs.aggregates.list()[0]
182+
_images = [images.Image(self.cs.aggregates, {'id': '1'}),
183+
images.Image(self.cs.aggregates, {'id': '2'})]
184+
aggregate.cache_images(_images)
185+
expected_body = {'cache': [{'id': image.id}
186+
for image in _images]}
187+
self.assert_called('POST', '/os-aggregates/1/images',
188+
expected_body)
189+
190+
def test_cache_images_just_ids(self):
191+
aggregate = self.cs.aggregates.list()[0]
192+
_images = ['1']
193+
aggregate.cache_images(_images)
194+
expected_body = {'cache': [{'id': '1'}]}
195+
self.assert_called('POST', '/os-aggregates/1/images',
196+
expected_body)
197+
198+
def test_cache_images_pre281(self):
199+
self.cs.api_version = api_versions.APIVersion('2.80')
200+
aggregate = self.cs.aggregates.list()[0]
201+
_images = [images.Image(self.cs.aggregates, {'id': '1'})]
202+
self.assertRaises(exceptions.VersionNotFoundForAPIMethod,
203+
aggregate.cache_images, _images)

novaclient/tests/unit/v2/test_shell.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2884,6 +2884,30 @@ def test_aggregate_show_by_name(self):
28842884
self.run_command('aggregate-show test')
28852885
self.assert_called('GET', '/os-aggregates')
28862886

2887+
def test_aggregate_cache_images(self):
2888+
self.run_command(
2889+
'aggregate-cache-images 1 %s %s' % (
2890+
FAKE_UUID_1, FAKE_UUID_2),
2891+
api_version='2.81')
2892+
body = {
2893+
'cache': [{'id': FAKE_UUID_1},
2894+
{'id': FAKE_UUID_2}],
2895+
}
2896+
self.assert_called('POST', '/os-aggregates/1/images', body)
2897+
2898+
def test_aggregate_cache_images_no_images(self):
2899+
self.assertRaises(SystemExit,
2900+
self.run_command,
2901+
'aggregate-cache-images 1',
2902+
api_version='2.81')
2903+
2904+
def test_aggregate_cache_images_pre281(self):
2905+
self.assertRaises(SystemExit,
2906+
self.run_command,
2907+
'aggregate-cache-images 1 %s %s' % (
2908+
FAKE_UUID_1, FAKE_UUID_2),
2909+
api_version='2.80')
2910+
28872911
def test_live_migration(self):
28882912
self.run_command('live-migration sample-server hostname')
28892913
self.assert_called('POST', '/servers/1234/action',

novaclient/v2/aggregates.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
"""Aggregate interface."""
1717

18+
from novaclient import api_versions
1819
from novaclient import base
1920

2021

@@ -45,6 +46,10 @@ def delete(self):
4546
"""
4647
return self.manager.delete(self)
4748

49+
@api_versions.wraps("2.81")
50+
def cache_images(self, images):
51+
return self.manager.cache_images(self, images)
52+
4853

4954
class AggregateManager(base.ManagerWithFind):
5055
resource_class = Aggregate
@@ -103,3 +108,20 @@ def delete(self, aggregate):
103108
:returns: An instance of novaclient.base.TupleWithMeta
104109
"""
105110
return self._delete('/os-aggregates/%s' % (base.getid(aggregate)))
111+
112+
@api_versions.wraps("2.81")
113+
def cache_images(self, aggregate, images):
114+
"""
115+
Request images be cached on a given aggregate.
116+
117+
:param aggregate: The aggregate to target
118+
:param images: A list of image IDs to request caching
119+
:returns: An instance of novaclient.base.TupleWithMeta
120+
"""
121+
body = {
122+
'cache': [{'id': base.getid(image)} for image in images],
123+
}
124+
resp, body = self.api.client.post(
125+
"/os-aggregates/%s/images" % base.getid(aggregate),
126+
body=body)
127+
return self.convert_into_with_meta(body, resp)

novaclient/v2/shell.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3526,6 +3526,21 @@ def parser_hosts(fields):
35263526
utils.print_list([aggregate], columns, formatters=formatters)
35273527

35283528

3529+
@api_versions.wraps("2.81")
3530+
@utils.arg(
3531+
'aggregate', metavar='<aggregate>',
3532+
help=_('Name or ID of the aggregate.'))
3533+
@utils.arg(
3534+
'images', metavar='<image>', nargs='+',
3535+
help=_('Name or ID of image(s) to cache on the hosts within '
3536+
'the aggregate.'))
3537+
def do_aggregate_cache_images(cs, args):
3538+
"""Request images be cached."""
3539+
aggregate = _find_aggregate(cs, args.aggregate)
3540+
images = _find_images(cs, args.images)
3541+
cs.aggregates.cache_images(aggregate, images)
3542+
3543+
35293544
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
35303545
@utils.arg(
35313546
'host', metavar='<host>', default=None, nargs='?',
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
features:
3+
- |
4+
Added support for `microversion 2.81`_ which adds image pre-caching support by
5+
aggregate.
6+
7+
- The ``aggregate-cache-images`` command is added to the CLI
8+
- The ``cache_images()`` method is added to the python API binding
9+
10+
.. _microversion 2.81: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id73

0 commit comments

Comments
 (0)