Merge "Adding Images support to CLI"

This commit is contained in:
Jenkins
2015-10-06 00:43:10 +00:00
committed by Gerrit Code Review
4 changed files with 667 additions and 0 deletions

View File

@@ -0,0 +1,306 @@
# Copyright (c) 2015 Mirantis Inc.
#
# 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.
from cliff import command
from cliff import lister
from cliff import show
from openstackclient.common import utils as osc_utils
from oslo_log import log as logging
from saharaclient.osc.v1 import utils
class ListImages(lister.Lister):
"""Lists registered images"""
log = logging.getLogger(__name__ + ".ListImages")
def get_parser(self, prog_name):
parser = super(ListImages, self).get_parser(prog_name)
parser.add_argument(
'--long',
action='store_true',
default=False,
help='List additional fields in output',
)
parser.add_argument(
'--name',
metavar="<name-regex>",
help="Regular expression to match image name"
)
parser.add_argument(
'--tags',
metavar="<tag>",
nargs="+",
help="List images with specific tag(s)"
)
parser.add_argument(
'--username',
metavar="<username>",
help="List images with specific username"
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
client = self.app.client_manager.data_processing
search_opts = {'tags': parsed_args.tags} if parsed_args.tags else {}
data = client.images.list(search_opts=search_opts)
if parsed_args.name:
data = utils.get_by_name_substring(data, parsed_args.name)
if parsed_args.username:
data = [i for i in data if parsed_args.username in i.username]
if parsed_args.long:
columns = ('name', 'id', 'username', 'tags', 'status',
'description')
column_headers = [c.capitalize() for c in columns]
else:
columns = ('name', 'id', 'username', 'tags')
column_headers = [c.capitalize() for c in columns]
return (
column_headers,
(osc_utils.get_item_properties(
s,
columns,
formatters={
'tags': osc_utils.format_list
},
) for s in data)
)
class ShowImage(show.ShowOne):
"""Display image details"""
log = logging.getLogger(__name__ + ".ShowImage")
def get_parser(self, prog_name):
parser = super(ShowImage, self).get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image>",
help="Name or id of the image to display",
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
client = self.app.client_manager.data_processing
data = utils.get_resource(
client.images, parsed_args.image).to_dict()
data['tags'] = osc_utils.format_list(data['tags'])
fields = ['name', 'id', 'username', 'tags', 'status', 'description']
data = utils.prepare_data(data, fields)
return self.dict2columns(data)
class RegisterImage(show.ShowOne):
"""Register an image"""
log = logging.getLogger(__name__ + ".RegisterImage")
def get_parser(self, prog_name):
parser = super(RegisterImage, self).get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image-id>",
help="Id of the image to register",
)
parser.add_argument(
"--username",
metavar="<username>",
help="Username of privileged user in the image [REQUIRED]",
required=True
)
parser.add_argument(
"--description",
metavar="<description>",
help="Description of the image",
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
client = self.app.client_manager.data_processing
description = parsed_args.description or ''
data = client.images.update_image(
parsed_args.image, user_name=parsed_args.username,
desc=description).to_dict()
data['tags'] = osc_utils.format_list(data['tags'])
fields = ['name', 'id', 'username', 'tags', 'status', 'description']
data = utils.prepare_data(data, fields)
return self.dict2columns(data)
class UnregisterImage(command.Command):
"""Unregister image(s)"""
log = logging.getLogger(__name__ + ".RegisterImage")
def get_parser(self, prog_name):
parser = super(UnregisterImage, self).get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image>",
nargs="+",
help="Name(s) or id(s) of the image(s) to unregister",
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
client = self.app.client_manager.data_processing
for image in parsed_args.image:
image_id = utils.get_resource(client.images, image).id
client.images.unregister_image(image_id)
class SetImageTags(show.ShowOne):
"""Set image tags (Replace current image tags with provided ones)"""
log = logging.getLogger(__name__ + ".AddImageTags")
def get_parser(self, prog_name):
parser = super(SetImageTags, self).get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image>",
help="Name or id of the image",
)
parser.add_argument(
'--tags',
metavar="<tag>",
nargs="+",
required=True,
help="Tag(s) to set [REQUIRED]"
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
client = self.app.client_manager.data_processing
image_id = utils.get_resource(client.images, parsed_args.image).id
data = client.images.update_tags(image_id, parsed_args.tags).to_dict()
data['tags'] = osc_utils.format_list(data['tags'])
fields = ['name', 'id', 'username', 'tags', 'status', 'description']
data = utils.prepare_data(data, fields)
return self.dict2columns(data)
class AddImageTags(show.ShowOne):
"""Add image tags"""
log = logging.getLogger(__name__ + ".AddImageTags")
def get_parser(self, prog_name):
parser = super(AddImageTags, self).get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image>",
help="Name or id of the image",
)
parser.add_argument(
'--tags',
metavar="<tag>",
nargs="+",
required=True,
help="Tag(s) to add [REQUIRED]"
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
client = self.app.client_manager.data_processing
image = utils.get_resource(client.images, parsed_args.image)
parsed_args.tags.extend(image.tags)
data = client.images.update_tags(
image.id, list(set(parsed_args.tags))).to_dict()
data['tags'] = osc_utils.format_list(data['tags'])
fields = ['name', 'id', 'username', 'tags', 'status', 'description']
data = utils.prepare_data(data, fields)
return self.dict2columns(data)
class RemoveImageTags(show.ShowOne):
"""Remove image tags"""
log = logging.getLogger(__name__ + ".RemoveImageTags")
def get_parser(self, prog_name):
parser = super(RemoveImageTags, self).get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image>",
help="Name or id of the image",
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
'--tags',
metavar="<tag>",
nargs="+",
help="Tag(s) to remove"
),
group.add_argument(
'--all',
action='store_true',
default=False,
help='Remove all tags from image',
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
client = self.app.client_manager.data_processing
image = utils.get_resource(client.images, parsed_args.image)
if parsed_args.all:
data = client.images.update_tags(image.id, []).to_dict()
else:
parsed_args.tags = parsed_args.tags or []
new_tags = list(set(image.tags) - set(parsed_args.tags))
data = client.images.update_tags(image.id, new_tags).to_dict()
data['tags'] = osc_utils.format_list(data['tags'])
fields = ['name', 'id', 'username', 'tags', 'status', 'description']
data = utils.prepare_data(data, fields)
return self.dict2columns(data)

View File

@@ -40,3 +40,7 @@ def prepare_data(data, fields):
def prepare_column_headers(columns):
return [c.replace('_', ' ').capitalize() for c in columns]
def get_by_name_substring(data, name):
return [obj for obj in data if name in obj.name]

View File

@@ -0,0 +1,349 @@
# Copyright (c) 2015 Mirantis Inc.
#
# 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.
from openstackclient.tests import utils as osc_utils
from saharaclient.api import images as api_images
from saharaclient.osc.v1 import images as osc_images
from saharaclient.tests.unit.osc.v1 import fakes
IMAGE_INFO = {'id': 'id', 'name': 'image', 'username': 'ubuntu',
'status': "Active", 'tags': ['fake', '0.1'],
'description': 'Image for tests'}
class TestImages(fakes.TestDataProcessing):
def setUp(self):
super(TestImages, self).setUp()
self.image_mock = (
self.app.client_manager.data_processing.images)
self.image_mock.reset_mock()
class TestListImages(TestImages):
def setUp(self):
super(TestListImages, self).setUp()
self.image_mock.list.return_value = [api_images.Image(
None, IMAGE_INFO)]
# Command to test
self.cmd = osc_images.ListImages(self.app, None)
def test_images_list_no_options(self):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = ['Name', 'Id', 'Username', 'Tags']
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [('image', 'id', 'ubuntu', '0.1, fake')]
self.assertEqual(expected_data, list(data))
def test_images_list_long(self):
arglist = ['--long']
verifylist = [('long', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = ['Name', 'Id', 'Username', 'Tags', 'Status',
'Description']
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [('image', 'id', 'ubuntu', '0.1, fake', 'Active',
'Image for tests')]
self.assertEqual(expected_data, list(data))
def test_images_list_successful_selection(self):
arglist = ['--name', 'image', '--tags', 'fake', '--username', 'ubuntu']
verifylist = [('name', 'image'), ('tags', ['fake']),
('username', 'ubuntu')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.image_mock.list.assert_called_once_with(
search_opts={'tags': ['fake']})
# Check that columns are correct
expected_columns = ['Name', 'Id', 'Username', 'Tags']
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = [('image', 'id', 'ubuntu', '0.1, fake')]
self.assertEqual(expected_data, list(data))
def test_images_list_with_name_nothing_selected(self):
arglist = ['--name', 'img']
verifylist = [('name', 'img')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = ['Name', 'Id', 'Username', 'Tags']
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = []
self.assertEqual(expected_data, list(data))
def test_images_list_with_username_nothing_selected(self):
arglist = ['--username', 'fedora']
verifylist = [('username', 'fedora')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that columns are correct
expected_columns = ['Name', 'Id', 'Username', 'Tags']
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = []
self.assertEqual(expected_data, list(data))
class TestShowImage(TestImages):
def setUp(self):
super(TestShowImage, self).setUp()
self.image_mock.find_unique.return_value = api_images.Image(
None, IMAGE_INFO)
# Command to test
self.cmd = osc_images.ShowImage(self.app, None)
def test_image_show_no_options(self):
arglist = []
verifylist = []
self.assertRaises(osc_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
def test_image_show(self):
arglist = ['image']
verifylist = [('image', 'image')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.image_mock.find_unique.assert_called_once_with(name='image')
# Check that columns are correct
expected_columns = ('Description', 'Id', 'Name', 'Status', 'Tags',
'Username')
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = ['Image for tests', 'id', 'image', 'Active',
'0.1, fake', 'ubuntu']
self.assertEqual(expected_data, list(data))
class TestRegisterImage(TestImages):
def setUp(self):
super(TestRegisterImage, self).setUp()
self.image_mock.update_image.return_value = api_images.Image(
None, IMAGE_INFO)
# Command to test
self.cmd = osc_images.RegisterImage(self.app, None)
def test_image_register_without_username(self):
arglist = ['id']
verifylist = [('image', 'id')]
self.assertRaises(osc_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
def test_image_register(self):
arglist = ['id', '--username', 'ubuntu']
verifylist = [('image', 'id'), ('username', 'ubuntu')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.image_mock.update_image.assert_called_once_with(
'id', desc='', user_name='ubuntu')
# Check that columns are correct
expected_columns = ('Description', 'Id', 'Name', 'Status', 'Tags',
'Username')
self.assertEqual(expected_columns, columns)
# Check that data is correct
expected_data = ['Image for tests', 'id', 'image', 'Active',
'0.1, fake', 'ubuntu']
self.assertEqual(expected_data, list(data))
class TestUnregisterImage(TestImages):
def setUp(self):
super(TestUnregisterImage, self).setUp()
self.image_mock.find_unique.return_value = api_images.Image(
None, IMAGE_INFO)
# Command to test
self.cmd = osc_images.UnregisterImage(self.app, None)
def test_image_unregister_no_options(self):
arglist = []
verifylist = []
self.assertRaises(osc_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
def test_image_unregister(self):
arglist = ['image']
verifylist = [('image', ['image'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.image_mock.find_unique.assert_called_once_with(name='image')
self.image_mock.unregister_image.assert_called_once_with('id')
class TestSetImageTags(TestImages):
def setUp(self):
super(TestSetImageTags, self).setUp()
image_info = IMAGE_INFO.copy()
image_info['tags'] = []
self.image_mock.find_unique.return_value = api_images.Image(
None, image_info)
self.image_mock.update_tags.return_value = api_images.Image(
None, image_info)
# Command to test
self.cmd = osc_images.SetImageTags(self.app, None)
def test_image_tags_set_without_tags(self):
arglist = ['id']
verifylist = [('image', 'id')]
self.assertRaises(osc_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
def test_image_tags_set(self):
arglist = ['image', '--tags', 'fake', '0.1']
verifylist = [('image', 'image'), ('tags', ['fake', '0.1'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.image_mock.find_unique.assert_called_with(name='image')
self.image_mock.update_tags.assert_called_once_with(
'id', ['fake', '0.1'])
class TestAddImageTags(TestImages):
def setUp(self):
super(TestAddImageTags, self).setUp()
image_info = IMAGE_INFO.copy()
image_info['tags'] = []
self.image_mock.update_tags.return_value = api_images.Image(
None, image_info)
self.image_mock.find_unique.return_value = api_images.Image(
None, image_info)
# Command to test
self.cmd = osc_images.AddImageTags(self.app, None)
def test_image_tags_add_without_tags(self):
arglist = ['id']
verifylist = [('image', 'id')]
self.assertRaises(osc_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
def test_image_tags_add(self):
arglist = ['image', '--tags', 'fake']
verifylist = [('image', 'image'), ('tags', ['fake'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.image_mock.find_unique.assert_called_with(name='image')
self.image_mock.update_tags.assert_called_once_with(
'id', ['fake'])
class TestRemoveImageTags(TestImages):
def setUp(self):
super(TestRemoveImageTags, self).setUp()
self.image_mock.update_tags.return_value = api_images.Image(
None, IMAGE_INFO)
self.image_mock.find_unique.return_value = api_images.Image(
None, IMAGE_INFO)
# Command to test
self.cmd = osc_images.RemoveImageTags(self.app, None)
def test_image_tags_remove_both_options(self):
arglist = ['id', '--all', '--tags', 'fake']
verifylist = [('image', 'id'), ('all', True), ('tags', ['fake'])]
self.assertRaises(osc_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
def test_image_tags_remove(self):
arglist = ['image', '--tags', 'fake']
verifylist = [('image', 'image'), ('tags', ['fake'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.image_mock.find_unique.assert_called_with(name='image')
self.image_mock.update_tags.assert_called_once_with(
'id', ['0.1'])
def test_image_tags_remove_all(self):
arglist = ['image', '--all']
verifylist = [('image', 'image'), ('all', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
# Check that correct arguments were passed
self.image_mock.find_unique.assert_called_with(name='image')
self.image_mock.update_tags.assert_called_once_with(
'id', [])

View File

@@ -46,6 +46,14 @@ openstack.data_processing.v1 =
dataprocessing_data_source_delete = saharaclient.osc.v1.data_sources:DeleteDataSource
dataprocessing_data_source_update = saharaclient.osc.v1.data_sources:UpdateDataSource
dataprocessing_image_list = saharaclient.osc.v1.images:ListImages
dataprocessing_image_show = saharaclient.osc.v1.images:ShowImage
dataprocessing_image_register = saharaclient.osc.v1.images:RegisterImage
dataprocessing_image_unregister = saharaclient.osc.v1.images:UnregisterImage
dataprocessing_image_tags_add = saharaclient.osc.v1.images:AddImageTags
dataprocessing_image_tags_remove = saharaclient.osc.v1.images:RemoveImageTags
dataprocessing_image_tags_set = saharaclient.osc.v1.images:SetImageTags
[build_sphinx]
all_files = 1
build-dir = doc/build