Modify datasource api and add datasource-driver api bindings

This patch adds bindings to read the datasource-drivers config/schema
from congress server. It also changes several methods so the look ups
are now done by id instead of name. We needed to change this in the server
api so we can have a multi tenant api.

Change-Id: Ib01efb0334b43a7dbf7bda47cf1f1304fa01b91f
This commit is contained in:
Aaron Rosen 2015-02-05 15:47:44 -08:00
parent 59b23bfcf7
commit 637100e1b2
6 changed files with 288 additions and 90 deletions

View File

@ -19,12 +19,38 @@ import logging
from cliff import command
from cliff import lister
from cliff import show
from keystoneclient.openstack.common.apiclient import exceptions
from openstackclient.common import parseractions
import six
from congressclient.common import utils
def get_resource_id_from_name(name, results):
# FIXME(arosen): move to common lib and add tests...
name_match = None
id_match = None
double_name_match = False
for result in results['results']:
if result['id'] == name:
id_match = result['id']
if result['name'] == name:
if name_match:
double_name_match = True
name_match = result['id']
if not double_name_match and name_match:
return name_match
if double_name_match and not id_match:
# NOTE(arosen): this should only occur is using congress
# as admin and multiple tenants use the same datsource name.
raise exceptions.Conflict(
"Multiple resources have this name %s. Delete by id." % name)
if id_match:
return id_match
raise exceptions.NotFound("Resource %s not found" % name)
class ListDatasources(lister.Lister):
"""List Datasources."""
@ -37,7 +63,7 @@ class ListDatasources(lister.Lister):
def take_action(self, parsed_args):
client = self.app.client_manager.congressclient
data = client.list_datasources()['results']
columns = ['id', 'owner_id', 'enabled', 'type', 'config']
columns = ['id', 'name', 'enabled', 'type', 'config']
formatters = {'Datasources': utils.format_list}
return (columns,
(utils.get_dict_properties(s, columns,
@ -87,8 +113,12 @@ class ListDatasourceStatus(lister.Lister):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
client = self.app.client_manager.congressclient
data = client.list_datasource_status(
parsed_args.datasource_name)['results']
results = client.list_datasources()
datasource_id = get_resource_id_from_name(parsed_args.datasource_name,
results)
data = client.list_datasource_status(datasource_id)['results']
newdata = []
for d in data:
temp = [{'key': key, 'value': value}
@ -118,8 +148,10 @@ class ShowDatasourceSchema(lister.Lister):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
client = self.app.client_manager.congressclient
data = client.show_datasource_schema(
parsed_args.datasource_name)
results = client.list_datasources()
datasource_id = get_resource_id_from_name(parsed_args.datasource_name,
results)
data = client.show_datasource_schema(datasource_id)
formatters = {'columns': utils.format_long_dict_list}
newdata = [{'table': x['table_id'],
'columns': x['columns']}
@ -225,7 +257,7 @@ class CreateDatasource(show.ShowOne):
def get_parser(self, prog_name):
parser = super(CreateDatasource, self).get_parser(prog_name)
parser.add_argument(
'datasource_driver',
'driver',
metavar="<datasource-driver>",
help="Selected datasource driver")
parser.add_argument(
@ -249,7 +281,7 @@ class CreateDatasource(show.ShowOne):
self.log.debug('take_action(%s)' % parsed_args)
client = self.app.client_manager.congressclient
body = {'name': parsed_args.name,
'datasource_driver': parsed_args.datasource_driver,
'driver': parsed_args.driver,
'config': parsed_args.config}
if parsed_args.description:
body['description'] = parsed_args.description
@ -257,28 +289,6 @@ class CreateDatasource(show.ShowOne):
return zip(*sorted(six.iteritems(results)))
class ShowDatasourceConfig(show.ShowOne):
"""Show config for datasource."""
log = logging.getLogger(__name__ + '.ShowDatasourceConfig')
def get_parser(self, prog_name):
parser = super(ShowDatasourceConfig, self).get_parser(prog_name)
parser.add_argument(
'datasource_name',
metavar="<datasource-name>",
help="Name of the datasource")
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
client = self.app.client_manager.congressclient
data = client.show_datasource_config(
parsed_args.datasource_name)
return zip(*sorted(six.iteritems(data)))
class DeleteDatasource(command.Command):
"""Delete a datasource."""
@ -295,4 +305,7 @@ class DeleteDatasource(command.Command):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
client = self.app.client_manager.congressclient
client.delete_datasource(parsed_args.datasource)
results = client.list_datasources()
datasource_id = get_resource_id_from_name(parsed_args.datasource,
results)
client.delete_datasource(datasource_id)

View File

@ -0,0 +1,101 @@
# Copyright 2012-2013 OpenStack, LLC.
#
# 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.
"""Driver action implemenations"""
import logging
from cliff import lister
from cliff import show
import six
from congressclient.common import utils
class ListDrivers(lister.Lister):
"""List drivers."""
log = logging.getLogger(__name__ + '.ListDrivers')
def get_parser(self, prog_name):
parser = super(ListDrivers, self).get_parser(prog_name)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.congressclient
data = client.list_drivers()['results']
columns = ['id', 'description']
formatters = {'Drivers': utils.format_list}
return (columns,
(utils.get_dict_properties(s, columns,
formatters=formatters)
for s in data))
class ShowDriverConfig(show.ShowOne):
"""List driver tables."""
log = logging.getLogger(__name__ + '.ShowDriverConfig')
def get_parser(self, prog_name):
parser = super(ShowDriverConfig, self).get_parser(prog_name)
parser.add_argument(
'driver',
metavar="<datasource-driver>",
help="Name of the datasource driver")
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
client = self.app.client_manager.congressclient
data = client.show_driver(
parsed_args.driver)
# remove table schema info from displaying
del data['tables']
return zip(*sorted(six.iteritems(data)))
columns = ['id']
formatters = {'DriverTables': utils.format_list}
return (columns,
(utils.get_dict_properties(s, columns,
formatters=formatters)
for s in data))
class ShowDriverSchema(lister.Lister):
"""List datasource tables."""
log = logging.getLogger(__name__ + '.ShowDriverSchema')
def get_parser(self, prog_name):
parser = super(ShowDriverSchema, self).get_parser(prog_name)
parser.add_argument(
'driver',
metavar="<datasource-driver>",
help="Name of the datasource driver")
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
client = self.app.client_manager.congressclient
data = client.show_driver(
parsed_args.driver)
formatters = {'columns': utils.format_long_dict_list}
newdata = [{'table': x['table_id'],
'columns': x['columns']}
for x in data['tables']]
columns = ['table', 'columns']
return (columns,
(utils.get_dict_properties(s, columns,
formatters=formatters)
for s in newdata))

View File

@ -26,7 +26,7 @@ class TestListDatasources(common.TestCongressBase):
]
response = {
"results": [{"id": datasource_name,
"owner_id": "system",
"name": "my_name",
"enabled": "True",
"type": "None",
"config": "None"}]
@ -39,7 +39,7 @@ class TestListDatasources(common.TestCongressBase):
result = cmd.take_action(parsed_args)
lister.assert_called_with()
self.assertEqual(['id', 'owner_id', 'enabled', 'type', 'config'],
self.assertEqual(['id', 'name', 'enabled', 'type', 'config'],
result[0])
@ -82,12 +82,15 @@ class TestListDatasourceStatus(common.TestCongressBase):
}
lister = mock.Mock(return_value=response)
self.app.client_manager.congressclient.list_datasource_status = lister
self.app.client_manager.congressclient.list_datasources = mock.Mock()
cmd = datasource.ListDatasourceStatus(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = cmd.take_action(parsed_args)
with mock.patch.object(datasource, "get_resource_id_from_name",
return_value="id"):
result = cmd.take_action(parsed_args)
lister.assert_called_with(datasource_name)
lister.assert_called_with("id")
self.assertEqual(['key', 'value'], result[0])
@ -113,12 +116,15 @@ class TestShowDatasourceSchema(common.TestCongressBase):
}
lister = mock.Mock(return_value=response)
self.app.client_manager.congressclient.show_datasource_schema = lister
self.app.client_manager.congressclient.list_datasources = mock.Mock()
cmd = datasource.ShowDatasourceSchema(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = cmd.take_action(parsed_args)
with mock.patch.object(datasource, "get_resource_id_from_name",
return_value="id"):
result = cmd.take_action(parsed_args)
lister.assert_called_with(datasource_name)
lister.assert_called_with("id")
self.assertEqual(['table', 'columns'], result[0])
@ -190,7 +196,7 @@ class TestListDatasourceRows(common.TestCongressBase):
class TestCreateDatasource(common.TestCongressBase):
def test_create_datasource(self):
datasource_driver = 'neutronv2'
driver = 'neutronv2'
name = 'arosen-neutronv2'
response = {"description": '',
"config": {"username": "admin",
@ -199,18 +205,18 @@ class TestCreateDatasource(common.TestCongressBase):
"auth_url": "http://127.0.0.1:5000/v2.0"},
"enabled": True,
"owner": "user",
"datasource_driver": "neutronv2",
"driver": "neutronv2",
"type": None,
"id": "b72f81a0-32b5-4bf4-a1f6-d69c09c42cec",
"name": "arosen-neutronv2"}
arglist = [datasource_driver, name,
arglist = [driver, name,
"--config", "username=admin",
"--config", "password=password",
"--config", "auth_url=http://1.1.1.1/foo",
"--config", "tenant_name=admin"]
verifylist = [
('datasource_driver', datasource_driver),
('driver', driver),
('name', name),
('config', {'username': 'admin', 'password': 'password',
'auth_url': 'http://1.1.1.1/foo',
@ -222,61 +228,31 @@ class TestCreateDatasource(common.TestCongressBase):
cmd = datasource.CreateDatasource(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = list(cmd.take_action(parsed_args))
filtered = [('config', 'datasource_driver',
'description', 'enabled', 'id', 'name',
filtered = [('config', 'description',
'driver', 'enabled', 'id', 'name',
'owner', 'type'),
(response['config'], response['datasource_driver'],
response['description'], response['enabled'],
(response['config'], response['description'],
response['driver'], response['enabled'],
response['id'], response['name'],
response['owner'], response['type'])]
self.assertEqual(filtered, result)
class TestShowDatasourceConfig(common.TestCongressBase):
def test_show_datasource_config(self):
datasource_driver = 'neutronv2'
response = {"description": '',
"config": {"username": "admin",
"tenant_name": "admin",
"password": "password",
"auth_url": "http://127.0.0.1:5000/v2.0"},
"enabled": True,
"owner": "user",
"datasource_driver": "neutronv2",
"type": None,
"name": "arosen-neutronv2"}
arglist = [datasource_driver]
verifylist = [('datasource_name', datasource_driver), ]
mocker = mock.Mock(return_value=response)
self.app.client_manager.congressclient.show_datasource_config = mocker
cmd = datasource.ShowDatasourceConfig(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = list(cmd.take_action(parsed_args))
filtered = [('config', 'datasource_driver',
'description', 'enabled', 'name',
'owner', 'type'),
(response['config'], response['datasource_driver'],
response['description'], response['enabled'],
response['name'],
response['owner'], response['type'])]
self.assertEqual(filtered, result)
class TestDeleteDatasourceDriver(common.TestCongressBase):
def test_delete_datasource_driver(self):
datasource_driver = 'neutronv2'
def test_delete_datasource(self):
driver = 'neutronv2'
arglist = [datasource_driver]
verifylist = [('datasource', datasource_driver), ]
arglist = [driver]
verifylist = [('datasource', driver), ]
mocker = mock.Mock(return_value=None)
self.app.client_manager.congressclient.delete_datasource = mocker
self.app.client_manager.congressclient.list_datasources = mock.Mock()
cmd = datasource.DeleteDatasource(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = cmd.take_action(parsed_args)
mocker.assert_called_with(datasource_driver)
with mock.patch.object(datasource, "get_resource_id_from_name",
return_value="id"):
result = cmd.take_action(parsed_args)
mocker.assert_called_with("id")
self.assertEqual(None, result)

View File

@ -0,0 +1,101 @@
# 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.
#
import mock
from congressclient.osc.v1 import driver
from congressclient.tests import common
class TestListDrivers(common.TestCongressBase):
def test_list_drivers(self):
arglist = [
]
verifylist = [
]
response = {
"results": [{"id": "neutronv2",
"description": "this does blah.."}]
}
lister = mock.Mock(return_value=response)
self.app.client_manager.congressclient.list_drivers = lister
cmd = driver.ListDrivers(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = cmd.take_action(parsed_args)
lister.assert_called_with()
self.assertEqual(['id', 'description'], result[0])
class TestShowDriverSchema(common.TestCongressBase):
def test_show_driver_shema(self):
arglist = [
"neutronv2"
]
verifylist = [
('driver', "neutronv2")
]
response = {
"tables":
[{'table_id': 'ports',
'columns': [{"name": "name", "description": "None"},
{"name": "status", "description": "None"},
{"name": "id", "description": "None"}]},
{'table_id': 'routers',
'columns': [{"name": "name", "description": "None"},
{"name": "floating_ip", "description": "None"},
{"name": "id", "description": "None"}]}]
}
lister = mock.Mock(return_value=response)
self.app.client_manager.congressclient.show_driver = lister
cmd = driver.ShowDriverSchema(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = cmd.take_action(parsed_args)
lister.assert_called_with("neutronv2")
self.assertEqual(['table', 'columns'], result[0])
class TestShowDriverConfig(common.TestCongressBase):
def test_show_driver_config(self):
arglist = [
"neutronv2"
]
verifylist = [
('driver', "neutronv2")
]
response = {
"tables": [],
'id': 'aabbcc',
'description': 'foobar',
'config': {'password': 'password'},
}
mocker = mock.Mock(return_value=response)
self.app.client_manager.congressclient.show_driver = mocker
cmd = driver.ShowDriverConfig(self.app, self.namespace)
parsed_args = self.check_parser(cmd, arglist, verifylist)
result = list(cmd.take_action(parsed_args))
mocker.assert_called_with("neutronv2")
filtered = [('config', 'description', 'id'),
(response['config'], response['description'],
response['id'])]
self.assertEqual(filtered, result)

View File

@ -51,9 +51,10 @@ class Client(object):
datasource_table_path = '/v1/data-sources/%s/tables/%s'
datasource_status = '/v1/data-sources/%s/status'
datasource_schema = '/v1/data-sources/%s/schema'
datasource_config = '/v1/data-sources/%s/config'
datasource_table_schema = '/v1/data-sources/%s/tables/%s/spec'
datasource_rows = '/v1/data-sources/%s/tables/%s/rows'
driver = '/v1/system/drivers'
driver_path = '/v1/system/drivers/%s'
def __init__(self, **kwargs):
super(Client, self).__init__()
@ -161,12 +162,16 @@ class Client(object):
self.datasources, body=body)
return body
def show_datasource_config(self, datasource_name):
resp, body = self.httpclient.get(self.datasource_config %
datasource_name)
return body
def delete_datasource(self, datasource):
resp, body = self.httpclient.delete(
self.datasource_path % datasource)
return body
def list_drivers(self):
resp, body = self.httpclient.get(self.driver)
return body
def show_driver(self, driver):
resp, body = self.httpclient.get(self.driver_path %
(driver))
return body

View File

@ -41,7 +41,6 @@ openstack.congressclient.v1 =
congress_datasource_list = congressclient.osc.v1.datasource:ListDatasources
congress_datasource_create = congressclient.osc.v1.datasource:CreateDatasource
congress_datasource_delete = congressclient.osc.v1.datasource:DeleteDatasource
congress_datasource_config_show = congressclient.osc.v1.datasource:ShowDatasourceConfig
congress_datasource_table_list = congressclient.osc.v1.datasource:ListDatasourceTables
congress_datasource_row_list = congressclient.osc.v1.datasource:ListDatasourceRows
congress_datasource_status_list = congressclient.osc.v1.datasource:ListDatasourceStatus
@ -49,6 +48,9 @@ openstack.congressclient.v1 =
congress_datasource_table_schema_show = congressclient.osc.v1.datasource:ShowDatasourceTableSchema
congress_policy_table_show = congressclient.osc.v1.policy:ShowPolicyTable
congress_datasource_table_show = congressclient.osc.v1.datasource:ShowDatasourceTable
congress_driver_config_show = congressclient.osc.v1.driver:ShowDriverConfig
congress_driver_schema_show = congressclient.osc.v1.driver:ShowDriverSchema
congress_driver_list = congressclient.osc.v1.driver:ListDrivers
[build_sphinx]
source-dir = doc/source