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:
parent
59b23bfcf7
commit
637100e1b2
|
@ -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)
|
||||
|
|
|
@ -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))
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue