Add OpenStackClient plugin and flavor list
This change adds database support to the python-openstackclient through a plugin and tests. The support can be demonstrated through the implementation of the trove command flavor-list which is now: openstack database flavor list Use the -v or --debug flag to see the calls being made to the correct flavor list function. ubuntu@ubuntu:~$ openstack database flavor list -v START with options: [u'database', u'flavor', u'list', u'-v'] command: database flavor list -> troveclient.osc.v1.flavors.ListFlavors Using auth plugin: password +-----+-----------+-------+-------+------+-----------+ | id | name | RAM | vCPUs | Disk | ephemeral | +-----+-----------+-------+-------+------+-----------+ | 1 | m1.tiny | 512 | 1 | 1 | 0 | | 2 | m1.small | 2048 | 1 | 20 | 0 | | 3 | m1.medium | 4096 | 2 | 40 | 0 | | 4 | m1.large | 8192 | 4 | 80 | 0 | | 42 | m1.nano | 64 | 1 | 0 | 0 | | 451 | m1.heat | 512 | 1 | 0 | 0 | | 5 | m1.xlarge | 16384 | 8 | 160 | 0 | | 84 | m1.micro | 128 | 1 | 0 | 0 | | c1 | cirros256 | 256 | 1 | 0 | 0 | | d1 | ds512M | 512 | 1 | 5 | 0 | | d2 | ds1G | 1024 | 1 | 10 | 0 | | d3 | ds2G | 2048 | 2 | 10 | 0 | | d4 | ds4G | 4096 | 4 | 20 | 0 | +-----+-----------+-------+-------+------+-----------+ END return value: 0 Change-Id: I308a6c6f3f5ce7dbb814ec0fd8ecb1734a2f137f Partially-Implements: trove-support-in-python-openstackclient
This commit is contained in:
parent
e2a2263e19
commit
4eebd56786
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- The command ``trove flavor-list`` is now available to use in
|
||||
the python-openstackclient CLI as ``openstack database flavor
|
||||
list``
|
|
@ -12,3 +12,4 @@ keystoneauth1>=2.16.0 # Apache-2.0
|
|||
six>=1.9.0 # MIT
|
||||
python-swiftclient>=3.2.0 # Apache-2.0
|
||||
python-mistralclient>=2.0.0 # Apache-2.0
|
||||
osc-lib>=1.2.0 # Apache-2.0
|
||||
|
|
|
@ -26,6 +26,12 @@ packages =
|
|||
console_scripts =
|
||||
trove = troveclient.shell:main
|
||||
|
||||
openstack.cli.extension =
|
||||
database = troveclient.osc.plugin
|
||||
|
||||
openstack.database.v1 =
|
||||
database_flavor_list = troveclient.osc.v1.database_flavors:ListDatabaseFlavors
|
||||
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
source-dir = doc/source
|
||||
|
|
|
@ -6,6 +6,7 @@ coverage>=4.0 # Apache-2.0
|
|||
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
oslosphinx>=4.7.0 # Apache-2.0
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
python-openstackclient>=3.3.0 # Apache-2.0
|
||||
requests-mock>=1.1 # Apache-2.0
|
||||
sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# 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 logging
|
||||
|
||||
from osc_lib import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_DATABASE_API_VERSION = '1'
|
||||
API_VERSION_OPTION = 'os_database_api_version'
|
||||
API_NAME = 'database'
|
||||
API_VERSIONS = {
|
||||
'1': 'troveclient.v1.client.Client',
|
||||
}
|
||||
|
||||
|
||||
def make_client(instance):
|
||||
"""Returns a database service client"""
|
||||
trove_client = utils.get_client_class(
|
||||
API_NAME,
|
||||
instance._api_version[API_NAME],
|
||||
API_VERSIONS)
|
||||
LOG.debug('Instantiating database client: %s', trove_client)
|
||||
client = trove_client(
|
||||
auth=instance.auth,
|
||||
session=instance.session
|
||||
)
|
||||
|
||||
return client
|
||||
|
||||
|
||||
def build_option_parser(parser):
|
||||
"""Hook to add global options"""
|
||||
parser.add_argument(
|
||||
'--os-database-api-version',
|
||||
metavar='<database-api-version>',
|
||||
default=utils.env(
|
||||
'OS_DATABASE_API_VERSION',
|
||||
default=DEFAULT_DATABASE_API_VERSION),
|
||||
help='Database API version, default=' +
|
||||
DEFAULT_DATABASE_API_VERSION +
|
||||
' (Env: OS_DATABASE_API_VERSION)')
|
||||
return parser
|
|
@ -0,0 +1,63 @@
|
|||
# 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.
|
||||
|
||||
"""Database v1 Flavors action implementations"""
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from troveclient import exceptions
|
||||
from troveclient.i18n import _
|
||||
|
||||
|
||||
class ListDatabaseFlavors(command.Lister):
|
||||
|
||||
_description = _("List database flavors")
|
||||
columns = ['ID', 'Name', 'RAM', 'vCPUs', 'Disk', 'Ephemeral']
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListDatabaseFlavors, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--datastore-type',
|
||||
dest='datastore_type',
|
||||
metavar='<datastore-type>',
|
||||
help=_('Type of the datastore. For eg: mysql.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--datastore-version-id',
|
||||
dest='datastore_version_id',
|
||||
metavar='<datastore-version-id>',
|
||||
help=_('ID of the datastore version.')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
db_flavors = self.app.client_manager.database.flavors
|
||||
if parsed_args.datastore_type and parsed_args.datastore_version_id:
|
||||
flavors = db_flavors.list_datastore_version_associated_flavors(
|
||||
datastore_type=parsed_args.datastore_type,
|
||||
datastore_version_id=parsed_args.datastore_version_id)
|
||||
elif (not parsed_args.datastore_type and not
|
||||
parsed_args.datastore_version_id):
|
||||
flavors = db_flavors.list()
|
||||
else:
|
||||
raise exceptions.MissingArgs(['datastore-type',
|
||||
'datastore-version-id'])
|
||||
|
||||
# Fallback to str_id where necessary.
|
||||
_flavors = []
|
||||
for f in flavors:
|
||||
if not f.id and hasattr(f, 'str_id'):
|
||||
f.id = f.str_id
|
||||
_flavors.append(utils.get_item_properties(f, self.columns))
|
||||
|
||||
return self.columns, _flavors
|
|
@ -0,0 +1,26 @@
|
|||
# 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.
|
||||
#
|
||||
|
||||
|
||||
class FakeStdout(object):
|
||||
def __init__(self):
|
||||
self.content = []
|
||||
|
||||
def write(self, text):
|
||||
self.content.append(text)
|
||||
|
||||
def make_string(self):
|
||||
result = ''
|
||||
for line in self.content:
|
||||
result = result + line
|
||||
return result
|
|
@ -0,0 +1,62 @@
|
|||
# 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 os
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
import sys
|
||||
import testtools
|
||||
|
||||
from troveclient.tests.osc import fakes
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
def setUp(self):
|
||||
testtools.TestCase.setUp(self)
|
||||
|
||||
if (os.environ.get("OS_STDOUT_CAPTURE") == "True" or
|
||||
os.environ.get("OS_STDOUT_CAPTURE") == "1"):
|
||||
stdout = self.useFixture(fixtures.StringStream("stdout")).stream
|
||||
self.useFixture(fixtures.MonkeyPatch("sys.stdout", stdout))
|
||||
|
||||
if (os.environ.get("OS_STDERR_CAPTURE") == "True" or
|
||||
os.environ.get("OS_STDERR_CAPTURE") == "1"):
|
||||
stderr = self.useFixture(fixtures.StringStream("stderr")).stream
|
||||
self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr))
|
||||
|
||||
|
||||
class TestCommand(TestCase):
|
||||
"""Test cliff command classes"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCommand, self).setUp()
|
||||
# Build up a fake app
|
||||
self.fake_stdout = fakes.FakeStdout()
|
||||
self.app = mock.MagicMock()
|
||||
self.app.stdout = self.fake_stdout
|
||||
self.app.stdin = sys.stdin
|
||||
self.app.stderr = sys.stderr
|
||||
|
||||
def check_parser(self, cmd, args, verify_args):
|
||||
cmd_parser = cmd.get_parser('check_parser')
|
||||
try:
|
||||
parsed_args = cmd_parser.parse_args(args)
|
||||
except SystemExit:
|
||||
raise Exception("Argument parse failed")
|
||||
for av in verify_args:
|
||||
attr, value = av
|
||||
if attr:
|
||||
self.assertIn(attr, parsed_args)
|
||||
self.assertEqual(getattr(parsed_args, attr), value)
|
||||
return parsed_args
|
|
@ -0,0 +1,31 @@
|
|||
# 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 troveclient.tests import fakes
|
||||
from troveclient.tests.osc import utils
|
||||
from troveclient.v1 import flavors
|
||||
|
||||
|
||||
class TestDatabasev1(utils.TestCommand):
|
||||
def setUp(self):
|
||||
super(TestDatabasev1, self).setUp()
|
||||
self.app.client_manager.database = mock.MagicMock()
|
||||
|
||||
|
||||
class FakeFlavors(object):
|
||||
fake_flavors = fakes.FakeHTTPClient().get_flavors()[2]['flavors']
|
||||
|
||||
def get_flavors_1(self):
|
||||
return flavors.Flavor(None, self.fake_flavors[0])
|
|
@ -0,0 +1,41 @@
|
|||
# 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 troveclient.osc.v1 import database_flavors
|
||||
from troveclient.tests.osc.v1 import fakes
|
||||
|
||||
|
||||
class TestFlavors(fakes.TestDatabasev1):
|
||||
fake_flavors = fakes.FakeFlavors()
|
||||
|
||||
def setUp(self):
|
||||
super(TestFlavors, self).setUp()
|
||||
self.mock_client = self.app.client_manager.database
|
||||
self.flavor_client = self.app.client_manager.database.flavors
|
||||
|
||||
|
||||
class TestFlavorList(TestFlavors):
|
||||
columns = database_flavors.ListDatabaseFlavors.columns
|
||||
values = (1, 'm1.tiny', 512, '', '', '')
|
||||
|
||||
def setUp(self):
|
||||
super(TestFlavorList, self).setUp()
|
||||
self.cmd = database_flavors.ListDatabaseFlavors(self.app, None)
|
||||
self.data = [self.fake_flavors.get_flavors_1()]
|
||||
self.flavor_client.list.return_value = self.data
|
||||
|
||||
def test_flavor_list_defaults(self):
|
||||
parsed_args = self.check_parser(self.cmd, [], [])
|
||||
columns, values = self.cmd.take_action(parsed_args)
|
||||
self.flavor_client.list.assert_called_once_with()
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual([self.values], values)
|
Loading…
Reference in New Issue