image: Add 'image task list' command

This replaces the 'glance task-list' command.

  $ openstack image task list

We also indicate that the 'image task create' command will never be
implemented. This is an admin-only API that isn't really intended to be
used by humans thus it does not need an OSC command implementation.

Change-Id: Id8a943a5443782fc70c0fbf3639f5aa17b9d30af
This commit is contained in:
Stephen Finucane 2022-07-07 12:31:44 +01:00
parent d163a20904
commit c9d445fc4b
6 changed files with 233 additions and 3 deletions

View File

@ -53,8 +53,8 @@ member-list,,Describe sharing permissions by image.
member-update,image set --accept --reject --status,Update the status of a member for a given image.
stores-delete,,Delete image from specific store.
stores-info,,Print available backends from Glance.
task-create,,Create a new task.
task-list,,List tasks you can access.
task-create,WONTFIX,Create a new task.
task-list,image task list,List tasks you can access.
task-show,image task show,Describe a specific task.
bash-completion,complete,Prints arguments for bash_completion.
help,help,Display help about this program or one of its subcommands.

1 explain WONTFIX Describe a specific model.
53 member-update image set --accept --reject --status Update the status of a member for a given image.
54 stores-delete Delete image from specific store.
55 stores-info Print available backends from Glance.
56 task-create WONTFIX Create a new task.
57 task-list image task list List tasks you can access.
58 task-show image task show Describe a specific task.
59 bash-completion complete Prints arguments for bash_completion.
60 help help Display help about this program or one of its subcommands.

View File

@ -12,9 +12,14 @@
from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
from openstackclient.i18n import _
_formatters = {
'tags': format_columns.ListColumn,
}
def _format_task(task):
"""Format an task to make it more consistent with OSC operations."""
@ -76,3 +81,99 @@ class ShowTask(command.ShowOne):
info = _format_task(task)
return zip(*sorted(info.items()))
class ListTask(command.Lister):
_description = _('List tasks')
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
'--sort-key',
metavar='<key>[:<field>]',
help=_(
'Sorts the response by one of the following attributes: '
'created_at, expires_at, id, status, type, updated_at. '
'(default is created_at) '
'(multiple keys and directions can be specified separated '
'by comma)'
),
)
parser.add_argument(
'--sort-dir',
metavar='<key>[:<direction>]',
help=_(
'Sort output by selected keys and directions (asc or desc) '
'(default: name:desc) '
'(multiple keys and directions can be specified separated '
'by comma)'
),
)
parser.add_argument(
'--limit',
metavar='<num-tasks>',
type=int,
help=_('Maximum number of tasks to display.'),
)
parser.add_argument(
'--marker',
metavar='<task>',
help=_(
'The last task of the previous page. '
'Display list of tasks after marker. '
'Display all tasks if not specified. '
'(name or ID)'
),
)
parser.add_argument(
'--type',
metavar='<type>',
choices=['import'],
help=_('Filters the response by a task type.'),
)
parser.add_argument(
'--status',
metavar='<status>',
choices=[
'pending',
'processing',
'success',
'failure',
],
help=_('Filter tasks based on status.'),
)
return parser
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
columns = ('id', 'type', 'status', 'owner_id')
column_headers = ('ID', 'Type', 'Status', 'Owner')
kwargs = {}
copy_attrs = {
'sort_key',
'sort_dir',
'limit',
'marker',
'type',
'status',
}
for attr in copy_attrs:
val = getattr(parsed_args, attr, None)
if val is not None:
# Only include a value in kwargs for attributes that are
# actually present on the command line
kwargs[attr] = val
data = image_client.tasks(**kwargs)
return (
column_headers,
(
utils.get_item_properties(s, columns, formatters=_formatters)
for s in data
),
)

View File

@ -52,6 +52,9 @@ class FakeImagev2Client:
self.management_url = kwargs['endpoint']
self.version = 2.0
self.tasks = mock.Mock()
self.tasks.resource_class = fakes.FakeResource(None, {})
class TestImagev2(utils.TestCommand):
@ -176,10 +179,26 @@ def create_one_task(attrs=None):
# https://github.com/openstack/glance/blob/24.0.0/glance/api/v2/tasks.py#L186-L190
'type': 'import',
'updated_at': '2016-06-29T16:13:07Z',
}
# Overwrite default attributes if there are some attributes set
task_info.update(attrs)
return task.Task(**task_info)
def create_tasks(attrs=None, count=2):
"""Create multiple fake tasks.
:param attrs: A dictionary with all attributes of Task
:type attrs: dict
:param count: The number of tasks to be faked
:type count: int
:return: A list of fake Task objects
:rtype: list
"""
tasks = []
for n in range(0, count):
tasks.append(create_one_task(attrs))
return tasks

View File

@ -78,3 +78,110 @@ class TestTaskShow(TestTask):
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
class TestTaskList(TestTask):
tasks = image_fakes.create_tasks()
columns = (
'ID',
'Type',
'Status',
'Owner',
)
datalist = [
(
task.id,
task.type,
task.status,
task.owner_id,
)
for task in tasks
]
def setUp(self):
super().setUp()
self.client.tasks.side_effect = [self.tasks, []]
# Get the command object to test
self.cmd = task.ListTask(self.app, None)
def test_task_list_no_options(self):
arglist = []
verifylist = [
('sort_key', None),
('sort_dir', None),
('limit', None),
('marker', None),
('type', None),
('status', None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.client.tasks.assert_called_with()
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.datalist, data)
def test_task_list_sort_key_option(self):
arglist = ['--sort-key', 'created_at']
verifylist = [('sort_key', 'created_at')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.client.tasks.assert_called_with(
sort_key=parsed_args.sort_key,
)
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.datalist, data)
def test_task_list_sort_dir_option(self):
arglist = ['--sort-dir', 'desc']
verifylist = [('sort_dir', 'desc')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.client.tasks.assert_called_with(
sort_dir=parsed_args.sort_dir,
)
def test_task_list_pagination_options(self):
arglist = ['--limit', '1', '--marker', self.tasks[0].id]
verifylist = [('limit', 1), ('marker', self.tasks[0].id)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.client.tasks.assert_called_with(
limit=parsed_args.limit,
marker=parsed_args.marker,
)
def test_task_list_type_option(self):
arglist = ['--type', self.tasks[0].type]
verifylist = [('type', self.tasks[0].type)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.client.tasks.assert_called_with(
type=self.tasks[0].type,
)
def test_task_list_status_option(self):
arglist = ['--status', self.tasks[0].status]
verifylist = [('status', self.tasks[0].status)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.client.tasks.assert_called_with(
status=self.tasks[0].status,
)

View File

@ -2,3 +2,5 @@
features:
- |
Add ``image task show`` command to show a task for the image service.
- |
Add ``image task list`` command to list tasks for the image service.

View File

@ -383,6 +383,7 @@ openstack.image.v2 =
image_set = openstackclient.image.v2.image:SetImage
image_unset = openstackclient.image.v2.image:UnsetImage
image_task_show = openstackclient.image.v2.task:ShowTask
image_task_list = openstackclient.image.v2.task:ListTask
openstack.network.v2 =
address_group_create = openstackclient.network.v2.address_group:CreateAddressGroup