Begin low-level API for Image v1 and v2
image list for v1 and v2: * Add --public|--private to command parsers * Implement local public/private filtering for v1 image_list() * Pass public/private filter to server for v2 image_list() Change-Id: Ie7c24ea2d1bf2b3b1b7fa342eb45fee45894634d
This commit is contained in:
parent
95fe3fda3d
commit
1ecf1bee2d
@ -139,12 +139,21 @@ List available images
|
|||||||
|
|
||||||
os image list
|
os image list
|
||||||
[--page-size <size>]
|
[--page-size <size>]
|
||||||
|
[--public|--private]
|
||||||
[--long]
|
[--long]
|
||||||
|
|
||||||
.. option:: --page-size <size>
|
.. option:: --page-size <size>
|
||||||
|
|
||||||
Number of images to request in each paginated request
|
Number of images to request in each paginated request
|
||||||
|
|
||||||
|
.. option:: --public
|
||||||
|
|
||||||
|
List only public images
|
||||||
|
|
||||||
|
.. option:: --private
|
||||||
|
|
||||||
|
List only private images
|
||||||
|
|
||||||
.. option:: --long
|
.. option:: --long
|
||||||
|
|
||||||
List additional fields in output
|
List additional fields in output
|
||||||
|
@ -161,7 +161,7 @@ class BaseAPI(KeystoneSession):
|
|||||||
):
|
):
|
||||||
"""Return a list of resources
|
"""Return a list of resources
|
||||||
|
|
||||||
GET ${ENDPOINT}/${PATH}
|
GET ${ENDPOINT}/${PATH}?${PARAMS}
|
||||||
|
|
||||||
path is often the object's plural resource type
|
path is often the object's plural resource type
|
||||||
|
|
||||||
|
68
openstackclient/api/image_v1.py
Normal file
68
openstackclient/api/image_v1.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Image v1 API Library"""
|
||||||
|
|
||||||
|
from openstackclient.api import api
|
||||||
|
|
||||||
|
|
||||||
|
class APIv1(api.BaseAPI):
|
||||||
|
"""Image v1 API"""
|
||||||
|
|
||||||
|
def __init__(self, endpoint=None, **kwargs):
|
||||||
|
super(APIv1, self).__init__(endpoint=endpoint, **kwargs)
|
||||||
|
|
||||||
|
# Hack this until discovery is up
|
||||||
|
self.endpoint = '/'.join([self.endpoint.rstrip('/'), 'v1'])
|
||||||
|
|
||||||
|
def image_list(
|
||||||
|
self,
|
||||||
|
detailed=False,
|
||||||
|
public=False,
|
||||||
|
private=False,
|
||||||
|
**filter
|
||||||
|
):
|
||||||
|
"""Get available images
|
||||||
|
|
||||||
|
:param detailed:
|
||||||
|
Retrieve detailed response from server if True
|
||||||
|
:param public:
|
||||||
|
Return public images if True
|
||||||
|
:param private:
|
||||||
|
Return private images if True
|
||||||
|
|
||||||
|
If public and private are both True or both False then all images are
|
||||||
|
returned. Both arguments False is equivalent to no filter and all
|
||||||
|
images are returned. Both arguments True is a filter that includes
|
||||||
|
both public and private images which is the same set as all images.
|
||||||
|
|
||||||
|
http://docs.openstack.org/api/openstack-image-service/1.1/content/requesting-a-list-of-public-vm-images.html
|
||||||
|
http://docs.openstack.org/api/openstack-image-service/1.1/content/requesting-detailed-metadata-on-public-vm-images.html
|
||||||
|
http://docs.openstack.org/api/openstack-image-service/1.1/content/filtering-images-returned-via-get-images-and-get-imagesdetail.html
|
||||||
|
|
||||||
|
TODO(dtroyer): Implement filtering
|
||||||
|
"""
|
||||||
|
|
||||||
|
url = "/images"
|
||||||
|
if detailed or public or private:
|
||||||
|
# Because we can't all use /details
|
||||||
|
url += "/detail"
|
||||||
|
|
||||||
|
image_list = self.list(url, **filter)['images']
|
||||||
|
|
||||||
|
if public != private:
|
||||||
|
# One is True and one is False, so public represents the filter
|
||||||
|
# state in either case
|
||||||
|
image_list = [i for i in image_list if i['is_public'] == public]
|
||||||
|
|
||||||
|
return image_list
|
69
openstackclient/api/image_v2.py
Normal file
69
openstackclient/api/image_v2.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Image v2 API Library"""
|
||||||
|
|
||||||
|
from openstackclient.api import image_v1
|
||||||
|
|
||||||
|
|
||||||
|
class APIv2(image_v1.APIv1):
|
||||||
|
"""Image v2 API"""
|
||||||
|
|
||||||
|
def __init__(self, endpoint=None, **kwargs):
|
||||||
|
super(APIv2, self).__init__(endpoint=endpoint, **kwargs)
|
||||||
|
|
||||||
|
# Hack this until discovery is up, and ignore parent endpoint setting
|
||||||
|
self.endpoint = '/'.join([endpoint.rstrip('/'), 'v2'])
|
||||||
|
|
||||||
|
def image_list(
|
||||||
|
self,
|
||||||
|
detailed=False,
|
||||||
|
public=False,
|
||||||
|
private=False,
|
||||||
|
**filter
|
||||||
|
):
|
||||||
|
"""Get available images
|
||||||
|
|
||||||
|
can add limit/marker
|
||||||
|
|
||||||
|
:param detailed:
|
||||||
|
For v1 compatibility only, ignored as v2 is always 'detailed'
|
||||||
|
:param public:
|
||||||
|
Return public images if True
|
||||||
|
:param private:
|
||||||
|
Return private images if True
|
||||||
|
|
||||||
|
If public and private are both True or both False then all images are
|
||||||
|
returned. Both arguments False is equivalent to no filter and all
|
||||||
|
images are returned. Both arguments True is a filter that includes
|
||||||
|
both public and private images which is the same set as all images.
|
||||||
|
|
||||||
|
http://docs.openstack.org/api/openstack-image-service/2.0/content/list-images.html
|
||||||
|
|
||||||
|
TODO(dtroyer): Implement filtering
|
||||||
|
"""
|
||||||
|
|
||||||
|
if public == private:
|
||||||
|
# No filtering for both False and both True cases
|
||||||
|
filter.pop('visibility', None)
|
||||||
|
elif public:
|
||||||
|
filter['visibility'] = 'public'
|
||||||
|
elif private:
|
||||||
|
filter['visibility'] = 'private'
|
||||||
|
|
||||||
|
url = "/images"
|
||||||
|
if detailed:
|
||||||
|
# Because we can't all use /details
|
||||||
|
url += "/detail"
|
||||||
|
|
||||||
|
return self.list(url, **filter)['images']
|
@ -31,9 +31,15 @@ API_VERSIONS = {
|
|||||||
"2": "glanceclient.v2.client.Client",
|
"2": "glanceclient.v2.client.Client",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IMAGE_API_TYPE = 'image'
|
||||||
|
IMAGE_API_VERSIONS = {
|
||||||
|
'1': 'openstackclient.api.image_v1.APIv1',
|
||||||
|
'2': 'openstackclient.api.image_v2.APIv2',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def make_client(instance):
|
def make_client(instance):
|
||||||
"""Returns an image service client."""
|
"""Returns an image service client"""
|
||||||
image_client = utils.get_client_class(
|
image_client = utils.get_client_class(
|
||||||
API_NAME,
|
API_NAME,
|
||||||
instance._api_version[API_NAME],
|
instance._api_version[API_NAME],
|
||||||
@ -45,13 +51,31 @@ def make_client(instance):
|
|||||||
region_name=instance._region_name,
|
region_name=instance._region_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
return image_client(
|
client = image_client(
|
||||||
endpoint,
|
endpoint,
|
||||||
token=instance.auth.get_token(instance.session),
|
token=instance.auth.get_token(instance.session),
|
||||||
cacert=instance._cacert,
|
cacert=instance._cacert,
|
||||||
insecure=instance._insecure,
|
insecure=instance._insecure,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Create the low-level API
|
||||||
|
|
||||||
|
image_api = utils.get_client_class(
|
||||||
|
API_NAME,
|
||||||
|
instance._api_version[API_NAME],
|
||||||
|
IMAGE_API_VERSIONS)
|
||||||
|
LOG.debug('Instantiating image api: %s', image_api)
|
||||||
|
|
||||||
|
client.api = image_api(
|
||||||
|
session=instance.session,
|
||||||
|
endpoint=instance.get_endpoint_for_service_type(
|
||||||
|
IMAGE_API_TYPE,
|
||||||
|
region_name=instance._region_name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return client
|
||||||
|
|
||||||
|
|
||||||
def build_option_parser(parser):
|
def build_option_parser(parser):
|
||||||
"""Hook to add global options"""
|
"""Hook to add global options"""
|
||||||
|
@ -300,6 +300,21 @@ class ListImage(lister.Lister):
|
|||||||
metavar="<size>",
|
metavar="<size>",
|
||||||
help="Number of images to request in each paginated request",
|
help="Number of images to request in each paginated request",
|
||||||
)
|
)
|
||||||
|
public_group = parser.add_mutually_exclusive_group()
|
||||||
|
public_group.add_argument(
|
||||||
|
"--public",
|
||||||
|
dest="public",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="List only public images",
|
||||||
|
)
|
||||||
|
public_group.add_argument(
|
||||||
|
"--private",
|
||||||
|
dest="private",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="List only private images",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--long',
|
'--long',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@ -316,15 +331,21 @@ class ListImage(lister.Lister):
|
|||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.page_size is not None:
|
if parsed_args.page_size is not None:
|
||||||
kwargs["page_size"] = parsed_args.page_size
|
kwargs["page_size"] = parsed_args.page_size
|
||||||
|
if parsed_args.public:
|
||||||
|
kwargs['public'] = True
|
||||||
|
if parsed_args.private:
|
||||||
|
kwargs['private'] = True
|
||||||
|
kwargs['detailed'] = parsed_args.long
|
||||||
|
|
||||||
data = image_client.images.list(**kwargs)
|
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = ('ID', 'Name', 'Disk Format', 'Container Format',
|
columns = ('ID', 'Name', 'Disk Format', 'Container Format',
|
||||||
'Size', 'Status')
|
'Size', 'Status')
|
||||||
else:
|
else:
|
||||||
columns = ("ID", "Name")
|
columns = ("ID", "Name")
|
||||||
|
|
||||||
return (columns, (utils.get_item_properties(s, columns) for s in data))
|
data = image_client.api.image_list(**kwargs)
|
||||||
|
|
||||||
|
return (columns, (utils.get_dict_properties(s, columns) for s in data))
|
||||||
|
|
||||||
|
|
||||||
class SaveImage(command.Command):
|
class SaveImage(command.Command):
|
||||||
|
@ -65,6 +65,21 @@ class ListImage(lister.Lister):
|
|||||||
metavar="<size>",
|
metavar="<size>",
|
||||||
help="Number of images to request in each paginated request",
|
help="Number of images to request in each paginated request",
|
||||||
)
|
)
|
||||||
|
public_group = parser.add_mutually_exclusive_group()
|
||||||
|
public_group.add_argument(
|
||||||
|
"--public",
|
||||||
|
dest="public",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="List only public images",
|
||||||
|
)
|
||||||
|
public_group.add_argument(
|
||||||
|
"--private",
|
||||||
|
dest="private",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="List only private images",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--long',
|
'--long',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@ -81,15 +96,21 @@ class ListImage(lister.Lister):
|
|||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.page_size is not None:
|
if parsed_args.page_size is not None:
|
||||||
kwargs["page_size"] = parsed_args.page_size
|
kwargs["page_size"] = parsed_args.page_size
|
||||||
|
if parsed_args.public:
|
||||||
|
kwargs['public'] = True
|
||||||
|
if parsed_args.private:
|
||||||
|
kwargs['private'] = True
|
||||||
|
kwargs['detailed'] = parsed_args.long
|
||||||
|
|
||||||
data = image_client.images.list(**kwargs)
|
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = ('ID', 'Name', 'Disk Format', 'Container Format',
|
columns = ('ID', 'Name', 'Disk Format', 'Container Format',
|
||||||
'Size', 'Status')
|
'Size', 'Status')
|
||||||
else:
|
else:
|
||||||
columns = ("ID", "Name")
|
columns = ("ID", "Name")
|
||||||
|
|
||||||
return (columns, (utils.get_item_properties(s, columns) for s in data))
|
data = image_client.api.image_list(**kwargs)
|
||||||
|
|
||||||
|
return (columns, (utils.get_dict_properties(s, columns) for s in data))
|
||||||
|
|
||||||
|
|
||||||
class SaveImage(command.Command):
|
class SaveImage(command.Command):
|
||||||
|
98
openstackclient/tests/api/test_image_v1.py
Normal file
98
openstackclient/tests/api/test_image_v1.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Image v1 API Library Tests"""
|
||||||
|
|
||||||
|
from requests_mock.contrib import fixture
|
||||||
|
|
||||||
|
from keystoneclient import session
|
||||||
|
from openstackclient.api import image_v1
|
||||||
|
from openstackclient.tests import utils
|
||||||
|
|
||||||
|
|
||||||
|
FAKE_PROJECT = 'xyzpdq'
|
||||||
|
FAKE_URL = 'http://gopher.com'
|
||||||
|
|
||||||
|
|
||||||
|
class TestImageAPIv1(utils.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestImageAPIv1, self).setUp()
|
||||||
|
|
||||||
|
sess = session.Session()
|
||||||
|
self.api = image_v1.APIv1(session=sess, endpoint=FAKE_URL)
|
||||||
|
self.requests_mock = self.useFixture(fixture.Fixture())
|
||||||
|
|
||||||
|
|
||||||
|
class TestImage(TestImageAPIv1):
|
||||||
|
|
||||||
|
PUB_PROT = {
|
||||||
|
'id': '1',
|
||||||
|
'name': 'pub1',
|
||||||
|
'is_public': True,
|
||||||
|
'protected': True,
|
||||||
|
}
|
||||||
|
PUB_NOPROT = {
|
||||||
|
'id': '2',
|
||||||
|
'name': 'pub2-noprot',
|
||||||
|
'is_public': True,
|
||||||
|
'protected': False,
|
||||||
|
}
|
||||||
|
NOPUB_PROT = {
|
||||||
|
'id': '3',
|
||||||
|
'name': 'priv3',
|
||||||
|
'is_public': False,
|
||||||
|
'protected': True,
|
||||||
|
}
|
||||||
|
NOPUB_NOPROT = {
|
||||||
|
'id': '4',
|
||||||
|
'name': 'priv4-noprot',
|
||||||
|
'is_public': False,
|
||||||
|
'protected': False,
|
||||||
|
}
|
||||||
|
LIST_IMAGE_RESP = [
|
||||||
|
PUB_PROT,
|
||||||
|
PUB_NOPROT,
|
||||||
|
NOPUB_PROT,
|
||||||
|
NOPUB_NOPROT,
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_image_list_no_options(self):
|
||||||
|
self.requests_mock.register_uri(
|
||||||
|
'GET',
|
||||||
|
FAKE_URL + '/v1/images',
|
||||||
|
json={'images': self.LIST_IMAGE_RESP},
|
||||||
|
status_code=200,
|
||||||
|
)
|
||||||
|
ret = self.api.image_list()
|
||||||
|
self.assertEqual(self.LIST_IMAGE_RESP, ret)
|
||||||
|
|
||||||
|
def test_image_list_public(self):
|
||||||
|
self.requests_mock.register_uri(
|
||||||
|
'GET',
|
||||||
|
FAKE_URL + '/v1/images/detail',
|
||||||
|
json={'images': self.LIST_IMAGE_RESP},
|
||||||
|
status_code=200,
|
||||||
|
)
|
||||||
|
ret = self.api.image_list(public=True)
|
||||||
|
self.assertEqual([self.PUB_PROT, self.PUB_NOPROT], ret)
|
||||||
|
|
||||||
|
def test_image_list_private(self):
|
||||||
|
self.requests_mock.register_uri(
|
||||||
|
'GET',
|
||||||
|
FAKE_URL + '/v1/images/detail',
|
||||||
|
json={'images': self.LIST_IMAGE_RESP},
|
||||||
|
status_code=200,
|
||||||
|
)
|
||||||
|
ret = self.api.image_list(private=True)
|
||||||
|
self.assertEqual([self.NOPUB_PROT, self.NOPUB_NOPROT], ret)
|
98
openstackclient/tests/api/test_image_v2.py
Normal file
98
openstackclient/tests/api/test_image_v2.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Image v2 API Library Tests"""
|
||||||
|
|
||||||
|
from requests_mock.contrib import fixture
|
||||||
|
|
||||||
|
from keystoneclient import session
|
||||||
|
from openstackclient.api import image_v2
|
||||||
|
from openstackclient.tests import utils
|
||||||
|
|
||||||
|
|
||||||
|
FAKE_PROJECT = 'xyzpdq'
|
||||||
|
FAKE_URL = 'http://gopher.com'
|
||||||
|
|
||||||
|
|
||||||
|
class TestImageAPIv2(utils.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestImageAPIv2, self).setUp()
|
||||||
|
|
||||||
|
sess = session.Session()
|
||||||
|
self.api = image_v2.APIv2(session=sess, endpoint=FAKE_URL)
|
||||||
|
self.requests_mock = self.useFixture(fixture.Fixture())
|
||||||
|
|
||||||
|
|
||||||
|
class TestImage(TestImageAPIv2):
|
||||||
|
|
||||||
|
PUB_PROT = {
|
||||||
|
'id': '1',
|
||||||
|
'name': 'pub1',
|
||||||
|
'visibility': 'public',
|
||||||
|
'protected': True,
|
||||||
|
}
|
||||||
|
PUB_NOPROT = {
|
||||||
|
'id': '2',
|
||||||
|
'name': 'pub2-noprot',
|
||||||
|
'visibility': 'public',
|
||||||
|
'protected': False,
|
||||||
|
}
|
||||||
|
NOPUB_PROT = {
|
||||||
|
'id': '3',
|
||||||
|
'name': 'priv3',
|
||||||
|
'visibility': 'private',
|
||||||
|
'protected': True,
|
||||||
|
}
|
||||||
|
NOPUB_NOPROT = {
|
||||||
|
'id': '4',
|
||||||
|
'name': 'priv4-noprot',
|
||||||
|
'visibility': 'private',
|
||||||
|
'protected': False,
|
||||||
|
}
|
||||||
|
LIST_IMAGE_RESP = [
|
||||||
|
PUB_PROT,
|
||||||
|
PUB_NOPROT,
|
||||||
|
NOPUB_PROT,
|
||||||
|
NOPUB_NOPROT,
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_image_list_no_options(self):
|
||||||
|
self.requests_mock.register_uri(
|
||||||
|
'GET',
|
||||||
|
FAKE_URL + '/v2/images',
|
||||||
|
json={'images': self.LIST_IMAGE_RESP},
|
||||||
|
status_code=200,
|
||||||
|
)
|
||||||
|
ret = self.api.image_list()
|
||||||
|
self.assertEqual(self.LIST_IMAGE_RESP, ret)
|
||||||
|
|
||||||
|
def test_image_list_public(self):
|
||||||
|
self.requests_mock.register_uri(
|
||||||
|
'GET',
|
||||||
|
FAKE_URL + '/v2/images',
|
||||||
|
json={'images': [self.PUB_PROT, self.PUB_NOPROT]},
|
||||||
|
status_code=200,
|
||||||
|
)
|
||||||
|
ret = self.api.image_list(public=True)
|
||||||
|
self.assertEqual([self.PUB_PROT, self.PUB_NOPROT], ret)
|
||||||
|
|
||||||
|
def test_image_list_private(self):
|
||||||
|
self.requests_mock.register_uri(
|
||||||
|
'GET',
|
||||||
|
FAKE_URL + '/v2/images',
|
||||||
|
json={'images': [self.NOPUB_PROT, self.NOPUB_NOPROT]},
|
||||||
|
status_code=200,
|
||||||
|
)
|
||||||
|
ret = self.api.image_list(public=True)
|
||||||
|
self.assertEqual([self.NOPUB_PROT, self.NOPUB_NOPROT], ret)
|
@ -300,6 +300,128 @@ class TestImageDelete(TestImage):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestImageList(TestImage):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestImageList, self).setUp()
|
||||||
|
|
||||||
|
self.api_mock = mock.Mock()
|
||||||
|
self.api_mock.image_list.return_value = [
|
||||||
|
copy.deepcopy(image_fakes.IMAGE),
|
||||||
|
]
|
||||||
|
self.app.client_manager.image.api = self.api_mock
|
||||||
|
|
||||||
|
# Get the command object to test
|
||||||
|
self.cmd = image.ListImage(self.app, None)
|
||||||
|
|
||||||
|
def test_image_list_no_options(self):
|
||||||
|
arglist = []
|
||||||
|
verifylist = [
|
||||||
|
('public', False),
|
||||||
|
('private', False),
|
||||||
|
('long', False),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
# DisplayCommandBase.take_action() returns two tuples
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
self.api_mock.image_list.assert_called_with(
|
||||||
|
detailed=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
collist = ('ID', 'Name')
|
||||||
|
|
||||||
|
self.assertEqual(columns, collist)
|
||||||
|
datalist = ((
|
||||||
|
image_fakes.image_id,
|
||||||
|
image_fakes.image_name,
|
||||||
|
), )
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
def test_image_list_public_option(self):
|
||||||
|
arglist = [
|
||||||
|
'--public',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('public', True),
|
||||||
|
('private', False),
|
||||||
|
('long', False),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
# DisplayCommandBase.take_action() returns two tuples
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
self.api_mock.image_list.assert_called_with(
|
||||||
|
detailed=False,
|
||||||
|
public=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
collist = ('ID', 'Name')
|
||||||
|
|
||||||
|
self.assertEqual(columns, collist)
|
||||||
|
datalist = ((
|
||||||
|
image_fakes.image_id,
|
||||||
|
image_fakes.image_name,
|
||||||
|
), )
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
def test_image_list_private_option(self):
|
||||||
|
arglist = [
|
||||||
|
'--private',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('public', False),
|
||||||
|
('private', True),
|
||||||
|
('long', False),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
# DisplayCommandBase.take_action() returns two tuples
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
self.api_mock.image_list.assert_called_with(
|
||||||
|
detailed=False,
|
||||||
|
private=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
collist = ('ID', 'Name')
|
||||||
|
|
||||||
|
self.assertEqual(columns, collist)
|
||||||
|
datalist = ((
|
||||||
|
image_fakes.image_id,
|
||||||
|
image_fakes.image_name,
|
||||||
|
), )
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
def test_image_list_long_option(self):
|
||||||
|
arglist = [
|
||||||
|
'--long',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('long', True),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
# DisplayCommandBase.take_action() returns two tuples
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
self.api_mock.image_list.assert_called_with(
|
||||||
|
detailed=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
collist = ('ID', 'Name', 'Disk Format', 'Container Format',
|
||||||
|
'Size', 'Status')
|
||||||
|
|
||||||
|
self.assertEqual(columns, collist)
|
||||||
|
datalist = ((
|
||||||
|
image_fakes.image_id,
|
||||||
|
image_fakes.image_name,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
), )
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
|
||||||
class TestImageSet(TestImage):
|
class TestImageSet(TestImage):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -453,48 +575,3 @@ class TestImageSet(TestImage):
|
|||||||
image_fakes.image_id,
|
image_fakes.image_id,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestImageList(TestImage):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestImageList, self).setUp()
|
|
||||||
|
|
||||||
# This is the return value for utils.find_resource()
|
|
||||||
self.images_mock.list.return_value = [
|
|
||||||
fakes.FakeResource(
|
|
||||||
None,
|
|
||||||
copy.deepcopy(image_fakes.IMAGE),
|
|
||||||
loaded=True,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Get the command object to test
|
|
||||||
self.cmd = image.ListImage(self.app, None)
|
|
||||||
|
|
||||||
def test_image_list_long_option(self):
|
|
||||||
arglist = [
|
|
||||||
'--long',
|
|
||||||
]
|
|
||||||
verifylist = [
|
|
||||||
('long', True),
|
|
||||||
]
|
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
|
||||||
|
|
||||||
# DisplayCommandBase.take_action() returns two tuples
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
|
||||||
self.images_mock.list.assert_called_with()
|
|
||||||
|
|
||||||
collist = ('ID', 'Name', 'Disk Format', 'Container Format',
|
|
||||||
'Size', 'Status')
|
|
||||||
|
|
||||||
self.assertEqual(columns, collist)
|
|
||||||
datalist = ((
|
|
||||||
image_fakes.image_id,
|
|
||||||
image_fakes.image_name,
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
), )
|
|
||||||
self.assertEqual(datalist, tuple(data))
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import mock
|
||||||
|
|
||||||
from openstackclient.image.v2 import image
|
from openstackclient.image.v2 import image
|
||||||
from openstackclient.tests import fakes
|
from openstackclient.tests import fakes
|
||||||
@ -68,18 +69,93 @@ class TestImageList(TestImage):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImageList, self).setUp()
|
super(TestImageList, self).setUp()
|
||||||
|
|
||||||
# This is the return value for utils.find_resource()
|
self.api_mock = mock.Mock()
|
||||||
self.images_mock.list.return_value = [
|
self.api_mock.image_list.return_value = [
|
||||||
fakes.FakeResource(
|
|
||||||
None,
|
|
||||||
copy.deepcopy(image_fakes.IMAGE),
|
copy.deepcopy(image_fakes.IMAGE),
|
||||||
loaded=True,
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
self.app.client_manager.image.api = self.api_mock
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = image.ListImage(self.app, None)
|
self.cmd = image.ListImage(self.app, None)
|
||||||
|
|
||||||
|
def test_image_list_no_options(self):
|
||||||
|
arglist = []
|
||||||
|
verifylist = [
|
||||||
|
('public', False),
|
||||||
|
('private', False),
|
||||||
|
('long', False),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
# DisplayCommandBase.take_action() returns two tuples
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
self.api_mock.image_list.assert_called_with(
|
||||||
|
detailed=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
collist = ('ID', 'Name')
|
||||||
|
|
||||||
|
self.assertEqual(columns, collist)
|
||||||
|
datalist = ((
|
||||||
|
image_fakes.image_id,
|
||||||
|
image_fakes.image_name,
|
||||||
|
), )
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
def test_image_list_public_option(self):
|
||||||
|
arglist = [
|
||||||
|
'--public',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('public', True),
|
||||||
|
('private', False),
|
||||||
|
('long', False),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
# DisplayCommandBase.take_action() returns two tuples
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
self.api_mock.image_list.assert_called_with(
|
||||||
|
detailed=False,
|
||||||
|
public=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
collist = ('ID', 'Name')
|
||||||
|
|
||||||
|
self.assertEqual(columns, collist)
|
||||||
|
datalist = ((
|
||||||
|
image_fakes.image_id,
|
||||||
|
image_fakes.image_name,
|
||||||
|
), )
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
def test_image_list_private_option(self):
|
||||||
|
arglist = [
|
||||||
|
'--private',
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('public', False),
|
||||||
|
('private', True),
|
||||||
|
('long', False),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
# DisplayCommandBase.take_action() returns two tuples
|
||||||
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
self.api_mock.image_list.assert_called_with(
|
||||||
|
detailed=False,
|
||||||
|
private=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
collist = ('ID', 'Name')
|
||||||
|
|
||||||
|
self.assertEqual(columns, collist)
|
||||||
|
datalist = ((
|
||||||
|
image_fakes.image_id,
|
||||||
|
image_fakes.image_name,
|
||||||
|
), )
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
def test_image_list_long_option(self):
|
def test_image_list_long_option(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--long',
|
'--long',
|
||||||
@ -91,7 +167,9 @@ class TestImageList(TestImage):
|
|||||||
|
|
||||||
# DisplayCommandBase.take_action() returns two tuples
|
# DisplayCommandBase.take_action() returns two tuples
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.images_mock.list.assert_called_with()
|
self.api_mock.image_list.assert_called_with(
|
||||||
|
detailed=True,
|
||||||
|
)
|
||||||
|
|
||||||
collist = ('ID', 'Name', 'Disk Format', 'Container Format',
|
collist = ('ID', 'Name', 'Disk Format', 'Container Format',
|
||||||
'Size', 'Status')
|
'Size', 'Status')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user