Adding update and minor fixes to Data Sources CLI

Changes:
* adding Data Sources update command to OpenstackClient CLI:
  $ dataprocessing data sources update
* adding ability to delete multiple data sources with one call
* adding prepare_column_headers method to osc utils
* adding public and protected support to datasources
* changing utils.get_resource method

Partially implements: blueprint cli-as-openstackclient-plugin

Change-Id: If3b1811eea5d1d9770c286dc9487d278f3616c48
This commit is contained in:
Andrey Pavlov
2015-09-09 13:41:05 +03:00
parent 1fa10e091b
commit 6a221fa79d
6 changed files with 293 additions and 90 deletions

View File

@@ -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="<type>",
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="<description>",
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="<type>",
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="<data-source>",
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="<data-source>",
help="Name or id of the data source",
)
parser.add_argument(
'--name',
metavar="<name>",
help="New name of the data source",
)
parser.add_argument(
'--type',
metavar="<type>",
choices=DATA_SOURCE_CHOICES,
help="Type of the data source "
"(%s)" % ', '.join(DATA_SOURCE_CHOICES)
)
parser.add_argument(
'--url',
metavar="<url>",
help="Url for the data source"
)
parser.add_argument(
'--username',
metavar="<username>",
help="Username for accessing the data source url"
)
parser.add_argument(
'--password',
metavar="<password>",
help="Password for accessing the data source url"
)
parser.add_argument(
'--description',
metavar="<description>",
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)

View File

@@ -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)

View File

@@ -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]

View File

@@ -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)

View File

@@ -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))

View File

@@ -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