From bd9e853604eed9d46dfffbb9a7563a506bcb6a10 Mon Sep 17 00:00:00 2001 From: Peter Sabaini Date: Tue, 29 Aug 2017 20:42:43 +0200 Subject: [PATCH] Add action to retrieve hugepage config Return current hugepage usage and kernel cmdline for static hugepage allocation Change-Id: Ib34b2d7c3da5aacd117b19249be41bb95b91dfcd Closes-Bug: #1734360 --- actions.yaml | 4 +- actions/hugepagereport | 1 + actions/hugepagereport.py | 52 +++++++++++++++++++++ tests/basic_deployment.py | 13 ++++++ unit_tests/test_actions_hugepagereport.py | 55 +++++++++++++++++++++++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 120000 actions/hugepagereport create mode 100755 actions/hugepagereport.py create mode 100644 unit_tests/test_actions_hugepagereport.py diff --git a/actions.yaml b/actions.yaml index 05d889ca..297c041e 100644 --- a/actions.yaml +++ b/actions.yaml @@ -3,4 +3,6 @@ openstack-upgrade: pause: description: Pause the nova_compute unit. This action will stop nova_compute services. resume: - descrpition: Resume the nova_compute unit. This action will start nova_compute services. + description: Resume the nova_compute unit. This action will start nova_compute services. +hugepagereport: + description: Report on hugepage configuration and usage diff --git a/actions/hugepagereport b/actions/hugepagereport new file mode 120000 index 00000000..c01ad5c6 --- /dev/null +++ b/actions/hugepagereport @@ -0,0 +1 @@ +hugepagereport.py \ No newline at end of file diff --git a/actions/hugepagereport.py b/actions/hugepagereport.py new file mode 100755 index 00000000..957821b3 --- /dev/null +++ b/actions/hugepagereport.py @@ -0,0 +1,52 @@ +#!/usr/bin/python +# +# Copyright 2016 Canonical Ltd +# +# 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 sys + +sys.path.append('hooks') + +import subprocess +from charmhelpers.core import hookenv + +SYSFS = '/sys' +KERNELCMD = '/proc/cmdline' + + +def hugepages_report(): + '''Action to return current hugepage usage and kernel cmdline for static + hugepage allocation. Takes no params. + ''' + outmap = {} + try: + devp = "{}/devices/system/node/node*/hugepages/*/*".format(SYSFS) + outmap['hugepagestats'] = subprocess.check_output( + "grep -H . {}".format(devp), + shell=True).decode('UTF-8') + except subprocess.CalledProcessError as e: + hookenv.log(e) + hookenv.action_fail( + "Getting hugepages report failed: {}".format(e.message) + ) + with open(KERNELCMD, 'rb') as cmdline: + try: + outmap['kernelcmd'] = cmdline.read().strip() + except IOError as e: + hookenv.action_fail('Could not read {}: {}'.format(KERNELCMD, e)) + return + hookenv.action_set(outmap) + +if __name__ == '__main__': + hugepages_report() diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index 325e18bd..79b87100 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -615,6 +615,19 @@ class NovaBasicDeployment(OpenStackAmuletDeployment): u.delete_resource(self.nova_demo.servers, instance.id, msg="nova instance") + def test_500_hugepagereport_action(self): + """Verify hugepagereport""" + u.log.debug("Testing hugepagereport") + sentry_unit = self.nova_compute_sentry + + action_id = u.run_action(sentry_unit, "hugepagereport") + assert u.wait_on_action(action_id), "Hugepagereport action failed." + data = amulet.actions.get_action_output(action_id, full_output=True) + assert data.get(u"status") == "completed", ("Hugepagereport action" + "failed") + report = data.get(u"results").get(u"hugepagestats") + assert report.find('free_hugepages') != -1 + def test_900_restart_on_config_change(self): """Verify that the specified services are restarted when the config is changed.""" diff --git a/unit_tests/test_actions_hugepagereport.py b/unit_tests/test_actions_hugepagereport.py new file mode 100644 index 00000000..afc53b73 --- /dev/null +++ b/unit_tests/test_actions_hugepagereport.py @@ -0,0 +1,55 @@ +# Copyright 2016 Canonical Ltd +# +# 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 os +import shutil +from tempfile import mkdtemp +from test_utils import CharmTestCase + +import hugepagereport as actions + +tmpdir = 'hugepagestats-test.' + +test_stats = {'free_hugepages': '224', + 'nr_hugepages': '12'} + + +class MainTestCase(CharmTestCase): + + def setUp(self): + self.sysfs = sysfs = mkdtemp(prefix=tmpdir) + self.addCleanup(shutil.rmtree, sysfs) + p = mock.patch('hugepagereport.SYSFS', new=sysfs) + p.start() + self.addCleanup(p.stop) + hpath = "{}/devices/system/node/node0/hugepages/hugepages-1048576kB" + self.hugepagestats = hpath.format(sysfs) + os.makedirs(self.hugepagestats) + for fn, val in test_stats.items(): + with open(os.path.join(self.hugepagestats, fn), 'w') as f: + f.write(val) + + @mock.patch('charmhelpers.core.hookenv.action_get') + @mock.patch('charmhelpers.core.hookenv.action_set') + def test_hugepagesreport(self, mock_action_set, mock_action_get): + dummy_action = [] + mock_action_set.side_effect = dummy_action.append + actions.hugepages_report() + self.assertEqual(len(dummy_action), 1) + d = dummy_action[0] + self.assertIsInstance(d, dict) + self.assert_('hugepagestats' in d) + self.assert_( + d['hugepagestats'].find('/free_hugepages') != -1)