summaryrefslogtreecommitdiff
path: root/tests/unittests/test_cli.py
diff options
context:
space:
mode:
authorChad Smith <chad.smith@canonical.com>2017-08-21 13:46:23 -0600
committerChad Smith <chad.smith@canonical.com>2017-08-21 13:46:23 -0600
commite74d7752f1761c3a8d3c19877de4707d00c49d08 (patch)
tree0627dd5d06d83b770b38196bceabddd06c0b1b77 /tests/unittests/test_cli.py
parentdc2bd79949492bccdc1d7df0132f98c354d51943 (diff)
downloadvyos-cloud-init-e74d7752f1761c3a8d3c19877de4707d00c49d08.tar.gz
vyos-cloud-init-e74d7752f1761c3a8d3c19877de4707d00c49d08.zip
tools: Add tooling for basic cloud-init performance analysis.
This branch adds cloudinit-analyze into cloud-init proper. It adds an "analyze" subcommand to the cloud-init command line utility for quick performance assessment of cloud-init stages and events. On a cloud-init configured instance, running "cloud-init analyze blame" will now report which cloud-init events cost the most wall time. This allows for quick assessment of the most costly stages of cloud-init. This functionality is pulled from Ryan Harper's analyze work. The cloudinit-analyze main script itself has been refactored a bit for inclusion as a subcommand of cloud-init CLI. There will be a followup branch at some point which will optionally instrument detailed strace profiling, but that approach needs a bit more discussion first. This branch also adds: * additional debugging topic to the sphinx-generated docs describing cloud-init analyze, dump and show as well as cloud-init single usage. * Updates the Makefile unittests target to include cloudinit directory because we now have unittests within that package. LP: #1709761
Diffstat (limited to 'tests/unittests/test_cli.py')
-rw-r--r--tests/unittests/test_cli.py87
1 files changed, 84 insertions, 3 deletions
diff --git a/tests/unittests/test_cli.py b/tests/unittests/test_cli.py
index 06f366b2..7780f164 100644
--- a/tests/unittests/test_cli.py
+++ b/tests/unittests/test_cli.py
@@ -31,9 +31,90 @@ class TestCLI(test_helpers.FilesystemMockingTestCase):
def test_no_arguments_shows_error_message(self):
exit_code = self._call_main()
- self.assertIn('cloud-init: error: too few arguments',
- self.stderr.getvalue())
+ missing_subcommand_message = [
+ 'too few arguments', # python2.7 msg
+ 'the following arguments are required: subcommand' # python3 msg
+ ]
+ error = self.stderr.getvalue()
+ matches = ([msg in error for msg in missing_subcommand_message])
+ self.assertTrue(
+ any(matches), 'Did not find error message for missing subcommand')
self.assertEqual(2, exit_code)
+ def test_all_subcommands_represented_in_help(self):
+ """All known subparsers are represented in the cloud-int help doc."""
+ self._call_main()
+ error = self.stderr.getvalue()
+ expected_subcommands = ['analyze', 'init', 'modules', 'single',
+ 'dhclient-hook', 'features']
+ for subcommand in expected_subcommands:
+ self.assertIn(subcommand, error)
-# vi: ts=4 expandtab
+ @mock.patch('cloudinit.cmd.main.status_wrapper')
+ def test_init_subcommand_parser(self, m_status_wrapper):
+ """The subcommand 'init' calls status_wrapper passing init."""
+ self._call_main(['cloud-init', 'init'])
+ (name, parseargs) = m_status_wrapper.call_args_list[0][0]
+ self.assertEqual('init', name)
+ self.assertEqual('init', parseargs.subcommand)
+ self.assertEqual('init', parseargs.action[0])
+ self.assertEqual('main_init', parseargs.action[1].__name__)
+
+ @mock.patch('cloudinit.cmd.main.status_wrapper')
+ def test_modules_subcommand_parser(self, m_status_wrapper):
+ """The subcommand 'modules' calls status_wrapper passing modules."""
+ self._call_main(['cloud-init', 'modules'])
+ (name, parseargs) = m_status_wrapper.call_args_list[0][0]
+ self.assertEqual('modules', name)
+ self.assertEqual('modules', parseargs.subcommand)
+ self.assertEqual('modules', parseargs.action[0])
+ self.assertEqual('main_modules', parseargs.action[1].__name__)
+
+ def test_analyze_subcommand_parser(self):
+ """The subcommand cloud-init analyze calls the correct subparser."""
+ self._call_main(['cloud-init', 'analyze'])
+ # These subcommands only valid for cloud-init analyze script
+ expected_subcommands = ['blame', 'show', 'dump']
+ error = self.stderr.getvalue()
+ for subcommand in expected_subcommands:
+ self.assertIn(subcommand, error)
+
+ @mock.patch('cloudinit.cmd.main.main_single')
+ def test_single_subcommand(self, m_main_single):
+ """The subcommand 'single' calls main_single with valid args."""
+ self._call_main(['cloud-init', 'single', '--name', 'cc_ntp'])
+ (name, parseargs) = m_main_single.call_args_list[0][0]
+ self.assertEqual('single', name)
+ self.assertEqual('single', parseargs.subcommand)
+ self.assertEqual('single', parseargs.action[0])
+ self.assertFalse(parseargs.debug)
+ self.assertFalse(parseargs.force)
+ self.assertIsNone(parseargs.frequency)
+ self.assertEqual('cc_ntp', parseargs.name)
+ self.assertFalse(parseargs.report)
+
+ @mock.patch('cloudinit.cmd.main.dhclient_hook')
+ def test_dhclient_hook_subcommand(self, m_dhclient_hook):
+ """The subcommand 'dhclient-hook' calls dhclient_hook with args."""
+ self._call_main(['cloud-init', 'dhclient-hook', 'net_action', 'eth0'])
+ (name, parseargs) = m_dhclient_hook.call_args_list[0][0]
+ self.assertEqual('dhclient_hook', name)
+ self.assertEqual('dhclient-hook', parseargs.subcommand)
+ self.assertEqual('dhclient_hook', parseargs.action[0])
+ self.assertFalse(parseargs.debug)
+ self.assertFalse(parseargs.force)
+ self.assertEqual('net_action', parseargs.net_action)
+ self.assertEqual('eth0', parseargs.net_interface)
+
+ @mock.patch('cloudinit.cmd.main.main_features')
+ def test_features_hook_subcommand(self, m_features):
+ """The subcommand 'features' calls main_features with args."""
+ self._call_main(['cloud-init', 'features'])
+ (name, parseargs) = m_features.call_args_list[0][0]
+ self.assertEqual('features', name)
+ self.assertEqual('features', parseargs.subcommand)
+ self.assertEqual('features', parseargs.action[0])
+ self.assertFalse(parseargs.debug)
+ self.assertFalse(parseargs.force)
+
+# : ts=4 expandtab