Drop the legacy client

Change-Id: I1d367e87f25daa1adad28409903c1de9d5a1d801
This commit is contained in:
Radosław Piliszek 2021-03-11 18:09:22 +00:00
parent 393960b3a6
commit c36e647d95
16 changed files with 8 additions and 1590 deletions

View File

@ -1,171 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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 argparse
from masakariclient.common.i18n import _
from masakariclient.common import utils
def add_global_args(parser, version):
# GLOBAL ARGUMENTS
parser.add_argument(
'-h', '--help', action='store_true',
help=argparse.SUPPRESS)
parser.add_argument(
'--masakari-api-version', action='version', version=version,
default=utils.env('MASAKARI_API_VERSION', default='1'),
help=_('Version number for Masakari API to use, Default to "1".'))
parser.add_argument(
'-d', '--debug',
action='store_true',
default=False,
help=_('Print debugging output.'))
def add_global_identity_args(parser):
parser.add_argument(
'--os-auth-plugin', dest='auth_plugin', metavar='AUTH_PLUGIN',
default=utils.env('OS_AUTH_PLUGIN', default=None),
help=_('Authentication plugin, default to env[OS_AUTH_PLUGIN]'))
parser.add_argument(
'--os-auth-url', dest='auth_url', metavar='AUTH_URL',
default=utils.env('OS_AUTH_URL'),
help=_('Defaults to env[OS_AUTH_URL]'))
parser.add_argument(
'--os-project-id', dest='project_id', metavar='PROJECT_ID',
default=utils.env('OS_PROJECT_ID'),
help=_('Defaults to env[OS_PROJECT_ID].'))
parser.add_argument(
'--os-project-name', dest='project_name', metavar='PROJECT_NAME',
default=utils.env('OS_PROJECT_NAME'),
help=_('Defaults to env[OS_PROJECT_NAME].'))
parser.add_argument(
'--os-tenant-id', dest='tenant_id', metavar='TENANT_ID',
default=utils.env('OS_TENANT_ID'),
help=_('Defaults to env[OS_TENANT_ID].'))
parser.add_argument(
'--os-tenant-name', dest='tenant_name', metavar='TENANT_NAME',
default=utils.env('OS_TENANT_NAME'),
help=_('Defaults to env[OS_TENANT_NAME].'))
parser.add_argument(
'--os-domain-id', dest='domain_id', metavar='DOMAIN_ID',
default=utils.env('OS_DOMAIN_ID'),
help=_('Domain ID for scope of authorization, defaults to '
'env[OS_DOMAIN_ID].'))
parser.add_argument(
'--os-domain-name', dest='domain_name', metavar='DOMAIN_NAME',
default=utils.env('OS_DOMAIN_NAME'),
help=_('Domain name for scope of authorization, defaults to '
'env[OS_DOMAIN_NAME].'))
parser.add_argument(
'--os-project-domain-id', dest='project_domain_id',
metavar='PROJECT_DOMAIN_ID',
default=utils.env('OS_PROJECT_DOMAIN_ID'),
help=_('Project domain ID for scope of authorization, defaults to '
'env[OS_PROJECT_DOMAIN_ID].'))
parser.add_argument(
'--os-project-domain-name', dest='project_domain_name',
metavar='PROJECT_DOMAIN_NAME',
default=utils.env('OS_PROJECT_DOMAIN_NAME'),
help=_('Project domain name for scope of authorization, defaults to '
'env[OS_PROJECT_DOMAIN_NAME].'))
parser.add_argument(
'--os-user-domain-id', dest='user_domain_id',
metavar='USER_DOMAIN_ID',
default=utils.env('OS_USER_DOMAIN_ID'),
help=_('User domain ID for scope of authorization, defaults to '
'env[OS_USER_DOMAIN_ID].'))
parser.add_argument(
'--os-user-domain-name', dest='user_domain_name',
metavar='USER_DOMAIN_NAME',
default=utils.env('OS_USER_DOMAIN_NAME'),
help=_('User domain name for scope of authorization, defaults to '
'env[OS_USER_DOMAIN_NAME].'))
parser.add_argument(
'--os-username', dest='username', metavar='USERNAME',
default=utils.env('OS_USERNAME'),
help=_('Defaults to env[OS_USERNAME].'))
parser.add_argument(
'--os-user-id', dest='user_id', metavar='USER_ID',
default=utils.env('OS_USER_ID'),
help=_('Defaults to env[OS_USER_ID].'))
parser.add_argument(
'--os-password', dest='password', metavar='PASSWORD',
default=utils.env('OS_PASSWORD'),
help=_('Defaults to env[OS_PASSWORD]'))
parser.add_argument(
'--os-trust-id', dest='trust_id', metavar='TRUST_ID',
default=utils.env('OS_TRUST_ID'),
help=_('Defaults to env[OS_TRUST_ID]'))
verify_group = parser.add_mutually_exclusive_group()
verify_group.add_argument(
'--os-cacert', dest='verify', metavar='CA_BUNDLE_FILE',
default=utils.env('OS_CACERT', default=True),
help=_('Path of CA TLS certificate(s) used to verify the remote '
'server\'s certificate. Without this option masakari looks '
'for the default system CA certificates.'))
verify_group.add_argument(
'--verify',
action='store_true',
help=_('Verify server certificate (default)'))
verify_group.add_argument(
'--insecure', dest='verify', action='store_false',
help=_('Explicitly allow masakariclient to perform "insecure SSL" '
'(HTTPS) requests. The server\'s certificate will not be '
'verified against any certificate authorities. This '
'option should be used with caution.'))
parser.add_argument(
'--os-token', dest='token', metavar='TOKEN',
default=utils.env('OS_TOKEN', default=None),
help=_('A string token to bootstrap the Keystone database, defaults '
'to env[OS_TOKEN]'))
parser.add_argument(
'--os-access-info', dest='access_info', metavar='ACCESS_INFO',
default=utils.env('OS_ACCESS_INFO'),
help=_('Access info, defaults to env[OS_ACCESS_INFO]'))
parser.add_argument(
'--os-interface', dest='interface', metavar='INTERFACE',
default=utils.env('OS_INTERFACE', default='internal'),
help=_('API Interface to use [public, internal, admin]'
', defaults to env[OS_INTERFACE]'))
parser.add_argument(
'--os-region-name', dest='region_name', metavar='REGION_NAME',
default=utils.env('OS_REGION_NAME'),
help=_('Region of the cloud to use, defaults to env[OS_REGION_NAME]'))

