Skip to content

Commit c628c2d

Browse files
author
Mridula Joshi
committed
image: Add support for cache commands
Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/874372 Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/874940 Change-Id: I96b95cb93d298602b6d4b0cd35a213478babff5f
1 parent 08faf81 commit c628c2d

6 files changed

Lines changed: 470 additions & 4 deletions

File tree

doc/source/cli/data/glance.csv

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
cache-clear,,"Clear all images from cache, queue or both."
2-
cache-delete,,Delete image from cache/caching queue.
3-
cache-list,,Get cache state.
4-
cache-queue,,Queue image(s) for caching.
1+
cache-clear,cached image list,"Clear all images from cache, queue or both."
2+
cache-delete,cached image delete,Delete image from cache/caching queue.
3+
cache-list,cached image list,Get cache state.
4+
cache-queue,cached image queue,Queue image(s) for caching.
55
explain,WONTFIX,Describe a specific model.
66
image-create,image create,Create a new image.
77
image-create-via-import, image create --import,"EXPERIMENTAL: Create a new image via image import using glance-direct import method. Missing support for web-download, copy-image and glance-download import methods. The OSC command is also missing support for importing image to specified store as well as all stores (--store, --stores, --all-stores) and skip or stop processing if import fails to one of the store (--allow-failure)"

openstackclient/image/v2/cache.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# Copyright 2023 Red Hat.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
import copy
17+
import datetime
18+
import logging
19+
20+
from osc_lib.command import command
21+
from osc_lib import exceptions
22+
from osc_lib import utils
23+
24+
from openstackclient.i18n import _
25+
26+
27+
LOG = logging.getLogger(__name__)
28+
29+
30+
def _format_image_cache(cached_images):
31+
"""Format image cache to make it more consistent with OSC operations."""
32+
33+
image_list = []
34+
for item in cached_images:
35+
if item == "cached_images":
36+
for image in cached_images[item]:
37+
image_obj = copy.deepcopy(image)
38+
image_obj['state'] = 'cached'
39+
image_obj[
40+
'last_accessed'
41+
] = datetime.datetime.utcfromtimestamp(
42+
image['last_accessed']
43+
).isoformat()
44+
image_obj[
45+
'last_modified'
46+
] = datetime.datetime.utcfromtimestamp(
47+
image['last_modified']
48+
).isoformat()
49+
image_list.append(image_obj)
50+
elif item == "queued_images":
51+
for image in cached_images[item]:
52+
image = {'image_id': image}
53+
image.update(
54+
{
55+
'state': 'queued',
56+
'last_accessed': 'N/A',
57+
'last_modified': 'N/A',
58+
'size': 'N/A',
59+
'hits': 'N/A',
60+
}
61+
)
62+
image_list.append(image)
63+
return image_list
64+
65+
66+
class ListCachedImage(command.Lister):
67+
_description = _("Get Cache State")
68+
69+
def get_parser(self, prog_name):
70+
parser = super().get_parser(prog_name)
71+
return parser
72+
73+
def take_action(self, parsed_args):
74+
image_client = self.app.client_manager.image
75+
76+
# List of Cache data received
77+
data = _format_image_cache(dict(image_client.get_image_cache()))
78+
columns = [
79+
'image_id',
80+
'state',
81+
'last_accessed',
82+
'last_modified',
83+
'size',
84+
'hits',
85+
]
86+
column_headers = [
87+
"ID",
88+
"State",
89+
"Last Accessed (UTC)",
90+
"Last Modified (UTC)",
91+
"Size",
92+
"Hits",
93+
]
94+
95+
return (
96+
column_headers,
97+
(
98+
utils.get_dict_properties(
99+
image,
100+
columns,
101+
)
102+
for image in data
103+
),
104+
)
105+
106+
107+
class QueueCachedImage(command.Command):
108+
_description = _("Queue image(s) for caching.")
109+
110+
def get_parser(self, prog_name):
111+
parser = super().get_parser(prog_name)
112+
parser.add_argument(
113+
"images",
114+
metavar="<image>",
115+
nargs="+",
116+
help=_("Image to display (name or ID)"),
117+
)
118+
return parser
119+
120+
def take_action(self, parsed_args):
121+
image_client = self.app.client_manager.image
122+
123+
failures = 0
124+
for image in parsed_args.images:
125+
try:
126+
image_obj = image_client.find_image(
127+
image,
128+
ignore_missing=False,
129+
)
130+
image_client.queue_image(image_obj.id)
131+
except Exception as e:
132+
failures += 1
133+
msg = _(
134+
"Failed to queue image with name or "
135+
"ID '%(image)s': %(e)s"
136+
)
137+
LOG.error(msg, {'image': image, 'e': e})
138+
139+
if failures > 0:
140+
total = len(parsed_args.images)
141+
msg = _("Failed to queue %(failures)s of %(total)s images") % {
142+
'failures': failures,
143+
'total': total,
144+
}
145+
raise exceptions.CommandError(msg)
146+
147+
148+
class DeleteCachedImage(command.Command):
149+
_description = _("Delete image(s) from cache")
150+
151+
def get_parser(self, prog_name):
152+
parser = super().get_parser(prog_name)
153+
parser.add_argument(
154+
"images",
155+
metavar="<image>",
156+
nargs="+",
157+
help=_("Image(s) to delete (name or ID)"),
158+
)
159+
return parser
160+
161+
def take_action(self, parsed_args):
162+
failures = 0
163+
image_client = self.app.client_manager.image
164+
for image in parsed_args.images:
165+
try:
166+
image_obj = image_client.find_image(
167+
image,
168+
ignore_missing=False,
169+
)
170+
image_client.cache_delete_image(image_obj.id)
171+
except Exception as e:
172+
failures += 1
173+
msg = _(
174+
"Failed to delete image with name or "
175+
"ID '%(image)s': %(e)s"
176+
)
177+
LOG.error(msg, {'image': image, 'e': e})
178+
179+
if failures > 0:
180+
total = len(parsed_args.images)
181+
msg = _("Failed to delete %(failures)s of %(total)s images.") % {
182+
'failures': failures,
183+
'total': total,
184+
}
185+
raise exceptions.CommandError(msg)
186+
187+
188+
class ClearCachedImage(command.Command):
189+
_description = _("Clear all images from cache, queue or both")
190+
191+
def get_parser(self, prog_name):
192+
parser = super().get_parser(prog_name)
193+
parser.add_argument(
194+
"--cache",
195+
action="store_const",
196+
const="cache",
197+
dest="target",
198+
help=_("Clears all the cached images"),
199+
)
200+
parser.add_argument(
201+
"--queue",
202+
action="store_const",
203+
const="queue",
204+
dest="target",
205+
help=_("Clears all the queued images"),
206+
)
207+
return parser
208+
209+
def take_action(self, parsed_args):
210+
image_client = self.app.client_manager.image
211+
212+
target = parsed_args.target
213+
try:
214+
image_client.clear_cache(target)
215+
except Exception:
216+
msg = _("Failed to clear image cache")
217+
LOG.error(msg)
218+
raise exceptions.CommandError(msg)

