Merge "Implemented the notification subcommands masakari CLI"

This commit is contained in:
Jenkins 2017-02-14 01:35:41 +00:00 committed by Gerrit Code Review
commit 3b00dff235
10 changed files with 363 additions and 5 deletions

View File

@ -30,7 +30,9 @@ def add_global_args(parser, version):
help=_('Version number for Masakari API to use, Default to "1".'))
parser.add_argument(
'--debug', default=False, action='store_true',
'-d', '--debug',
action='store_true',
default=False,
help=_('Print debugging output.'))

View File

@ -13,6 +13,7 @@
# limitations under the License.
import argparse
import logging
import sys
from oslo_utils import encodeutils
@ -26,6 +27,7 @@ from masakariclient.common.i18n import _
from masakariclient.common import utils
USER_AGENT = 'python-masakariclient'
LOG = logging.getLogger(__name__)
class MasakariShell(object):
@ -113,6 +115,16 @@ class MasakariShell(object):
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(
@ -129,6 +141,8 @@ class MasakariShell(object):
# parse main arguments
(options, args) = parser.parse_known_args(argv)
self._setup_logging(options.debug)
base_parser = parser
api_ver = options.masakari_api_version
@ -177,7 +191,7 @@ def main(args=None):
print(_("KeyboardInterrupt masakari client"), sys.stderr)
return 130
except Exception as e:
if '--debug' in args:
if '--debug' in args or '-d' in args:
raise
else:
print(encodeutils.safe_encode(six.text_type(e)), sys.stderr)

View File

View File

@ -0,0 +1,29 @@
# 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 mock
import testtools
from masakariclient import cliargs
class TestCliArgs(testtools.TestCase):
def test_add_global_identity_args(self):
parser = mock.Mock()
cliargs.add_global_identity_args(parser)
def test_add_global_args(self):
parser = mock.Mock()
cliargs.add_global_args(parser, '1')

View File

@ -1,3 +1,5 @@
# 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
@ -16,11 +18,30 @@ test_masakariclient
Tests for `masakariclient` module.
"""
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):
def test_something(self):
pass
@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

@ -0,0 +1,128 @@
# 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 logging
import mock
import six
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()
return self.service
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 = six.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

View File

@ -0,0 +1,50 @@
# 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.
"""
import mock
from masakariclient.sdk.ha 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.vmha = None
class TestV1Client(base.TestCase):
def setUp(self):
super(TestV1Client, self).setUp()
self.conn = mock.Mock()
self.service = mock.Mock()
self.conn.vmha = self.service
def test_client_init(self):
with mock.patch.object(connection,
'create_connection') as mock_connection:
mock_connection.return_value = self.conn
res = mc.Client()
self.assertEqual(self.conn.ha, res.service)
mock_connection.assert_called_once_with(prof=None, user_agent=None)

View File

@ -0,0 +1,57 @@
# 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.
"""
import mock
from masakariclient.common import utils
from masakariclient.tests import base
import masakariclient.v1.shell as ms
class TestV1Shell(base.TestCase):
def setUp(self):
super(TestV1Shell, self).setUp()
self.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'}
@mock.patch.object(utils, 'print_list')
def test_do_notification_list(self, mock_print_list):
service = mock.Mock()
service.notifications.return_value = self.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.vals,
columns)

View File

@ -12,4 +12,61 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Implement sub commands in this file after that."""
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,
help='Type of failure.')
@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)