View File

@ -1,28 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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 masakariclient.common import utils
def Client(api_ver, *args, **kwargs):
"""Import versioned client module.
:param api_ver: API version required.
:param args: API args.
:param kwargs: the auth parameters for client.
"""
module = utils.import_versioned_module(api_ver, 'client')
cls = getattr(module, 'Client')
return cls(*args, **kwargs)

View File

@ -12,20 +12,13 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import os
import prettytable
import textwrap
from oslo_serialization import jsonutils
from oslo_utils import encodeutils
from oslo_utils import importutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
from masakariclient.common import exception as exc from masakariclient.common import exception as exc
from masakariclient.common.i18n import _ from masakariclient.common.i18n import _
def format_parameters(params, parse_semicolon=True): def _format_parameters(params, parse_semicolon=True):
"""Reformat parameters into dict of format expected by the API.""" """Reformat parameters into dict of format expected by the API."""
if not params: if not params:
return {} return {}
@ -62,113 +55,6 @@ def remove_unspecified_items(attrs):
return attrs return attrs
def import_versioned_module(version, submodule=None):
module = 'masakariclient.v%s' % version
if submodule:
module = '.'.join((module, submodule))
return importutils.import_module(module)
def arg(*args, **kwargs):
"""Decorator for CLI args."""
def _decorator(func):
if not hasattr(func, 'arguments'):
func.arguments = []
if (args, kwargs) not in func.arguments:
func.arguments.insert(0, (args, kwargs))
return func
return _decorator
def env(*args, **kwargs):
"""Returns the first environment variable set.
If all are empty, defaults to '' or keyword arg `default`.
"""
for arg in args:
value = os.environ.get(arg)
if value:
return value
return kwargs.get('default', '')
def print_list(objs, fields, formatters={}, sortby_index=None):
"""Print list data by PrettyTable."""
if sortby_index is None:
sortby = None
else:
sortby = fields[sortby_index]
mixed_case_fields = ['serverId']
pt = prettytable.PrettyTable([f for f in fields], caching=False)
pt.align = 'l'
for o in objs:
row = []
for field in fields:
if field in formatters:
row.append(formatters[field](o))
else:
if field in mixed_case_fields:
field_name = field.replace(' ', '_')
else:
field_name = field.lower().replace(' ', '_')
data = getattr(o, field_name, '')
if data is None:
data = '-'
# '\r' would break the table, so remove it.
data = str(data).replace("\r", "")
row.append(data)
pt.add_row(row)
if sortby is not None:
result = encodeutils.safe_encode(pt.get_string(sortby=sortby))
else:
result = encodeutils.safe_encode(pt.get_string())
result = result.decode()
print(result)
def print_dict(d, dict_property="Property", dict_value="Value", wrap=0):
"""Print dictionary data (eg. show) by PrettyTable."""
pt = prettytable.PrettyTable([dict_property, dict_value], caching=False)
pt.align = 'l'
for k, v in sorted(d.items()):
# convert dict to str to check length
if isinstance(v, (dict, list)):
v = jsonutils.dumps(v)
if wrap > 0:
v = textwrap.fill(str(v), wrap)
# if value has a newline, add in multiple rows
# e.g. fault with stacktrace
if v and isinstance(v, str) and (r'\n' in v or '\r' in v):
# '\r' would break the table, so remove it.
if '\r' in v:
v = v.replace('\r', '')
lines = v.strip().split(r'\n')
col1 = k
for line in lines:
pt.add_row([col1, line])
col1 = ''
else:
if v is None:
v = '-'
pt.add_row([k, v])
result = encodeutils.safe_encode(pt.get_string())
result = result.decode()
print(result)
def format_sort_filter_params(parsed_args): def format_sort_filter_params(parsed_args):
queries = {} queries = {}
limit = parsed_args.limit limit = parsed_args.limit
@ -196,7 +82,7 @@ def format_sort_filter_params(parsed_args):
queries['sort_dir'] = sort_dirs queries['sort_dir'] = sort_dirs
if parsed_args.filters: if parsed_args.filters:
queries.update(format_parameters(parsed_args.filters)) queries.update(_format_parameters(parsed_args.filters))
return queries return queries

View File