openstackclient/tests/unit/image/v2/fakes.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import uuid
1818

1919
from openstack.image.v2 import _proxy
20+
from openstack.image.v2 import cache
2021
from openstack.image.v2 import image
2122
from openstack.image.v2 import member
2223
from openstack.image.v2 import metadef_namespace
@@ -238,6 +239,28 @@ def create_tasks(attrs=None, count=2):
238239
return tasks
239240

240241

242+
def create_cache(attrs=None):
243+
attrs = attrs or {}
244+
cache_info = {
245+
'cached_images': [
246+
{
247+
'hits': 0,
248+
'image_id': '1a56983c-f71f-490b-a7ac-6b321a18935a',
249+
'last_accessed': 1671699579.444378,
250+
'last_modified': 1671699579.444378,
251+
'size': 0,
252+
},
253+
],
254+
'queued_images': [
255+
'3a4560a1-e585-443e-9b39-553b46ec92d1',
256+
'6f99bf80-2ee6-47cf-acfe-1f1fabb7e810',
257+
],
258+
}
259+
cache_info.update(attrs)
260+
261+
return cache.Cache(**cache_info)
262+
263+
241264
def create_one_metadef_namespace(attrs=None):
242265
"""Create a fake MetadefNamespace member.
243266

0 commit comments

Comments
 (0)