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:
Matt Riedemann 2016-04-06 16:52:05 -04:00
parent 77f214bdbd
commit a602e59806
6 changed files with 95 additions and 23 deletions

View File

@ -88,7 +88,8 @@ class SimpleReadOnlyNovaClientTest(base.ClientTestBase):
self.nova('hypervisor-list')
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")
def test_admin_interface_list(self):

View File

@ -11,6 +11,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import warnings
import mock
from novaclient.tests.unit.fixture_data import client
from novaclient.tests.unit.fixture_data import images as data
from novaclient.tests.unit import utils
@ -23,13 +27,15 @@ class ImagesTest(utils.FixturedTestCase):
client_fixture_class = client.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()
self.assert_request_id(il, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('GET', '/images/detail')
for i in il:
self.assertIsInstance(i, images.Image)
self.assertEqual(2, len(il))
self.assertEqual(1, mock_warn.call_count)
def test_list_images_undetailed(self):
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_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)
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('GET', '/images/1')
self.assertIsInstance(i, images.Image)
self.assertEqual(1, i.id)
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)
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
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'})
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
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'})
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('POST', '/images/1/metadata',
{"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")
self.assert_request_id(i, fakes.FAKE_REQUEST_ID_LIST)
self.assertEqual(1, i.id)
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')
self.assert_request_id(iml, fakes.FAKE_REQUEST_ID_LIST)

View File

@ -883,11 +883,13 @@ class ShellTest(utils.TestCase):
{'removeTenantAccess': {'tenant': 'proj2'}})
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')
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',
{'metadata': {'test_key': 'test_value'}})
@ -950,7 +952,8 @@ class ShellTest(utils.TestCase):
'image-create sample-server mysnapshot_deleted --poll')
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')
def test_image_delete_multiple(self):

View File

@ -13,9 +13,11 @@
# under the License.
"""
Image interface.
DEPRECATED: Image interface.
"""
import warnings
from six.moves.urllib import parse
from novaclient import base
@ -23,7 +25,8 @@ from novaclient import base
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
@ -32,7 +35,7 @@ class Image(base.Resource):
def delete(self):
"""
Delete this image.
DEPRECATED: Delete this image.
:returns: An instance of novaclient.base.TupleWithMeta
"""
@ -41,28 +44,36 @@ class Image(base.Resource):
class ImageManager(base.ManagerWithFind):
"""
Manage :class:`Image` resources.
DEPRECATED: Manage :class:`Image` resources.
"""
resource_class = Image
def get(self, image):
"""
Get an image.
DEPRECATED: Get an image.
:param image: The ID of the image to get.
: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")
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`
:param limit: maximum number of images to return.
:param marker: Begin returning images that appear later in the image
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 = {}
detail = ''
if detailed:
@ -77,7 +88,7 @@ class ImageManager(base.ManagerWithFind):
def delete(self, image):
"""
Delete an image.
DEPRECATED: Delete an image.
It should go without saying that you can't delete an image
that you didn't create.
@ -85,27 +96,39 @@ class ImageManager(base.ManagerWithFind):
:param image: The :class:`Image` (or its ID) to delete.
: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))
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 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}
return self._create("/images/%s/metadata" % base.getid(image),
body, "metadata")
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 keys: A list of metadata keys to delete from the image
: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)
for k in keys:
ret = self._delete("/images/%s/metadata/%s" %

View File

@ -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):
try:
(k, v) = text.split('=', 1)
@ -1201,7 +1209,8 @@ def do_network_create(cs, args):
metavar="<limit>",
help=_('Number of images to return per request.'))
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
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 '
'delete).'))
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)
metadata = _extract_metadata(args)
@ -1297,7 +1307,8 @@ def _print_flavor(flavor):
metavar='<image>',
help=_("Name or ID of image."))
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)
_print_image(image)
@ -1306,7 +1317,8 @@ def do_image_show(cs, args):
'image', metavar='<image>', nargs='+',
help=_('Name or ID of image(s).'))
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:
try:
_find_image(cs, image).delete()

View File

@ -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.