@ -1,206 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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 argparse
import logging
import sys
from oslo_utils import encodeutils
import masakariclient
from masakariclient import cliargs
from masakariclient import client as masakari_client
from masakariclient.common import exception as exc
from masakariclient.common.i18n import _
from masakariclient.common import utils
USER_AGENT = 'python-masakariclient'
LOG = logging.getLogger(__name__)
class MasakariShell(object):
def __init__(self):
pass
def do_bash_completion(self, args):
"""All of the commands and options to stdout."""
commands = set()
options = set()
for sc_str, sc in self.subcommands.items():
if sc_str == 'bash_completion' or sc_str == 'bash-completion':
continue
commands.add(sc_str)
for option in list(sc._optionals._option_string_actions):
options.add(option)
print(' '.join(commands | options))
def _add_bash_completion_subparser(self, subparsers):
subparser = subparsers.add_parser('bash_completion',
add_help=False,
formatter_class=HelpFormatter)
subparser.set_defaults(func=self.do_bash_completion)
self.subcommands['bash_completion'] = subparser
def _get_subcommand_parser(self, base_parser, version):
parser = base_parser
self.subcommands = {}
subparsers = parser.add_subparsers(metavar='<subcommand>')
submodule = utils.import_versioned_module(version, 'shell')
self._find_actions(subparsers, submodule)
self._add_bash_completion_subparser(subparsers)
return parser
def _find_actions(self, subparsers, actions_module):
for attr in (a for a in dir(actions_module) if a.startswith('do_')):
command = attr[3:].replace('_', '-')
callback = getattr(actions_module, attr)
desc = callback.__doc__ or ''
help = desc.strip().split('\n')[0]
arguments = getattr(callback, 'arguments', [])
subparser = subparsers.add_parser(command,
help=help,
description=desc,
add_help=False,
formatter_class=HelpFormatter)
subparser.add_argument('-h', '--help',
action='help',
help=argparse.SUPPRESS)
for (args, kwargs) in arguments:
subparser.add_argument(*args, **kwargs)
subparser.set_defaults(func=callback)
self.subcommands[command] = subparser
def _setup_masakari_client(self, api_ver, args):
"""Create masakari client using given args."""
kwargs = {
'auth_plugin': args.auth_plugin or 'password',
'auth_url': args.auth_url,
'project_name': args.project_name or args.tenant_name,
'project_id': args.project_id or args.tenant_id,
'domain_name': args.domain_name,
'domain_id': args.domain_id,
'project_domain_name': args.project_domain_name,
'project_domain_id': args.project_domain_id,
'user_domain_name': args.user_domain_name,
'user_domain_id': args.user_domain_id,
'username': args.username,
'user_id': args.user_id,
'password': args.password,
'verify': args.verify,
'token': args.token,
'trust_id': args.trust_id,
'interface': args.interface,
'region_name': args.region_name,
}
return masakari_client.Client(api_ver, user_agent=USER_AGENT, **kwargs)
def _setup_logging(self, debug):
if debug:
log_level = logging.DEBUG
else:
log_level = logging.WARNING
log_format = "%(levelname)s (%(module)s) %(message)s"
logging.basicConfig(format=log_format, level=log_level)
logging.getLogger('iso8601').setLevel(logging.WARNING)
logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING)
def main(self, argv):
parser = argparse.ArgumentParser(
prog='masakari',
description="masakari shell",
epilog='Type "masakari help <COMMAND>" for help on a specific '
'command.',
add_help=False,
)
# add add arguments
cliargs.add_global_args(parser, masakariclient.__version__)
cliargs.add_global_identity_args(parser)
# parse main arguments
(options, args) = parser.parse_known_args(argv)
self._setup_logging(options.debug)
base_parser = parser
api_ver = options.masakari_api_version
# add subparser
subcommand_parser = self._get_subcommand_parser(base_parser, api_ver)
self.parser = subcommand_parser
# --help/-h or no arguments
if not args and options.help or not argv:
self.do_help(options)
return 0
args = subcommand_parser.parse_args(argv)
sc = self._setup_masakari_client(api_ver, args)
# call specified function
args.func(sc.service, args)
@utils.arg('command', metavar='<subcommand>', nargs='?',
help=_('Display help for <subcommand>.'))
def do_help(self, args):
"""Display help about this program or one of its subcommands."""
if getattr(args, 'command', None):
if args.command in self.subcommands:
self.subcommands[args.command].print_help()
else:
raise exc.CommandError("'%s' is not a valid subcommand" %
args.command)
else:
self.parser.print_help()
class HelpFormatter(argparse.HelpFormatter):
def start_section(self, heading):
heading = '%s%s' % (heading[0].upper(), heading[1:])
super(HelpFormatter, self).start_section(heading)
def main(args=None):
try:
print(_("Deprecated: masakari CLI is deprecated and will be removed "
"after Stein is released. Use openstack CLI instead."),
file=sys.stderr)
if args is None:
args = sys.argv[1:]
MasakariShell().main(args)
except KeyboardInterrupt:
print(_("KeyboardInterrupt masakari client"), sys.stderr)
return 130
except Exception as e:
if '--debug' in args or '-d' in args:
raise
else:
print(encodeutils.safe_encode(str(e), sys.stderr))
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,73 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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 testtools
from unittest import mock
from masakariclient import cliargs
class TestCliArgs(testtools.TestCase):
def test_add_global_identity_args(self):
parser = mock.Mock()
cliargs.add_global_identity_args(parser)
expected = [
'--os-auth-plugin',
'--os-auth-url',
'--os-project-id',
'--os-project-name',
'--os-tenant-id',
'--os-tenant-name',
'--os-domain-id',
'--os-domain-name',
'--os-project-domain-id',
'--os-project-domain-name',
'--os-user-domain-id',
'--os-user-domain-name',
'--os-username',
'--os-user-id',
'--os-password',
'--os-trust-id',
'--os-token',
'--os-access-info',
'--os-interface',
'--os-region-name'
]
options = [arg[0][0] for arg in parser.add_argument.call_args_list]
self.assertEqual(expected, options)
parser.add_mutually_exclusive_group.assert_called_once_with()
group = parser.add_mutually_exclusive_group.return_value
verify_opts = [arg[0][0] for arg in group.add_argument.call_args_list]
verify_args = [
'--os-cacert',
'--verify',
'--insecure'
]
self.assertEqual(verify_args, verify_opts)
def test_add_global_args(self):
parser = mock.Mock()
cliargs.add_global_args(parser, '1')
expected = [
'-h',
'--masakari-api-version',
'-d'
]
options = [arg[0][0] for arg in parser.add_argument.call_args_list]
self.assertEqual(expected, options)

View File

@ -1,47 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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.
"""
test_masakariclient
----------------------------------
Tests for `masakariclient` module.
"""
from unittest import mock
from masakariclient import client as mc
from masakariclient.common import utils
from masakariclient.tests import base
class FakeClient(object):
def __init__(self, session):
super(FakeClient, self).__init__()
self.session = session
class TestMasakariclient(base.TestCase):
@mock.patch.object(utils, 'import_versioned_module')
def test_client_init(self, mock_import):
the_module = mock.Mock()
the_module.Client = FakeClient
mock_import.return_value = the_module
session = mock.Mock()
res = mc.Client('FAKE_VER', session)
mock_import.assert_called_once_with('FAKE_VER', 'client')
self.assertIsInstance(res, FakeClient)

