diff --git a/saharaclient/osc/v1/data_sources.py b/saharaclient/osc/v1/data_sources.py index 079834d3..b5532e59 100644 --- a/saharaclient/osc/v1/data_sources.py +++ b/saharaclient/osc/v1/data_sources.py @@ -21,6 +21,10 @@ from oslo_log import log as logging from saharaclient.osc.v1 import utils +DATA_SOURCE_FIELDS = ['name', 'id', 'type', 'url', 'description', 'is_public', + 'is_protected'] +DATA_SOURCE_CHOICES = ["swift", "hdfs", "maprfs", "manila"] + class CreateDataSource(show.ShowOne): """Creates data source""" @@ -38,8 +42,9 @@ class CreateDataSource(show.ShowOne): parser.add_argument( '--type', metavar="", - choices=["swift", "hdfs", "maprfs"], - help="Type of the data source (swift, hdfs or maprfs) [REQUIRED]", + choices=DATA_SOURCE_CHOICES, + help="Type of the data source (%s) " + "[REQUIRED]" % ', '.join(DATA_SOURCE_CHOICES), required=True ) parser.add_argument( @@ -63,6 +68,18 @@ class CreateDataSource(show.ShowOne): metavar="", help="Description of the data source" ) + parser.add_argument( + '--public', + action='store_true', + default=False, + help='Make the data source public', + ) + parser.add_argument( + '--protected', + action='store_true', + default=False, + help='Make the data source protected', + ) return parser def take_action(self, parsed_args): @@ -74,10 +91,11 @@ class CreateDataSource(show.ShowOne): name=parsed_args.name, description=description, data_source_type=parsed_args.type, url=parsed_args.url, credential_user=parsed_args.username, - credential_pass=parsed_args.password).to_dict() + credential_pass=parsed_args.password, + is_public=parsed_args.public, + is_protected=parsed_args.protected).to_dict() - fields = ['name', 'id', 'type', 'url', 'description'] - data = utils.prepare_data(data, fields) + data = utils.prepare_data(data, DATA_SOURCE_FIELDS) return self.dict2columns(data) @@ -98,8 +116,9 @@ class ListDataSources(lister.Lister): parser.add_argument( '--type', metavar="", - choices=["swift", "hdfs", "maprfs"], - help="List data sources of specific type (swift, hdfs or maprfs)" + choices=DATA_SOURCE_CHOICES, + help="List data sources of specific type " + "(%s)" % ', '.join(DATA_SOURCE_CHOICES) ) return parser @@ -112,12 +131,12 @@ class ListDataSources(lister.Lister): data = client.data_sources.list(search_opts=search_opts) if parsed_args.long: - columns = ('name', 'id', 'type', 'url', 'description') - column_headers = [c.capitalize() for c in columns] + columns = DATA_SOURCE_FIELDS + column_headers = utils.prepare_column_headers(columns) else: columns = ('name', 'id', 'type') - column_headers = [c.capitalize() for c in columns] + column_headers = utils.prepare_column_headers(columns) return ( column_headers, @@ -149,9 +168,7 @@ class ShowDataSource(show.ShowOne): data = utils.get_resource( client.data_sources, parsed_args.data_source).to_dict() - - fields = ['name', 'id', 'type', 'url', 'description'] - data = utils.prepare_data(data, fields) + data = utils.prepare_data(data, DATA_SOURCE_FIELDS) return self.dict2columns(data) @@ -166,7 +183,8 @@ class DeleteDataSource(command.Command): parser.add_argument( "data_source", metavar="", - help="Name or id of the data source to delete", + nargs="+", + help="Name(s) or id(s) of the data source(s) to delete", ) return parser @@ -174,6 +192,113 @@ class DeleteDataSource(command.Command): def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) client = self.app.client_manager.data_processing - data_source_id = utils.get_resource( + for ds in parsed_args.data_source: + data_source_id = utils.get_resource( + client.data_sources, ds).id + client.data_sources.delete(data_source_id) + + +class UpdateDataSource(show.ShowOne): + """Update data source""" + + log = logging.getLogger(__name__ + ".UpdateDataSource") + + def get_parser(self, prog_name): + parser = super(UpdateDataSource, self).get_parser(prog_name) + parser.add_argument( + 'data_source', + metavar="", + help="Name or id of the data source", + ) + parser.add_argument( + '--name', + metavar="", + help="New name of the data source", + ) + parser.add_argument( + '--type', + metavar="", + choices=DATA_SOURCE_CHOICES, + help="Type of the data source " + "(%s)" % ', '.join(DATA_SOURCE_CHOICES) + ) + parser.add_argument( + '--url', + metavar="", + help="Url for the data source" + ) + parser.add_argument( + '--username', + metavar="", + help="Username for accessing the data source url" + ) + parser.add_argument( + '--password', + metavar="", + help="Password for accessing the data source url" + ) + parser.add_argument( + '--description', + metavar="", + help="Description of the data source" + ) + public = parser.add_mutually_exclusive_group() + public.add_argument( + '--public', + action='store_true', + default=None, + help='Make the data source public (Visible from other tenants)', + ) + public.add_argument( + '--private', + action='store_true', + default=None, + help='Make the data source private (Visible only from this ' + 'tenant)', + ) + protected = parser.add_mutually_exclusive_group() + protected.add_argument( + '--protected', + action='store_true', + default=None, + help='Make the data source protected', + ) + protected.add_argument( + '--unprotected', + action='store_true', + default=None, + help='Make the data source unprotected', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + client = self.app.client_manager.data_processing + + is_public = None + if parsed_args.public: + is_public = True + elif parsed_args.private: + is_public = False + + is_protected = None + if parsed_args.protected: + is_protected = True + elif parsed_args.unprotected: + is_protected = False + + update_fields = utils.create_dict_from_kwargs( + name=parsed_args.name, + description=parsed_args.description, + data_source_type=parsed_args.type, url=parsed_args.url, + credential_user=parsed_args.username, + credential_pass=parsed_args.password, + is_public=is_public, + is_protected=is_protected) + + ds_id = utils.get_resource( client.data_sources, parsed_args.data_source).id - client.data_sources.delete(data_source_id) + data = client.data_sources.update(ds_id, update_fields).data_source + data = utils.prepare_data(data, DATA_SOURCE_FIELDS) + + return self.dict2columns(data) diff --git a/saharaclient/osc/v1/plugins.py b/saharaclient/osc/v1/plugins.py index 3a992372..eb7f0a0c 100644 --- a/saharaclient/osc/v1/plugins.py +++ b/saharaclient/osc/v1/plugins.py @@ -18,10 +18,12 @@ from os import path from cliff import command from cliff import lister from cliff import show -from openstackclient.common import utils +from openstackclient.common import utils as osc_utils from oslo_log import log as logging from oslo_serialization import jsonutils +from saharaclient.osc.v1 import utils + class ListPlugins(lister.Lister): """Lists plugins""" @@ -46,19 +48,19 @@ class ListPlugins(lister.Lister): if parsed_args.long: columns = ('name', 'title', 'versions', 'description') - column_headers = [c.capitalize() for c in columns] + column_headers = utils.prepare_column_headers(columns) else: columns = ('name', 'versions') - column_headers = [c.capitalize() for c in columns] + column_headers = utils.prepare_column_headers(columns) return ( column_headers, - (utils.get_item_properties( + (osc_utils.get_item_properties( s, columns, formatters={ - 'versions': utils.format_list + 'versions': osc_utils.format_list }, ) for s in data) ) @@ -84,7 +86,7 @@ class ShowPlugin(show.ShowOne): client = self.app.client_manager.data_processing data = client.plugins.get(parsed_args.plugin).to_dict() - data['versions'] = utils.format_list(data['versions']) + data['versions'] = osc_utils.format_list(data['versions']) return self.dict2columns(data) diff --git a/saharaclient/osc/v1/utils.py b/saharaclient/osc/v1/utils.py index 1108a748..499d0ea0 100644 --- a/saharaclient/osc/v1/utils.py +++ b/saharaclient/osc/v1/utils.py @@ -13,25 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -from openstackclient.common import exceptions -from openstackclient.common import utils +import six + +from oslo_utils import uuidutils def get_resource(manager, name_or_id): - resource = utils.find_resource(manager, name_or_id) - if isinstance(resource, list): - if not resource: - msg = "No %s with a name or ID of '%s' exists." % \ - (manager.resource_class.__name__.lower(), name_or_id) - raise exceptions.CommandError(msg) - if len(resource) > 1: - msg = "More than one %s exists with the name '%s'." % \ - (manager.resource_class.__name__.lower(), name_or_id) - raise exceptions.CommandError(msg) - return resource[0] - + if uuidutils.is_uuid_like(name_or_id): + return manager.get(name_or_id) else: - return resource + return manager.find_unique(name=name_or_id) + + +def create_dict_from_kwargs(**kwargs): + return dict((k, v) for (k, v) in six.iteritems(kwargs) if v is not None) def prepare_data(data, fields): @@ -41,3 +36,7 @@ def prepare_data(data, fields): new_data[f.replace('_', ' ').capitalize()] = data[f] return new_data + + +def prepare_column_headers(columns): + return [c.replace('_', ' ').capitalize() for c in columns] diff --git a/saharaclient/tests/unit/osc/v1/test_data_sources.py b/saharaclient/tests/unit/osc/v1/test_data_sources.py index 34d7828a..b031a0b0 100644 --- a/saharaclient/tests/unit/osc/v1/test_data_sources.py +++ b/saharaclient/tests/unit/osc/v1/test_data_sources.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import mock + from openstackclient.tests import utils as osc_utils from saharaclient.api import data_sources as api_ds @@ -22,7 +24,8 @@ from saharaclient.tests.unit.osc.v1 import fakes DS_INFO = {'id': 'id', 'name': 'source', 'type': 'swift', 'url': 'swift://container.sahara/object', - 'description': 'Data Source for tests'} + 'description': 'Data Source for tests', + 'is_public': True, 'is_protected': True} class TestDataSources(fakes.TestDataProcessing): @@ -63,27 +66,30 @@ class TestCreateDataSource(TestDataSources): called_args = {'credential_pass': None, 'credential_user': None, 'data_source_type': 'swift', 'name': 'source', 'description': '', - 'url': 'swift://container.sahara/object'} + 'url': 'swift://container.sahara/object', + 'is_public': False, 'is_protected': False} self.ds_mock.create.assert_called_once_with(**called_args) # Check that columns are correct - expected_columns = ('Description', 'Id', 'Name', 'Type', 'Url') + expected_columns = ('Description', 'Id', 'Is protected', 'Is public', + 'Name', 'Type', 'Url') self.assertEqual(expected_columns, columns) # Check that data is correct - expected_data = ('Data Source for tests', 'id', 'source', 'swift', - 'swift://container.sahara/object') + expected_data = ('Data Source for tests', 'id', True, True, 'source', + 'swift', 'swift://container.sahara/object') self.assertEqual(expected_data, data) def test_data_sources_create_all_options(self): arglist = ['source', '--type', 'swift', '--url', 'swift://container.sahara/object', '--username', 'user', '--password', 'pass', '--description', - 'Data Source for tests'] + 'Data Source for tests', '--public', '--protected'] verifylist = [('name', 'source'), ('type', 'swift'), ('url', 'swift://container.sahara/object'), ('username', 'user'), ('password', 'pass'), - ('description', 'Data Source for tests')] + ('description', 'Data Source for tests'), + ('public', True), ('protected', True)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -93,16 +99,18 @@ class TestCreateDataSource(TestDataSources): called_args = {'credential_pass': 'pass', 'credential_user': 'user', 'data_source_type': 'swift', 'name': 'source', 'description': 'Data Source for tests', - 'url': 'swift://container.sahara/object'} + 'url': 'swift://container.sahara/object', + 'is_protected': True, 'is_public': True} self.ds_mock.create.assert_called_once_with(**called_args) # Check that columns are correct - expected_columns = ('Description', 'Id', 'Name', 'Type', 'Url') + expected_columns = ('Description', 'Id', 'Is protected', 'Is public', + 'Name', 'Type', 'Url') self.assertEqual(expected_columns, columns) # Check that data is correct - expected_data = ('Data Source for tests', 'id', 'source', 'swift', - 'swift://container.sahara/object') + expected_data = ('Data Source for tests', 'id', True, True, 'source', + 'swift', 'swift://container.sahara/object') self.assertEqual(expected_data, data) @@ -140,20 +148,21 @@ class TestListDataSources(TestDataSources): columns, data = self.cmd.take_action(parsed_args) # Check that columns are correct - expected_columns = ['Name', 'Id', 'Type', 'Url', 'Description'] + expected_columns = ['Name', 'Id', 'Type', 'Url', 'Description', + 'Is public', 'Is protected'] self.assertEqual(expected_columns, columns) # Check that data is correct expected_data = [('source', 'id', 'swift', 'swift://container.sahara/object', - 'Data Source for tests')] + 'Data Source for tests', True, True)] self.assertEqual(expected_data, list(data)) class TestShowDataSource(TestDataSources): def setUp(self): super(TestShowDataSource, self).setUp() - self.ds_mock.get.return_value = api_ds.DataSources( + self.ds_mock.find_unique.return_value = api_ds.DataSources( None, DS_INFO) # Command to test @@ -168,22 +177,23 @@ class TestShowDataSource(TestDataSources): columns, data = self.cmd.take_action(parsed_args) # Check that correct arguments was passed - self.ds_mock.get.assert_called_once_with('source') + self.ds_mock.find_unique.assert_called_once_with(name='source') # Check that columns are correct - expected_columns = ('Description', 'Id', 'Name', 'Type', 'Url') + expected_columns = ('Description', 'Id', 'Is protected', 'Is public', + 'Name', 'Type', 'Url') self.assertEqual(expected_columns, columns) # Check that data is correct - expected_data = ['Data Source for tests', 'id', 'source', 'swift', - 'swift://container.sahara/object'] + expected_data = ['Data Source for tests', 'id', True, True, 'source', + 'swift', 'swift://container.sahara/object'] self.assertEqual(expected_data, list(data)) class TestDeleteDataSource(TestDataSources): def setUp(self): super(TestDeleteDataSource, self).setUp() - self.ds_mock.get.return_value = api_ds.DataSources( + self.ds_mock.find_unique.return_value = api_ds.DataSources( None, DS_INFO) # Command to test @@ -191,7 +201,7 @@ class TestDeleteDataSource(TestDataSources): def test_data_sources_delete(self): arglist = ['source'] - verifylist = [('data_source', 'source')] + verifylist = [('data_source', ['source'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -199,3 +209,77 @@ class TestDeleteDataSource(TestDataSources): # Check that correct arguments was passed self.ds_mock.delete.assert_called_once_with('id') + + +class TestUpdateDataSource(TestDataSources): + def setUp(self): + super(TestUpdateDataSource, self).setUp() + self.ds_mock.find_unique.return_value = api_ds.DataSources( + None, DS_INFO) + self.ds_mock.update.return_value = mock.Mock( + data_source=DS_INFO) + + # Command to test + self.cmd = osc_ds.UpdateDataSource(self.app, None) + + def test_data_sources_update_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(osc_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_data_sources_update_required_options(self): + arglist = ['source'] + verifylist = [('data_source', 'source')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that data source was created with correct arguments + self.ds_mock.update.assert_called_once_with('id', {}) + + # Check that columns are correct + expected_columns = ('Description', 'Id', 'Is protected', 'Is public', + 'Name', 'Type', 'Url') + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = ('Data Source for tests', 'id', True, True, 'source', + 'swift', 'swift://container.sahara/object') + self.assertEqual(expected_data, data) + + def test_data_sources_update_all_options(self): + arglist = ['source', '--name', 'source', '--type', 'swift', '--url', + 'swift://container.sahara/object', '--username', 'user', + '--password', 'pass', '--description', + 'Data Source for tests', '--public', '--protected'] + verifylist = [('data_source', 'source'), ('name', 'source'), + ('type', 'swift'), + ('url', 'swift://container.sahara/object'), + ('username', 'user'), ('password', 'pass'), + ('description', 'Data Source for tests'), + ('public', True), ('protected', True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that data source was created with correct arguments + called_args = {'credential_pass': 'pass', 'credential_user': 'user', + 'data_source_type': 'swift', 'name': 'source', + 'description': 'Data Source for tests', + 'url': 'swift://container.sahara/object', + 'is_protected': True, 'is_public': True} + self.ds_mock.update.assert_called_once_with('id', called_args) + + # Check that columns are correct + expected_columns = ('Description', 'Id', 'Is protected', 'Is public', + 'Name', 'Type', 'Url') + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = ('Data Source for tests', 'id', True, True, 'source', + 'swift', 'swift://container.sahara/object') + self.assertEqual(expected_data, data) diff --git a/saharaclient/tests/unit/osc/v1/test_utils.py b/saharaclient/tests/unit/osc/v1/test_utils.py index f7d3cf0e..b9aeef48 100644 --- a/saharaclient/tests/unit/osc/v1/test_utils.py +++ b/saharaclient/tests/unit/osc/v1/test_utils.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from openstackclient.common import exceptions +import mock from saharaclient.osc.v1 import utils from saharaclient.tests.unit import base @@ -36,41 +36,33 @@ class TestUtils(base.BaseTestCase): expected_data = {'Name of res': 'name'} self.assertEqual(expected_data, utils.prepare_data(data, fields)) - def test_get_resource_id(self): - class TestResource(object): - def __init__(self, id): - self.id = id - - class TestManager(object): - - resource_class = TestResource - - def get(self, id): - if id == 'id': - return TestResource('from_id') - else: - raise - - def find(self, name): - if name == 'name': - return [TestResource('from_name')] - if name == 'null': - return [] - if name == 'mult': - return [TestResource('1'), TestResource('2')] + def test_get_resource(self): + manager = mock.Mock() # check case when resource id is passed - self.assertEqual('from_id', utils.get_resource( - TestManager(), 'id').id) + uuid = '82065b4d-2c79-420d-adc3-310de275e922' + utils.get_resource(manager, uuid) + manager.get.assert_called_once_with(uuid) # check case when resource name is passed - self.assertEqual('from_name', utils.get_resource( - TestManager(), 'name').id) + utils.get_resource(manager, 'name') + manager.find_unique.assert_called_once_with(name='name') - # check that error is raised when resource doesn't exists - self.assertRaises(exceptions.CommandError, utils.get_resource, - TestManager(), 'null') + def test_create_dict_from_kwargs(self): + dict1 = utils.create_dict_from_kwargs(first='1', second=2) + self.assertEqual({'first': '1', 'second': 2}, dict1) - # check that error is raised when multiple resources choice - self.assertRaises(exceptions.CommandError, utils.get_resource, - TestManager(), 'mult') + dict2 = utils.create_dict_from_kwargs(first='1', second=None) + self.assertEqual({'first': '1'}, dict2) + + dict3 = utils.create_dict_from_kwargs(first='1', second=False) + self.assertEqual({'first': '1', 'second': False}, dict3) + + def test_prepare_column_headers(self): + columns1 = ['first', 'second_column'] + self.assertEqual( + ['First', 'Second column'], utils.prepare_column_headers(columns1)) + + columns2 = ['First', 'Second column'] + self.assertEqual( + ['First', 'Second column'], utils.prepare_column_headers(columns2)) diff --git a/setup.cfg b/setup.cfg index da71acdc..e49c8b8a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,6 +44,7 @@ openstack.data_processing.v1 = dataprocessing_data_source_list = saharaclient.osc.v1.data_sources:ListDataSources dataprocessing_data_source_show = saharaclient.osc.v1.data_sources:ShowDataSource dataprocessing_data_source_delete = saharaclient.osc.v1.data_sources:DeleteDataSource + dataprocessing_data_source_update = saharaclient.osc.v1.data_sources:UpdateDataSource [build_sphinx] all_files = 1