Deprecate image list/show/delete/update CLIs/APIs
This deprecates all of the image CLIs/python API bindings that use the Nova os-images API which is a proxy to the Glance v1 API. This will emit a warning each time a deprecated CLI/API is used and also updates the help docs for the deprecated CLIs and docstrings for APIs. The plan is to do a release once this is merged so people start seeing it and then we'll actually remove the deprecated CLIs/APIs in the first python-novaclient release after the Nova server 15.0.0 'O' release. Depends-On: Iff5fb3180855de7adb3399f6be16bedc8543b4ec Change-Id: I3f60cc7f4c6e27861c4a84b925d573f35f1a1848
This commit is contained in:
parent
77f214bdbd
commit
a602e59806
@ -88,7 +88,8 @@ class SimpleReadOnlyNovaClientTest(base.ClientTestBase):
|
|||||||
self.nova('hypervisor-list')
|
self.nova('hypervisor-list')
|
||||||
|
|
||||||
def test_admin_image_list(self):
|
def test_admin_image_list(self):
|
||||||
self.nova('image-list')
|
out = self.nova('image-list', merge_stderr=True)
|
||||||
|
self.assertIn('Command image-list is deprecated', out)
|
||||||
|
|
||||||
@decorators.skip_because(bug="1157349")
|
@decorators.skip_because(bug="1157349")
|
||||||
def test_admin_interface_list(self):
|
def test_admin_interface_list(self):
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
from novaclient.tests.unit.fixture_data import client
|
from novaclient.tests.unit.fixture_data import client
|
||||||
from novaclient.tests.unit.fixture_data import images as data
|
from novaclient.tests.unit.fixture_data import images as data
|
||||||
from novaclient.tests.unit import utils
|
from novaclient.tests.unit import utils
|
||||||
@ -23,13 +27,15 @@ class ImagesTest(utils.FixturedTestCase):
|
|||||||
client_fixture_class = client.V1
|
client_fixture_class = client.V1
|
||||||
data_fixture_class = data.V1
|
data_fixture_class = data.V1
|
||||||
|
|
||||||
def test_list_images(self):
|
@mock.patch.object(warnings, 'warn')
|
||||||
|
def test_list_images(self, mock_warn):
|
||||||
il = self.cs.images.list()
|
il = self.cs.images.list()
|
||||||
self.assert_request_id(il, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(il, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('GET', '/images/detail')
|
self.assert_called('GET', '/images/detail')
|
||||||
for i in il:
|
for i in il:
|
||||||
self.assertIsInstance(i, images.Image)
|
self.assertIsInstance(i, images.Image)
|
||||||
self.assertEqual(2, len(il))
|
self.assertEqual(2, len(il))
|
||||||
|
self.assertEqual(1, mock_warn.call_count)
|
||||||
|
|
||||||
def test_list_images_undetailed(self):
|
def test_list_images_undetailed(self):
|
||||||
il = self.cs.images.list(detailed=False)
|
il = self.cs.images.list(detailed=False)
|
||||||
@ -43,35 +49,48 @@ class ImagesTest(utils.FixturedTestCase):
|
|||||||
self.assert_request_id(il, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(il, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('GET', '/images/detail?limit=4&marker=1234')
|
self.assert_called('GET', '/images/detail?limit=4&marker=1234')
|
||||||
|
|
||||||
def test_get_image_details(self):
|
@mock.patch.object(warnings, 'warn')
|
||||||
|
def test_get_image_details(self, mock_warn):
|
||||||
i = self.cs.images.get(1)
|
i = self.cs.images.get(1)
|
||||||
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('GET', '/images/1')
|
self.assert_called('GET', '/images/1')
|
||||||
self.assertIsInstance(i, images.Image)
|
self.assertIsInstance(i, images.Image)
|
||||||
self.assertEqual(1, i.id)
|
self.assertEqual(1, i.id)
|
||||||
self.assertEqual('CentOS 5.2', i.name)
|
self.assertEqual('CentOS 5.2', i.name)
|
||||||
|
self.assertEqual(1, mock_warn.call_count)
|
||||||
|
|
||||||
def test_delete_image(self):
|
@mock.patch.object(warnings, 'warn')
|
||||||
|
def test_delete_image(self, mock_warn):
|
||||||
i = self.cs.images.delete(1)
|
i = self.cs.images.delete(1)
|
||||||
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('DELETE', '/images/1')
|
self.assert_called('DELETE', '/images/1')
|
||||||
|
self.assertEqual(1, mock_warn.call_count)
|
||||||
|
|
||||||
def test_delete_meta(self):
|
@mock.patch.object(warnings, 'warn')
|
||||||
|
def test_delete_meta(self, mock_warn):
|
||||||
i = self.cs.images.delete_meta(1, {'test_key': 'test_value'})
|
i = self.cs.images.delete_meta(1, {'test_key': 'test_value'})
|
||||||
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('DELETE', '/images/1/metadata/test_key')
|
self.assert_called('DELETE', '/images/1/metadata/test_key')
|
||||||
|
self.assertEqual(1, mock_warn.call_count)
|
||||||
|
|
||||||
def test_set_meta(self):
|
@mock.patch.object(warnings, 'warn')
|
||||||
|
def test_set_meta(self, mock_warn):
|
||||||
i = self.cs.images.set_meta(1, {'test_key': 'test_value'})
|
i = self.cs.images.set_meta(1, {'test_key': 'test_value'})
|
||||||
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assert_called('POST', '/images/1/metadata',
|
self.assert_called('POST', '/images/1/metadata',
|
||||||
{"metadata": {'test_key': 'test_value'}})
|
{"metadata": {'test_key': 'test_value'}})
|
||||||
|
self.assertEqual(1, mock_warn.call_count)
|
||||||
|
|
||||||
def test_find(self):
|
@mock.patch.object(warnings, 'warn')
|
||||||
|
def test_find(self, mock_warn):
|
||||||
i = self.cs.images.find(name="CentOS 5.2")
|
i = self.cs.images.find(name="CentOS 5.2")
|
||||||
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
self.assertEqual(1, i.id)
|
self.assertEqual(1, i.id)
|
||||||
self.assert_called('GET', '/images/1')
|
self.assert_called('GET', '/images/1')
|
||||||
|
# This is two warnings because find calls findall which calls list
|
||||||
|
# which is the first warning, which finds one results and then calls
|
||||||
|
# get on that, which is the second warning.
|
||||||
|
self.assertEqual(2, mock_warn.call_count)
|
||||||
|
|
||||||
iml = self.cs.images.findall(status='SAVING')
|
iml = self.cs.images.findall(status='SAVING')
|
||||||
self.assert_request_id(iml, fakes.FAKE_REQUEST_ID_LIST)
|
self.assert_request_id(iml, fakes.FAKE_REQUEST_ID_LIST)
|
||||||
|
@ -883,11 +883,13 @@ class ShellTest(utils.TestCase):
|
|||||||
{'removeTenantAccess': {'tenant': 'proj2'}})
|
{'removeTenantAccess': {'tenant': 'proj2'}})
|
||||||
|
|
||||||
def test_image_show(self):
|
def test_image_show(self):
|
||||||
self.run_command('image-show 1')
|
_, err = self.run_command('image-show 1')
|
||||||
|
self.assertIn('Command image-show is deprecated', err)
|
||||||
self.assert_called('GET', '/images/1')
|
self.assert_called('GET', '/images/1')
|
||||||
|
|
||||||
def test_image_meta_set(self):
|
def test_image_meta_set(self):
|
||||||
self.run_command('image-meta 1 set test_key=test_value')
|
_, err = self.run_command('image-meta 1 set test_key=test_value')
|
||||||
|
self.assertIn('Command image-meta is deprecated', err)
|
||||||
self.assert_called('POST', '/images/1/metadata',
|
self.assert_called('POST', '/images/1/metadata',
|
||||||
{'metadata': {'test_key': 'test_value'}})
|
{'metadata': {'test_key': 'test_value'}})
|
||||||
|
|
||||||
@ -950,7 +952,8 @@ class ShellTest(utils.TestCase):
|
|||||||
'image-create sample-server mysnapshot_deleted --poll')
|
'image-create sample-server mysnapshot_deleted --poll')
|
||||||
|
|
||||||
def test_image_delete(self):
|
def test_image_delete(self):
|
||||||
self.run_command('image-delete 1')
|
_, err = self.run_command('image-delete 1')
|
||||||
|
self.assertIn('Command image-delete is deprecated', err)
|
||||||
self.assert_called('DELETE', '/images/1')
|
self.assert_called('DELETE', '/images/1')
|
||||||
|
|
||||||
def test_image_delete_multiple(self):
|
def test_image_delete_multiple(self):
|
||||||
|
@ -13,9 +13,11 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Image interface.
|
DEPRECATED: Image interface.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
from six.moves.urllib import parse
|
from six.moves.urllib import parse
|
||||||
|
|
||||||
from novaclient import base
|
from novaclient import base
|
||||||
@ -23,7 +25,8 @@ from novaclient import base
|
|||||||
|
|
||||||
class Image(base.Resource):
|
class Image(base.Resource):
|
||||||
"""
|
"""
|
||||||
An image is a collection of files used to create or rebuild a server.
|
DEPRECATED: An image is a collection of files used to create or rebuild a
|
||||||
|
server.
|
||||||
"""
|
"""
|
||||||
HUMAN_ID = True
|
HUMAN_ID = True
|
||||||
|
|
||||||
@ -32,7 +35,7 @@ class Image(base.Resource):
|
|||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"""
|
"""
|
||||||
Delete this image.
|
DEPRECATED: Delete this image.
|
||||||
|
|
||||||
:returns: An instance of novaclient.base.TupleWithMeta
|
:returns: An instance of novaclient.base.TupleWithMeta
|
||||||
"""
|
"""
|
||||||
@ -41,28 +44,36 @@ class Image(base.Resource):
|
|||||||
|
|
||||||
class ImageManager(base.ManagerWithFind):
|
class ImageManager(base.ManagerWithFind):
|
||||||
"""
|
"""
|
||||||
Manage :class:`Image` resources.
|
DEPRECATED: Manage :class:`Image` resources.
|
||||||
"""
|
"""
|
||||||
resource_class = Image
|
resource_class = Image
|
||||||
|
|
||||||
def get(self, image):
|
def get(self, image):
|
||||||
"""
|
"""
|
||||||
Get an image.
|
DEPRECATED: Get an image.
|
||||||
|
|
||||||
:param image: The ID of the image to get.
|
:param image: The ID of the image to get.
|
||||||
:rtype: :class:`Image`
|
:rtype: :class:`Image`
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
'The novaclient.v2.images module is deprecated and will be '
|
||||||
|
'removed after Nova 15.0.0 is released. Use python-glanceclient '
|
||||||
|
'or python-openstacksdk instead.', DeprecationWarning)
|
||||||
return self._get("/images/%s" % base.getid(image), "image")
|
return self._get("/images/%s" % base.getid(image), "image")
|
||||||
|
|
||||||
def list(self, detailed=True, limit=None, marker=None):
|
def list(self, detailed=True, limit=None, marker=None):
|
||||||
"""
|
"""
|
||||||
Get a list of all images.
|
DEPRECATED: Get a list of all images.
|
||||||
|
|
||||||
:rtype: list of :class:`Image`
|
:rtype: list of :class:`Image`
|
||||||
:param limit: maximum number of images to return.
|
:param limit: maximum number of images to return.
|
||||||
:param marker: Begin returning images that appear later in the image
|
:param marker: Begin returning images that appear later in the image
|
||||||
list than that represented by this image id (optional).
|
list than that represented by this image id (optional).
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
'The novaclient.v2.images module is deprecated and will be '
|
||||||
|
'removed after Nova 15.0.0 is released. Use python-glanceclient '
|
||||||
|
'or python-openstacksdk instead.', DeprecationWarning)
|
||||||
params = {}
|
params = {}
|
||||||
detail = ''
|
detail = ''
|
||||||
if detailed:
|
if detailed:
|
||||||
@ -77,7 +88,7 @@ class ImageManager(base.ManagerWithFind):
|
|||||||
|
|
||||||
def delete(self, image):
|
def delete(self, image):
|
||||||
"""
|
"""
|
||||||
Delete an image.
|
DEPRECATED: Delete an image.
|
||||||
|
|
||||||
It should go without saying that you can't delete an image
|
It should go without saying that you can't delete an image
|
||||||
that you didn't create.
|
that you didn't create.
|
||||||
@ -85,27 +96,39 @@ class ImageManager(base.ManagerWithFind):
|
|||||||
:param image: The :class:`Image` (or its ID) to delete.
|
:param image: The :class:`Image` (or its ID) to delete.
|
||||||
:returns: An instance of novaclient.base.TupleWithMeta
|
:returns: An instance of novaclient.base.TupleWithMeta
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
'The novaclient.v2.images module is deprecated and will be '
|
||||||
|
'removed after Nova 15.0.0 is released. Use python-glanceclient '
|
||||||
|
'or python-openstacksdk instead.', DeprecationWarning)
|
||||||
return self._delete("/images/%s" % base.getid(image))
|
return self._delete("/images/%s" % base.getid(image))
|
||||||
|
|
||||||
def set_meta(self, image, metadata):
|
def set_meta(self, image, metadata):
|
||||||
"""
|
"""
|
||||||
Set an images metadata
|
DEPRECATED: Set an images metadata
|
||||||
|
|
||||||
:param image: The :class:`Image` to add metadata to
|
:param image: The :class:`Image` to add metadata to
|
||||||
:param metadata: A dict of metadata to add to the image
|
:param metadata: A dict of metadata to add to the image
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
'The novaclient.v2.images module is deprecated and will be '
|
||||||
|
'removed after Nova 15.0.0 is released. Use python-glanceclient '
|
||||||
|
'or python-openstacksdk instead.', DeprecationWarning)
|
||||||
body = {'metadata': metadata}
|
body = {'metadata': metadata}
|
||||||
return self._create("/images/%s/metadata" % base.getid(image),
|
return self._create("/images/%s/metadata" % base.getid(image),
|
||||||
body, "metadata")
|
body, "metadata")
|
||||||
|
|
||||||
def delete_meta(self, image, keys):
|
def delete_meta(self, image, keys):
|
||||||
"""
|
"""
|
||||||
Delete metadata from an image
|
DEPRECATED: Delete metadata from an image
|
||||||
|
|
||||||
:param image: The :class:`Image` to delete metadata
|
:param image: The :class:`Image` to delete metadata
|
||||||
:param keys: A list of metadata keys to delete from the image
|
:param keys: A list of metadata keys to delete from the image
|
||||||
:returns: An instance of novaclient.base.TupleWithMeta
|
:returns: An instance of novaclient.base.TupleWithMeta
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
'The novaclient.v2.images module is deprecated and will be '
|
||||||
|
'removed after Nova 15.0.0 is released. Use python-glanceclient '
|
||||||
|
'or python-openstacksdk instead.', DeprecationWarning)
|
||||||
result = base.TupleWithMeta((), None)
|
result = base.TupleWithMeta((), None)
|
||||||
for k in keys:
|
for k in keys:
|
||||||
ret = self._delete("/images/%s/metadata/%s" %
|
ret = self._delete("/images/%s/metadata/%s" %
|
||||||
|
@ -66,6 +66,14 @@ CLIENT_BDM2_KEYS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE(mriedem): Remove this along with the deprecated commands in the first
|
||||||
|
# python-novaclient release AFTER the nova server 15.0.0 'O' release.
|
||||||
|
def emit_image_deprecation_warning(command_name):
|
||||||
|
print('WARNING: Command %s is deprecated and will be removed after Nova '
|
||||||
|
'15.0.0 is released. Use python-glanceclient or openstackclient '
|
||||||
|
'instead.' % command_name, file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def _key_value_pairing(text):
|
def _key_value_pairing(text):
|
||||||
try:
|
try:
|
||||||
(k, v) = text.split('=', 1)
|
(k, v) = text.split('=', 1)
|
||||||
@ -1201,7 +1209,8 @@ def do_network_create(cs, args):
|
|||||||
metavar="<limit>",
|
metavar="<limit>",
|
||||||
help=_('Number of images to return per request.'))
|
help=_('Number of images to return per request.'))
|
||||||
def do_image_list(cs, _args):
|
def do_image_list(cs, _args):
|
||||||
"""Print a list of available images to boot from."""
|
"""DEPRECATED: Print a list of available images to boot from."""
|
||||||
|
emit_image_deprecation_warning('image-list')
|
||||||
limit = _args.limit
|
limit = _args.limit
|
||||||
image_list = cs.images.list(limit=limit)
|
image_list = cs.images.list(limit=limit)
|
||||||
|
|
||||||
@ -1234,7 +1243,8 @@ def do_image_list(cs, _args):
|
|||||||
help=_('Metadata to add/update or delete (only key is necessary on '
|
help=_('Metadata to add/update or delete (only key is necessary on '
|
||||||
'delete).'))
|
'delete).'))
|
||||||
def do_image_meta(cs, args):
|
def do_image_meta(cs, args):
|
||||||
"""Set or delete metadata on an image."""
|
"""DEPRECATED: Set or delete metadata on an image."""
|
||||||
|
emit_image_deprecation_warning('image-meta')
|
||||||
image = _find_image(cs, args.image)
|
image = _find_image(cs, args.image)
|
||||||
metadata = _extract_metadata(args)
|
metadata = _extract_metadata(args)
|
||||||
|
|
||||||
@ -1297,7 +1307,8 @@ def _print_flavor(flavor):
|
|||||||
metavar='<image>',
|
metavar='<image>',
|
||||||
help=_("Name or ID of image."))
|
help=_("Name or ID of image."))
|
||||||
def do_image_show(cs, args):
|
def do_image_show(cs, args):
|
||||||
"""Show details about the given image."""
|
"""DEPRECATED: Show details about the given image."""
|
||||||
|
emit_image_deprecation_warning('image-show')
|
||||||
image = _find_image(cs, args.image)
|
image = _find_image(cs, args.image)
|
||||||
_print_image(image)
|
_print_image(image)
|
||||||
|
|
||||||
@ -1306,7 +1317,8 @@ def do_image_show(cs, args):
|
|||||||
'image', metavar='<image>', nargs='+',
|
'image', metavar='<image>', nargs='+',
|
||||||
help=_('Name or ID of image(s).'))
|
help=_('Name or ID of image(s).'))
|
||||||
def do_image_delete(cs, args):
|
def do_image_delete(cs, args):
|
||||||
"""Delete specified image(s)."""
|
"""DEPRECATED: Delete specified image(s)."""
|
||||||
|
emit_image_deprecation_warning('image-delete')
|
||||||
for image in args.image:
|
for image in args.image:
|
||||||
try:
|
try:
|
||||||
_find_image(cs, image).delete()
|
_find_image(cs, image).delete()
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
deprecations:
|
||||||
|
- |
|
||||||
|
The following CLIs and python API bindings are now deprecated for removal:
|
||||||
|
|
||||||
|
* nova image-delete
|
||||||
|
* nova image-list
|
||||||
|
* nova image-meta
|
||||||
|
* nova image-show
|
||||||
|
|
||||||
|
These will be removed in the first major python-novaclient release after
|
||||||
|
the Nova 15.0.0 Ocata release. Use python-glanceclient or
|
||||||
|
python-openstackclient for CLI and python-glanceclient or openstacksdk
|
||||||
|
for python API bindings.
|
Loading…
Reference in New Issue
Block a user