View File

@ -1,128 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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.
"""
test_shell
----------------------------------
Tests for `masakariclient` module.
"""
import io
import logging
from unittest import mock
import sys
import testtools
from masakariclient import shell
from masakariclient.tests import base
class FakeClient(object):
def __init__(self):
super(FakeClient, self).__init__()
self.service = FakeService()
class FakeService(object):
def __init__(self):
super(FakeService, self).__init__()
def do_notification_list(self):
pass
class HelpFormatterTest(testtools.TestCase):
def test_start_section(self):
formatter = shell.HelpFormatter('masakari')
res = formatter.start_section(('dummyheading', 'dummy', 'dummy'))
self.assertIsNone(res)
heading = formatter._current_section.heading
self.assertEqual("DUMMYHEADING('dummy', 'dummy')", heading)
class TestMasakariShell(base.TestCase):
def setUp(self):
super(TestMasakariShell, self).setUp()
def _shell(self, func, *args, **kwargs):
orig_out = sys.stdout
sys.stdout = io.StringIO()
func(*args, **kwargs)
output = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = orig_out
return output
def test_do_bash_completion(self):
sh = shell.MasakariShell()
sc1 = mock.Mock()
sc2 = mock.Mock()
sc1._optionals._option_string_actions = ('A1', 'A2', 'C')
sc2._optionals._option_string_actions = ('B1', 'B2', 'C')
sh.subcommands = {
'command-foo': sc1,
'command-bar': sc2,
'bash-completion': None,
'bash_completion': None,
}
output = self._shell(sh.do_bash_completion, None)
output = output.split('\n')[0]
output_list = output.split(' ')
for option in ('A1', 'A2', 'C', 'B1', 'B2',
'command-foo', 'command-bar'):
self.assertIn(option, output_list)
@mock.patch.object(logging, 'basicConfig')
@mock.patch.object(logging, 'getLogger')
def test_setup_logging_debug_true(self, moc_getLogger,
moc_basicConfig):
sh = shell.MasakariShell()
sh._setup_logging(True)
moc_basicConfig.assert_called_once_with(
format="%(levelname)s (%(module)s) %(message)s",
level=logging.DEBUG)
mock_calls = [
mock.call('iso8601'),
mock.call().setLevel(logging.WARNING),
mock.call('urllib3.connectionpool'),
mock.call().setLevel(logging.WARNING),
]
moc_getLogger.assert_has_calls(mock_calls)
@mock.patch.object(logging, 'basicConfig')
@mock.patch.object(logging, 'getLogger')
def test_setup_logging_debug_false(self,
moc_getLogger,
moc_basicConfig):
sh = shell.MasakariShell()
sh._setup_logging(False)
moc_basicConfig.assert_called_once_with(
format="%(levelname)s (%(module)s) %(message)s",
level=logging.WARNING)
mock_calls = [
mock.call('iso8601'),
mock.call().setLevel(logging.WARNING),
mock.call('urllib3.connectionpool'),
mock.call().setLevel(logging.WARNING),
]
moc_getLogger.assert_has_calls(mock_calls)

View File

@ -1,29 +0,0 @@
# Copyright(c) 2017 Nippon Telegraph and Telephone Corporation
#
# 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 FakeSegment(object):
def __init__(self, segment_values):
self.segment_values = segment_values
def to_dict(self):
return self.segment_values
class FakeHost(object):
def __init__(self, host_values):
self.host_values = host_values
def to_dict(self):
return self.host_values

View File

@ -1,86 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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.
"""
test_masakariclient
----------------------------------
Tests for `masakariclient` module.
"""
from unittest import mock
from keystoneauth1.identity.generic import password as ks_password
from keystoneauth1 import session as ks_session
from openstack import connection
from masakariclient.tests import base
import masakariclient.v1.client as mc
class FakeConnection(object):
def __init__(self, prof=None, user_agent=None, **kwargs):
super(FakeConnection, self).__init__()
self.ha = None
class TestV1Client(base.TestCase):
def setUp(self):
super(TestV1Client, self).setUp()
self.auth = mock.Mock()
self.session = mock.Mock()
self.conn = mock.Mock()
self.service = mock.Mock()
self.conn.instance_ha = self.service
@mock.patch.object(connection, 'Connection')
@mock.patch.object(ks_session, 'Session')
@mock.patch.object(ks_password, 'Password')
def test_client_init(self, mock_password, mock_session, mock_connection):
mock_password.return_value = self.auth
mock_session.return_value = self.session
mock_connection.return_value = self.conn
fake_auth_url = 'fake_auth_url'
fake_username = 'fake_username'
fake_password = 'fake_password'
fake_user_domain_id = 'fake_user_domain_id'
fake_project_name = 'fake_project_name'
fake_project_domain_id = 'fake_project_domain_id'
fake_interface = 'fake_interface'
fake_region_name = 'fake_region_name'
res = mc.Client(auth_url=fake_auth_url,
username=fake_username,
password=fake_password,
user_domain_id=fake_user_domain_id,
project_name=fake_project_name,
project_domain_id=fake_project_domain_id,
interface=fake_interface,
region_name=fake_region_name)
self.assertEqual(self.conn.instance_ha, res.service)
mock_password.assert_called_once_with(
auth_url=fake_auth_url,
username=fake_username,
password=fake_password,
user_domain_id=fake_user_domain_id,
project_name=fake_project_name,
project_domain_id=fake_project_domain_id)
mock_session.assert_called_once_with(auth=self.auth)
mock_connection.assert_called_once_with(
session=self.session, interface=fake_interface,
region_name=fake_region_name,
ha_api_version=None)

View File

