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:
parent
d163a20904
commit
c9d445fc4b
@ -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.
|
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-delete,,Delete image from specific store.
|
||||||
stores-info,,Print available backends from Glance.
|
stores-info,,Print available backends from Glance.
|
||||||
task-create,,Create a new task.
|
task-create,WONTFIX,Create a new task.
|
||||||
task-list,,List tasks you can access.
|
task-list,image task list,List tasks you can access.
|
||||||
task-show,image task show,Describe a specific task.
|
task-show,image task show,Describe a specific task.
|
||||||
bash-completion,complete,Prints arguments for bash_completion.
|
bash-completion,complete,Prints arguments for bash_completion.
|
||||||
help,help,Display help about this program or one of its subcommands.
|
help,help,Display help about this program or one of its subcommands.
|
||||||
|
|
@ -12,9 +12,14 @@
|
|||||||
|
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.command import command
|
from osc_lib.command import command
|
||||||
|
from osc_lib import utils
|
||||||
|
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
_formatters = {
|
||||||
|
'tags': format_columns.ListColumn,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _format_task(task):
|
def _format_task(task):
|
||||||
"""Format an task to make it more consistent with OSC operations."""
|
"""Format an task to make it more consistent with OSC operations."""
|
||||||
@ -76,3 +81,99 @@ class ShowTask(command.ShowOne):
|
|||||||
info = _format_task(task)
|
info = _format_task(task)
|
||||||
|
|
||||||
return zip(*sorted(info.items()))
|
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
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@ -52,6 +52,9 @@ class FakeImagev2Client:
|
|||||||
self.management_url = kwargs['endpoint']
|
self.management_url = kwargs['endpoint']
|
||||||
self.version = 2.0
|
self.version = 2.0
|
||||||
|
|
||||||
|
self.tasks = mock.Mock()
|
||||||
|
self.tasks.resource_class = fakes.FakeResource(None, {})
|
||||||
|
|
||||||
|
|
||||||
class TestImagev2(utils.TestCommand):
|
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
|
# https://github.com/openstack/glance/blob/24.0.0/glance/api/v2/tasks.py#L186-L190
|
||||||
'type': 'import',
|
'type': 'import',
|
||||||
'updated_at': '2016-06-29T16:13:07Z',
|
'updated_at': '2016-06-29T16:13:07Z',
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Overwrite default attributes if there are some attributes set
|
# Overwrite default attributes if there are some attributes set
|
||||||
task_info.update(attrs)
|
task_info.update(attrs)
|
||||||
|
|
||||||
return task.Task(**task_info)
|
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
|
||||||
|
@ -78,3 +78,110 @@ class TestTaskShow(TestTask):
|
|||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertCountEqual(self.data, data)
|
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,
|
||||||
|
)
|
||||||
|
@ -2,3 +2,5 @@
|
|||||||
features:
|
features:
|
||||||
- |
|
- |
|
||||||
Add ``image task show`` command to show a task for the image service.
|
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.
|
||||||
|
@ -383,6 +383,7 @@ openstack.image.v2 =
|
|||||||
image_set = openstackclient.image.v2.image:SetImage
|
image_set = openstackclient.image.v2.image:SetImage
|
||||||
image_unset = openstackclient.image.v2.image:UnsetImage
|
image_unset = openstackclient.image.v2.image:UnsetImage
|
||||||
image_task_show = openstackclient.image.v2.task:ShowTask
|
image_task_show = openstackclient.image.v2.task:ShowTask
|
||||||
|
image_task_list = openstackclient.image.v2.task:ListTask
|
||||||
|
|
||||||
openstack.network.v2 =
|
openstack.network.v2 =
|
||||||
address_group_create = openstackclient.network.v2.address_group:CreateAddressGroup
|
address_group_create = openstackclient.network.v2.address_group:CreateAddressGroup
|
||||||
|
Loading…
Reference in New Issue
Block a user