Skip to content

Commit 1ecf1be

Browse files
author
Dean Troyer
committed
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
1 parent 95fe3fd commit 1ecf1be

11 files changed

Lines changed: 623 additions & 60 deletions

File tree

doc/source/command-objects/image.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,21 @@ List available images
139139
140140
os image list
141141
[--page-size <size>]
142+
[--public|--private]
142143
[--long]
143144
144145
.. option:: --page-size <size>
145146

146147
Number of images to request in each paginated request
147148

149+
.. option:: --public
150+
151+
List only public images
152+
153+
.. option:: --private
154+
155+
List only private images
156+
148157
.. option:: --long
149158

150159
List additional fields in output

openstackclient/api/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def list(
161161
):
162162
"""Return a list of resources
163163
164-
GET ${ENDPOINT}/${PATH}
164+
GET ${ENDPOINT}/${PATH}?${PARAMS}
165165
166166
path is often the object's plural resource type
167167

openstackclient/api/image_v1.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
"""Image v1 API Library"""
15+
16+
from openstackclient.api import api
17+
18+
19+
class APIv1(api.BaseAPI):
20+
"""Image v1 API"""
21+
22+
def __init__(self, endpoint=None, **kwargs):
23+
super(APIv1, self).__init__(endpoint=endpoint, **kwargs)
24+
25+
# Hack this until discovery is up
26+
self.endpoint = '/'.join([self.endpoint.rstrip('/'), 'v1'])
27+
28+
def image_list(
29+
self,
30+
detailed=False,
31+
public=False,
32+
private=False,
33+
**filter
34+
):
35+
"""Get available images
36+
37+
:param detailed:
38+
Retrieve detailed response from server if True
39+
:param public:
40+
Return public images if True
41+
:param private:
42+
Return private images if True
43+
44+
If public and private are both True or both False then all images are
45+
returned. Both arguments False is equivalent to no filter and all
46+
images are returned. Both arguments True is a filter that includes
47+
both public and private images which is the same set as all images.
48+
49+
http://docs.openstack.org/api/openstack-image-service/1.1/content/requesting-a-list-of-public-vm-images.html
50+
http://docs.openstack.org/api/openstack-image-service/1.1/content/requesting-detailed-metadata-on-public-vm-images.html
51+
http://docs.openstack.org/api/openstack-image-service/1.1/content/filtering-images-returned-via-get-images-and-get-imagesdetail.html
52+
53+
TODO(dtroyer): Implement filtering
54+
"""
55+
56+
url = "/images"
57+
if detailed or public or private:
58+
# Because we can't all use /details
59+
url += "/detail"
60+
61+
image_list = self.list(url, **filter)['images']
62+
63+
if public != private:
64+
# One is True and one is False, so public represents the filter
65+
# state in either case
66+
image_list = [i for i in image_list if i['is_public'] == public]
67+
68+
return image_list

openstackclient/api/image_v2.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
"""Image v2 API Library"""
15+
16+
from openstackclient.api import image_v1
17+
18+
19+
class APIv2(image_v1.APIv1):
20+
"""Image v2 API"""
21+
22+
def __init__(self, endpoint=None, **kwargs):
23+
super(APIv2, self).__init__(endpoint=endpoint, **kwargs)
24+
25+
# Hack this until discovery is up, and ignore parent endpoint setting
26+
self.endpoint = '/'.join([endpoint.rstrip('/'), 'v2'])
27+
28+
def image_list(
29+
self,
30+
detailed=False,
31+
public=False,
32+
private=False,
33+
**filter
34+
):
35+
"""Get available images
36+
37+
can add limit/marker
38+
39+
:param detailed:
40+
For v1 compatibility only, ignored as v2 is always 'detailed'
41+
:param public:
42+
Return public images if True
43+
:param private:
44+
Return private images if True
45+
46+
If public and private are both True or both False then all images are
47+
returned. Both arguments False is equivalent to no filter and all
48+
images are returned. Both arguments True is a filter that includes
49+
both public and private images which is the same set as all images.
50+
51+
http://docs.openstack.org/api/openstack-image-service/2.0/content/list-images.html
52+
53+
TODO(dtroyer): Implement filtering
54+
"""
55+
56+
if public == private:
57+
# No filtering for both False and both True cases
58+
filter.pop('visibility', None)
59+
elif public:
60+
filter['visibility'] = 'public'
61+
elif private:
62+
filter['visibility'] = 'private'
63+
64+
url = "/images"
65+
if detailed:
66+
# Because we can't all use /details
67+
url += "/detail"
68+
69+
return self.list(url, **filter)['images']

openstackclient/image/client.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,15 @@
3131
"2": "glanceclient.v2.client.Client",
3232
}
3333

34+
IMAGE_API_TYPE = 'image'
35+
IMAGE_API_VERSIONS = {
36+
'1': 'openstackclient.api.image_v1.APIv1',
37+
'2': 'openstackclient.api.image_v2.APIv2',
38+
}
39+
3440

3541
def make_client(instance):
36-
"""Returns an image service client."""
42+
"""Returns an image service client"""
3743
image_client = utils.get_client_class(
3844
API_NAME,
3945
instance._api_version[API_NAME],
@@ -45,13 +51,31 @@ def make_client(instance):
4551
region_name=instance._region_name,
4652
)
4753

48-
return image_client(
54+
client = image_client(
4955
endpoint,
5056
token=instance.auth.get_token(instance.session),
5157
cacert=instance._cacert,
5258
insecure=instance._insecure,
5359
)
5460

61+
# Create the low-level API
62+
63+
image_api = utils.get_client_class(
64+
API_NAME,
65+
instance._api_version[API_NAME],
66+
IMAGE_API_VERSIONS)
67+
LOG.debug('Instantiating image api: %s', image_api)
68+
69+
client.api = image_api(
70+
session=instance.session,
71+
endpoint=instance.get_endpoint_for_service_type(
72+
IMAGE_API_TYPE,
73+
region_name=instance._region_name,
74+
)
75+
)
76+
77+
return client
78+
5579

5680
def build_option_parser(parser):
5781
"""Hook to add global options"""

openstackclient/image/v1/image.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,21 @@ def get_parser(self, prog_name):
300300
metavar="<size>",
301301
help="Number of images to request in each paginated request",
302302
)
303+
public_group = parser.add_mutually_exclusive_group()
304+
public_group.add_argument(
305+
"--public",
306+
dest="public",
307+
action="store_true",
308+
default=False,
309+
help="List only public images",
310+
)
311+
public_group.add_argument(
312+
"--private",
313+
dest="private",
314+
action="store_true",
315+
default=False,
316+
help="List only private images",
317+
)
303318
parser.add_argument(
304319
'--long',
305320
action='store_true',
@@ -316,15 +331,21 @@ def take_action(self, parsed_args):
316331
kwargs = {}
317332
if parsed_args.page_size is not None:
318333
kwargs["page_size"] = parsed_args.page_size
334+
if parsed_args.public:
335+
kwargs['public'] = True
336+
if parsed_args.private:
337+
kwargs['private'] = True
338+
kwargs['detailed'] = parsed_args.long
319339

320-
data = image_client.images.list(**kwargs)
321340
if parsed_args.long:
322341
columns = ('ID', 'Name', 'Disk Format', 'Container Format',
323342
'Size', 'Status')
324343
else:
325344
columns = ("ID", "Name")
326345

327-
return (columns, (utils.get_item_properties(s, columns) for s in data))
346+
data = image_client.api.image_list(**kwargs)
347+
348+
return (columns, (utils.get_dict_properties(s, columns) for s in data))
328349

329350

330351
class SaveImage(command.Command):

openstackclient/image/v2/image.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,21 @@ def get_parser(self, prog_name):
6565
metavar="<size>",
6666
help="Number of images to request in each paginated request",
6767
)
68+
public_group = parser.add_mutually_exclusive_group()
69+
public_group.add_argument(
70+
"--public",
71+
dest="public",
72+
action="store_true",
73+
default=False,
74+
help="List only public images",
75+
)
76+
public_group.add_argument(
77+
"--private",
78+
dest="private",
79+
action="store_true",
80+
default=False,
81+
help="List only private images",
82+
)
6883
parser.add_argument(
6984
'--long',
7085
action='store_true',
@@ -81,15 +96,21 @@ def take_action(self, parsed_args):
8196
kwargs = {}
8297
if parsed_args.page_size is not None:
8398
kwargs["page_size"] = parsed_args.page_size
99+
if parsed_args.public:
100+
kwargs['public'] = True
101+
if parsed_args.private:
102+
kwargs['private'] = True
103+
kwargs['detailed'] = parsed_args.long
84104

85-
data = image_client.images.list(**kwargs)
86105
if parsed_args.long:
87106
columns = ('ID', 'Name', 'Disk Format', 'Container Format',
88107
'Size', 'Status')
89108
else:
90109
columns = ("ID", "Name")
91110

92-
return (columns, (utils.get_item_properties(s, columns) for s in data))
111+
data = image_client.api.image_list(**kwargs)
112+
113+
return (columns, (utils.get_dict_properties(s, columns) for s in data))
93114

94115

95116
class SaveImage(command.Command):

0 commit comments

Comments
 (0)