@ -1,371 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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.
"""
test_masakariclient
----------------------------------
Tests for `masakariclient` module.
"""
from unittest import mock
import ddt
import io
import sys
from openstack import exceptions as sdk_exc
from openstack.instance_ha.v1 import _proxy
from masakariclient.common.i18n import _
from masakariclient.common import utils
from masakariclient import shell
from masakariclient.tests import base
from masakariclient.tests.unit.v1 import fakes
import masakariclient.v1.shell as ms
@ddt.ddt
class TestV1Shell(base.TestCase):
def setUp(self):
super(TestV1Shell, self).setUp()
self.notification_vals = {
'notification_uuid': 'b3bf75d7-c2e9-4023-a10b-e5b464b9b539',
'source_host_uuid': '68fa7386-983e-4497-b5c4-3780f774d302',
'created_at': '2016-11-15T12:24:39.000000',
'updated_at': None,
'payload': {'event': 'STOPPED'},
'generated_time': '2016-10-10T10:00:00.000000',
'type': 'VM',
'id': '27'}
self.segment_vals = {
'uuid': '870da19d-37ec-41d2-a4b2-7be54b0d6ec9',
'created_at': '2016-11-17T10:08:32.000000',
'recovery_method': 'auto',
'updated_at': '2016-11-17T10:09:56.000000',
'name': 'testsegment05',
'service_type': 'testsegment01_auto',
'id': '14',
'description': 'UPDATE Discription'}
self.hosts_vals = {
'reserved': False,
'uuid': '0951e72c-49f5-46aa-8465-2d61ed3b46d9',
'deleted': False,
'on_maintenance': False,
'created_at': '2016-11-29T11:10:51.000000',
'control_attributes': 'control-attributesX',
'updated_at': '2016-11-29T11:30:18.000000',
'name': 'new_host-3',
'failover_segment': {
'uuid': '6b985a8a-f8c0-42e4-beaa-d2fcd8dabbb6',
'deleted': False,
'created_at': '2016-11-16T04:46:38.000000',
'description': None,
'recovery_method': 'auto',
'updated_at': None,
'service_type': 'testsegment01_auto',
'deleted_at': None,
'id': 3,
'name': 'testsegment01'},
'deleted_at': None,
'type': 'typeX',
'id': 10,
'failover_segment_id': '6b985a8a-f8c0-42e4-beaa-d2fcd8dabbb6'}
self.hosts_object = fakes.FakeHost(self.hosts_vals)
def _run_command(self, cmd):
shell.main(cmd.split())
@mock.patch.object(utils, 'print_list')
def test_do_notification_list(self, mock_print_list):
service = mock.Mock()
service.notifications.return_value = self.notification_vals
args = mock.Mock()
columns = [
'notification_uuid',
'generated_time',
'status',
'source_host_uuid',
'type']
ms.do_notification_list(service, args)
mock_print_list.assert_called_once_with(
self.notification_vals,
columns)
@mock.patch.object(utils, 'print_list')
def test_do_segment_list(self, mock_print_list):
service = mock.Mock()
service.segments.return_value = self.segment_vals
args = mock.Mock()
columns = [
'uuid',
'name',
'description',
'service_type',
'recovery_method']
ms.do_segment_list(service, args)
mock_print_list.assert_called_once_with(
self.segment_vals,
columns)
@mock.patch.object(utils, 'print_dict')
@mock.patch.object(utils, 'get_uuid_by_name')
def test_do_segment_show(self, mock_get_uuid_by_name, mock_print_dict):
mock_get_uuid_by_name.return_value = self.segment_vals.get('uuid')
service = mock.Mock()
segment_object = fakes.FakeSegment(self.segment_vals)
service.get_segment.return_value = segment_object
args = mock.Mock()
ms.do_segment_show(service, args)
mock_get_uuid_by_name.assert_called_once_with(service, args.id)
mock_print_dict.assert_called_once_with(self.segment_vals)
@mock.patch.object(utils, 'print_dict')
@ddt.data({"recovery_method": "auto"},
{"recovery_method": "reserved_host"},
{"recovery_method": "auto_priority"},
{"recovery_method": "rh_priority"})
def test_do_segment_create_with_all_recovery_methods(
self, ddt_data, mock_print_dict):
service = mock.Mock()
args = mock.Mock()
self.segment_vals['recovery_method'] = ddt_data['recovery_method']
segment_object = fakes.FakeSegment(self.segment_vals)
service.create_segment.return_value = segment_object
self._run_command(
"segment-create "
"--name 'test-segment'"
" --recovery-method %s "
"--service-type test_service" % ddt_data['recovery_method'])
ms.do_segment_create(service, args)
mock_print_dict.assert_called_once_with(
self.segment_vals)
def test_do_segment_create_with_invalid_recovery_method(self):
cmd = ("segment-create --name 'test-segment' --recovery-method"
" invalid_recovery_method --service-type test_service")
self.assertRaises(SystemExit, self._run_command, cmd)
@mock.patch.object(utils, 'print_dict')
@mock.patch.object(utils, 'remove_unspecified_items')
@mock.patch.object(utils, 'get_uuid_by_name')
@ddt.data({"recovery_method": "auto"},
{"recovery_method": "reserved_host"},
{"recovery_method": "auto_priority"},
{"recovery_method": "rh_priority"})
def test_do_segment_update(self, ddt_data,
mock_get_uuid_by_name,
mock_remove_unspecified_items,
mock_print_dict):
mock_get_uuid_by_name.return_value = self.segment_vals.get('uuid')
self.segment_vals['recovery_method'] = ddt_data['recovery_method']
mock_remove_unspecified_items.return_value = self.segment_vals
service = mock.Mock()
segment_object = fakes.FakeSegment(self.segment_vals)
service.update_segment.return_value = segment_object
args = mock.Mock()
args.recovery_method = ddt_data['recovery_method']
ms.do_segment_update(service, args)
mock_get_uuid_by_name.assert_called_once_with(service, args.id)
attrs = {
'name': args.name,
'description': args.description,
'recovery_method': args.recovery_method,
'service_type': args.service_type,
}
mock_remove_unspecified_items.assert_called_once_with(attrs)
mock_print_dict.assert_called_once_with(self.segment_vals)
def test_do_segment_update_with_invalid_recovery_method(self):
cmd = ("segment-update --id bdba001e-c01b-490e-9405-fdf72d849b41 "
"--recovery-method invalid_recovery_method")
self.assertRaises(SystemExit, self._run_command, cmd)
@mock.patch.object(utils, 'print_dict')
@mock.patch.object(utils, 'get_uuid_by_name')
def test_do_segment_delete(self, mock_get_uuid_by_name, mock_print_dict):
mock_get_uuid_by_name.return_value = self.segment_vals.get('uuid')
service = mock.Mock()
segment_object = fakes.FakeSegment(self.segment_vals)
service.delete_segment.return_value = segment_object
args = mock.Mock()
ms.do_segment_delete(service, args)
mock_get_uuid_by_name.assert_called_once_with(service, args.id)
mock_print_dict.assert_called_once_with(self.segment_vals)
@mock.patch.object(utils, 'get_uuid_by_name')
@mock.patch.object(_proxy.Proxy, 'delete_segment')
def test_do_segment_delete_with_non_existing_uuid(
self, mock_get_uuid_by_name, mock_delete_segment):
mock_get_uuid_by_name.return_value = self.segment_vals.get('uuid')
expected_msg = _("No failover segment with "
"id (%s)") % mock_get_uuid_by_name
def fake_delete_segment(self, mock_get_uuid_by_name,
ignore_missing=False):
if not ignore_missing:
raise sdk_exc.ResourceNotFound(expected_msg)
mock_delete_segment.side_effect = fake_delete_segment
service = mock.Mock()
args = mock.Mock()
original = sys.stdout
sys.stdout = io.StringIO()
ms.do_segment_delete(service, args)
output = sys.stdout.getvalue()
sys.stdout = original
self.assertIn(expected_msg, output)
@mock.patch.object(utils, 'print_list')
@mock.patch.object(utils, 'get_uuid_by_name')
def test_do_host_list(self, mock_get_uuid_by_name, mock_print_list):
mock_get_uuid_by_name.return_value = self.segment_vals.get('uuid')
service = mock.Mock()
service.hosts.return_value = self.hosts_vals
args = mock.Mock()
columns = [
'uuid',
'name',
'type',
'control_attributes',
'reserved',
'on_maintenance',
'failover_segment_id']
ms.do_host_list(service, args)
mock_get_uuid_by_name.assert_called_once_with(service, args.segment_id)
mock_print_list.assert_called_once_with(
self.hosts_vals,
columns)
@mock.patch.object(utils, 'print_dict')
@mock.patch.object(utils, 'get_uuid_by_name')
def test_do_host_show(self, mock_get_uuid_by_name, mock_print_dict):
mock_get_uuid_by_name.side_effect = [self.segment_vals.get('uuid'),
self.hosts_vals.get('uuid')]
service = mock.Mock()
service.get_host.return_value = self.hosts_object
args = mock.Mock()
ms.do_host_show(service, args)
mock_get_uuid_by_name.assert_any_call(service, args.segment_id)
mock_get_uuid_by_name.assert_any_call(
service, args.id, segment=self.segment_vals.get('uuid'))
mock_print_dict.assert_called_once_with(self.hosts_vals)
@mock.patch.object(utils, 'print_dict')
@mock.patch.object(utils, 'remove_unspecified_items')
@mock.patch.object(utils, 'get_uuid_by_name')
def test_do_host_create(self,
mock_get_uuid_by_name,
mock_remove_unspecified_items,
mock_print_dict):
mock_get_uuid_by_name.return_value = self.hosts_vals.get('uuid')
mock_remove_unspecified_items.return_value = self.hosts_vals
service = mock.Mock()
service.create_host.return_value = self.hosts_object
args = mock.Mock()
ms.do_host_create(service, args)
mock_get_uuid_by_name.assert_called_once_with(service, args.segment_id)
attrs = {
'name': args.name,
'type': args.type,
'control_attributes': args.control_attributes,
'reserved': args.reserved,
'on_maintenance': args.on_maintenance,
}
mock_remove_unspecified_items.assert_called_once_with(attrs)
mock_print_dict.assert_called_once_with(self.hosts_vals)
@mock.patch.object(utils, 'print_dict')
@mock.patch.object(utils, 'remove_unspecified_items')
@mock.patch.object(utils, 'get_uuid_by_name')
def test_do_host_update(self,
mock_get_uuid_by_name,
mock_remove_unspecified_items,
mock_print_dict):
mock_get_uuid_by_name.side_effect = [self.segment_vals.get('uuid'),
self.hosts_vals.get('uuid')]
mock_remove_unspecified_items.return_value = self.hosts_vals
service = mock.Mock()
service.update_host.return_value = self.hosts_object
args = mock.Mock()
ms.do_host_update(service, args)
mock_get_uuid_by_name.assert_any_call(service, args.segment_id)
mock_get_uuid_by_name.assert_any_call(
service, args.id, segment=self.segment_vals.get('uuid'))
attrs = {
'name': args.name,
'type': args.type,
'control_attributes': args.control_attributes,
'reserved': args.reserved,
'on_maintenance': args.on_maintenance,
}
mock_remove_unspecified_items.assert_called_once_with(attrs)
mock_print_dict.assert_called_once_with(self.hosts_vals)
@mock.patch.object(utils, 'print_dict')
@mock.patch.object(utils, 'get_uuid_by_name')
def test_do_host_delete(self, mock_get_uuid_by_name, mock_print_dict):
mock_get_uuid_by_name.side_effect = [self.segment_vals.get('uuid'),
self.hosts_vals.get('uuid')]
service = mock.Mock()
service.delete_host.return_value = self.hosts_object
args = mock.Mock()
ms.do_host_delete(service, args)
mock_get_uuid_by_name.assert_any_call(service, args.segment_id)
mock_get_uuid_by_name.assert_any_call(
service, args.id, segment=self.segment_vals.get('uuid'))
mock_print_dict.assert_called_once_with(self.hosts_vals)
@mock.patch.object(utils, 'get_uuid_by_name')
@mock.patch.object(_proxy.Proxy, 'delete_host')
def test_do_host_delete_with_non_existing_uuid(self,
mock_get_uuid_by_name,
mock_delete_host):
mock_get_uuid_by_name.return_value = self.segment_vals.get('uuid')
host_id = self.hosts_vals.get('uuid')
expected_msg = _("Host '%(host_id)s' under failover_segment " "'"
"%(seg_id)s' ""could not be found") % {
'host_id': host_id, 'seg_id': mock_get_uuid_by_name}
def fake_delete_host(
host_id, mock_get_uuid_by_name, ignore_missing=False):
if not ignore_missing:
raise sdk_exc.ResourceNotFound(expected_msg)
mock_delete_host.side_effect = fake_delete_host
service = mock.Mock()
args = mock.Mock()
original = sys.stdout
sys.stdout = io.StringIO()
ms.do_host_delete(service, args)
output = sys.stdout.getvalue()
sys.stdout = original
self.assertIn(expected_msg, output)

View File

@ -1,40 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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 keystoneauth1.identity.generic import password as ks_password
from keystoneauth1 import session as ks_session
from openstack import connection
class Client(object):
def __init__(self, **kwargs):
session = kwargs.get('session')
if session is None:
auth = ks_password.Password(
auth_url=kwargs.get('auth_url'),
username=kwargs.get('username'),
password=kwargs.get('password'),
user_domain_id=kwargs.get('user_domain_id'),
project_name=kwargs.get('project_name'),
project_domain_id=kwargs.get('project_domain_id'))
session = ks_session.Session(auth=auth)
con = connection.Connection(
session=session,
interface=kwargs.get('interface'),
region_name=kwargs.get('region_name'),
ha_api_version=kwargs.get('api_version'))
self.service = con.instance_ha

View File

@ -1,292 +0,0 @@
# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation
#
# 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 oslo_serialization import jsonutils
from masakariclient.common import utils
def do_notification_list(service, args):
"""List notifications.
:param service: service object.
:param args: API args.
"""
try:
notifications = service.notifications()
columns = [
'notification_uuid', 'generated_time', 'status',
'source_host_uuid', 'type']
utils.print_list(notifications, columns)
except Exception as e:
print(e)
@utils.arg('--id', metavar='<NOTIFICATION_ID>', required=True,
help='Notification to display (name or ID)')
def do_notification_show(service, args):
"""Show a notification details."""
try:
notification = service.get_notification(args.id)
utils.print_dict(notification.to_dict())
except Exception as e:
print(e)
@utils.arg('--type', metavar='<TYPE>', required=True,
choices=['COMPUTE_HOST', 'VM', 'PROCESS'],
help='Type of failure. The supported options are: '
'COMPUTE_HOST, VM, PROCESS.')
@utils.arg('--hostname', metavar='<HOSTNAME>', required=True,
help='Hostname of notification.')
@utils.arg('--generated-time', metavar='<GENERATED_TIME>', required=True,
help='Timestamp for notification. e.g. 2016-01-01T01:00:00.000')
@utils.arg('--payload', metavar='<PAYLOAD>', required=True,
help='JSON string about failure event.')
def do_notification_create(service, args):
"""Create a notification."""
try:
payload = jsonutils.loads(args.payload)
attrs = {
'type': args.type,
'hostname': args.hostname,
'generated_time': args.generated_time,
'payload': payload,
}
notification = service.create_notification(**attrs)
utils.print_dict(notification.to_dict())
except Exception as e:
print(e)
def do_segment_list(service, args):
"""List segments."""
try:
segments = service.segments()
fields = [
'uuid', 'name', 'description',
'service_type', 'recovery_method']
utils.print_list(segments, fields)
except Exception as e:
print(e)
@utils.arg('--id', metavar='<SEGMENT_ID>', required=True,
help='Segment to display (name or ID)')
def do_segment_show(service, args):
"""Show a segment details."""
try:
segment_id = utils.get_uuid_by_name(service, args.id)
segment = service.get_segment(segment_id)
utils.print_dict(segment.to_dict())
except Exception as e:
print(e)
@utils.arg('--name', metavar='<SEGMENT_NAME>', required=True,
help='Name of segment.')
@utils.arg('--recovery-method', metavar='<RECOVERY_METHOD>', required=True,
choices=['auto', 'reserved_host', 'auto_priority', 'rh_priority'],
help='Recovery method. '
'The supported options are: auto, reserved_host,'
' auto_priority, rh_priority.')
@utils.arg('--service-type', metavar='<SERVICE_TYPE>', required=True,
help='Service type of segment.')
@utils.arg('--description', metavar='<DESCRIPTION>', required=False,
help='Description of segment.')
def do_segment_create(service, args):
"""Create segment."""
try:
attrs = {
'name': args.name,
'description': args.description,
'recovery_method': args.recovery_method,
'service_type': args.service_type,
}
segment = service.create_segment(**attrs)
utils.print_dict(segment.to_dict())
except Exception as e:
print(e)
@utils.arg('--id', metavar='<SEGMENT_ID>',
required=True, help='Name or ID of segment.')
@utils.arg('--name', metavar='<SEGMENT_NAME>',
required=False, help='Name of segment.')
@utils.arg('--recovery-method', metavar='<RECOVERY_METHOD>',
choices=['auto', 'reserved_host', 'auto_priority', 'rh_priority'],
required=False,
help='Recovery method. '
'The supported options are: auto, reserved_host, '
'auto_priority, rh_priority.')
@utils.arg('--service-type', metavar='<SERVICE_TYPE>',
required=False, help='Service type of segment.')
@utils.arg('--description', metavar='<DESCRIPTION>',
required=False, help='Description of segment.')
def do_segment_update(service, args):
"""Update a segment."""
try:
segment_id = utils.get_uuid_by_name(
service, args.id)
attrs = {
'name': args.name,
'description': args.description,
'recovery_method': args.recovery_method,
'service_type': args.service_type,
}
attrs = utils.remove_unspecified_items(attrs)
segment = service.update_segment(segment_id, **attrs)
utils.print_dict(segment.to_dict())
except Exception as e:
print(e)
@utils.arg('--id', metavar='<SEGMENT_ID>', required=True,
help='Name or ID of the segment to delete.')
def do_segment_delete(service, args):
"""Delete a segment."""
try:
segment_id = utils.get_uuid_by_name(
service, args.id)
segment = service.delete_segment(segment_id, ignore_missing=False)
utils.print_dict(segment.to_dict())
except Exception as e:
print(e)
@utils.arg('--segment-id', metavar='<SEGMENT_ID>', required=True,
help='Segment to display (name or ID)')
def do_host_list(service, args):
"""List hosts."""
try:
segment_id = utils.get_uuid_by_name(
service, args.segment_id)
hosts = service.hosts(segment_id)
fields = [
'uuid', 'name', 'type', 'control_attributes', 'reserved',
'on_maintenance', 'failover_segment_id']
utils.print_list(hosts, fields)
except Exception as e:
print(e)
@utils.arg('--segment-id', metavar='<SEGMENT_ID>', required=True,
help='Segment to display (name or ID)')
@utils.arg('--id', metavar='<HOST_ID>', required=True,
help='Host to display (name or ID)')
def do_host_show(service, args):
"""Show a host details."""
try:
segment_id = utils.get_uuid_by_name(
service, args.segment_id)
host_id = utils.get_uuid_by_name(
service, args.id, segment=segment_id)
host = service.get_host(host_id, segment_id=segment_id)
utils.print_dict(host.to_dict())
except Exception as e:
print(e)
@utils.arg('--name', metavar='<HOST_NAME>', required=True,
help='Name of host.')
@utils.arg('--type', metavar='<TYPE>', required=True,
help='Type of host.')
@utils.arg('--control-attributes', metavar='<CONTROL_ATTRIBUTES>',
required=True, help='Control attributes of host.')
@utils.arg('--segment-id', metavar='<SEGMENT_ID>', required=True,
help='Name or ID of segment.')
@utils.arg('--reserved', metavar='<RESERVED>', required=False,
choices=['True', 'False'],
help='Host reservation. The supported options are: True, False.')
@utils.arg('--on-maintenance', metavar='<ON_MAINTENANCE>', required=False,
choices=['True', 'False'],
help='Maintenance status of host. The supported options are: '
'True, False.')
def do_host_create(service, args):
"""Create a host."""
try:
segment_id = utils.get_uuid_by_name(
service, args.segment_id)
attrs = {
'name': args.name,
'type': args.type,
'control_attributes': args.control_attributes,
'reserved': args.reserved,
'on_maintenance': args.on_maintenance,
}
utils.remove_unspecified_items(attrs)
host = service.create_host(segment_id, **attrs)
utils.print_dict(host.to_dict())
except Exception as e:
print(e)
@utils.arg('--segment-id', metavar='<SEGMENT_ID>', required=True,
help='Name or ID of segment.')
@utils.arg('--id', metavar='<HOST_ID>', required=True,
help='Name or ID of host.')
@utils.arg('--reserved', metavar='<RESERVED>', required=False,
choices=['True', 'False'],
help='Host reservation. The supported options are: True, False.')
@utils.arg('--on-maintenance', metavar='<ON_MAINTENANCE>',
required=False, choices=['True', 'False'],
help='Maintenance status of host. The supported options are: '
'True, False.')
@utils.arg('--name', metavar='<HOST_NAME>', required=False,
help='Name of host.')
@utils.arg('--type', metavar='<TYPE>', required=False,
help='Type of host.')
@utils.arg('--control-attributes', metavar='<CONTROL_ATTRIBUTES>',
required=False, help='Control attributes of host.')
def do_host_update(service, args):
"""Update a host."""
try:
segment_id = utils.get_uuid_by_name(
service, args.segment_id)
host_id = utils.get_uuid_by_name(
service, args.id, segment=segment_id)
attrs = {
'name': args.name,
'type': args.type,
'control_attributes': args.control_attributes,
'reserved': args.reserved,
'on_maintenance': args.on_maintenance,
}
attrs = utils.remove_unspecified_items(attrs)
host = service.update_host(host_id, segment_id=segment_id, **attrs)
utils.print_dict(host.to_dict())
except Exception as e:
print(e)
@utils.arg('--segment-id', metavar='<SEGMENT_ID>', required=True,
help='Name or ID of segment.')
@utils.arg('--id', metavar='<HOST_ID>', required=True,
help='Name or ID of the host to delete.')
def do_host_delete(service, args):
"""Delete a host."""
try:
segment_id = utils.get_uuid_by_name(
service, args.segment_id)
host_id = utils.get_uuid_by_name(
service, args.id, segment=segment_id)
host = service.delete_host(host_id, segment_id=segment_id,
ignore_missing=False)
utils.print_dict(host.to_dict())
except Exception as e:
print(e)

View File

@ -0,0 +1,6 @@
---
upgrade:
- |
The legacy Masakari client (invoked by calling ``masakari`` as opposed to
``openstack``) has been removed. It has been deprecated since
Stein.

View File

@ -26,9 +26,6 @@ packages =
masakariclient masakariclient
[entry_points] [entry_points]
console_scripts =
masakari = masakariclient.shell:main
openstack.cli.extension = openstack.cli.extension =
ha = masakariclient.plugin ha = masakariclient.plugin