From a85fdc643ba144d4a5f2031dfb40c0b6456507ec Mon Sep 17 00:00:00 2001 From: Katerina Pilatova Date: Tue, 18 Apr 2017 15:08:24 +0200 Subject: [PATCH] Unit testing for undercloud_conf - modifies custom module undercloud_conf.py in such a way that its individual stages can be easily tested - tests for this module (test_undercloud_conf.py) are created Change-Id: I65951cd9bc864a731d485a94bf468eab46e7e2a0 Partial-Bug: #1594785 --- .../tests/test_undercloud_conf.py | 144 ++++++++++++++++++ validations/library/undercloud_conf.py | 71 +++++++-- 2 files changed, 201 insertions(+), 14 deletions(-) create mode 100644 tripleo_validations/tests/test_undercloud_conf.py diff --git a/tripleo_validations/tests/test_undercloud_conf.py b/tripleo_validations/tests/test_undercloud_conf.py new file mode 100644 index 000000000..912abc1fd --- /dev/null +++ b/tripleo_validations/tests/test_undercloud_conf.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- + +# 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_undercloud_conf +---------------------------------- + +Tests for `undercloud_conf` module. +""" + +import os +import tempfile + +from tripleo_validations.tests import base +import validations.library.undercloud_conf as validation + + +missing_section_content = 'hello world' + +valid_content = ''' +[DEFAULT] +debug=True + +[dhcp] +dhcp_start=192.168.0.1 +dhcp_end=192.168.0.254 + +[secrets] +password=1234 +''' + +parsing_error_content = ''' +[DEFAULT] + debug = + + [ip +''' + +valid_result = {'DEFAULT': {'debug': 'True'}, + 'dhcp': {'debug': 'True', + 'dhcp_end': '192.168.0.254', + 'dhcp_start': '192.168.0.1'}, + 'secrets': {'debug': 'True', + 'password': '1234'}} + + +class TestUndercloudConf(base.TestCase): + + def test_check_arguments_invalid_path(self): + '''Test undercloud_conf when path is invalid''' + + exists, errors = validation.check_arguments('non/existing/path', False) + self.assertEqual(False, exists) + self.assertEqual(1, len(errors)) + self.assertEqual(errors[0], 'Could not open the undercloud.conf file' + + ' at non/existing/path') + + def test_check_arguments_valid_path(self): + '''Test undercloud_conf when path is valid''' + + tmpfile = self.create_tmp_undercloud_conf() + tmp_name = os.path.relpath(tmpfile.name) + exists, errors = validation.check_arguments(tmp_name, False) + tmpfile.close() + + self.assertEqual(True, exists) + self.assertEqual([], errors) + + def test_check_arguments_ignore_missing_invalid_path(self): + '''Test undercloud_conf when ignore_missing is set, path is invalid''' + + exists, errors = validation.check_arguments('non/existing/path', True) + self.assertEqual(False, exists) + self.assertEqual([], errors) + + def test_check_arguments_ignore_missing_valid_path(self): + '''Test undercloud_conf when ignore_missing is set, path is valid''' + + tmpfile = self.create_tmp_undercloud_conf() + tmp_name = os.path.relpath(tmpfile.name) + exists, errors = validation.check_arguments(tmp_name, True) + tmpfile.close() + + self.assertEqual(True, exists) + self.assertEqual([], errors) + + def test_get_result_missing_section_headers(self): + '''Test undercloud_conf when content format is invalid''' + + tmpfile = self.create_tmp_undercloud_conf() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(missing_section_content.encode('utf-8')) + tmpfile.seek(0) + errors, result = validation.get_result(tmp_name) + tmpfile.close() + + self.assertEqual({}, result) + self.assertEqual(1, len(errors)) + self.assertEqual('File contains no section headers.', errors[0]) + + def test_get_result_parsing_error(self): + '''Test undercloud_conf when content format is invalid''' + + tmpfile = self.create_tmp_undercloud_conf() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(parsing_error_content.encode('utf-8')) + tmpfile.seek(0) + errors, result = validation.get_result(tmp_name) + tmpfile.close() + + self.assertEqual({}, result) + self.assertEqual(1, len(errors)) + self.assertEqual('File contains parsing errors.', errors[0]) + + def test_get_result_valid_file(self): + '''Test undercloud_conf when content format is valid''' + + tmpfile = self.create_tmp_undercloud_conf() + tmp_name = os.path.relpath(tmpfile.name) + tmpfile.write(valid_content.encode('utf-8')) + tmpfile.seek(0) + errors, result = validation.get_result(tmp_name) + tmpfile.close() + + self.assertEqual(valid_result, result) + self.assertEqual([], errors) + + def create_tmp_undercloud_conf(self): + '''Create temporary undercloud.conf file, return its full name''' + path = 'tripleo_validations/tests' + tmpfile = tempfile.NamedTemporaryFile(suffix='.conf', + prefix='undercloud', dir=path) + return tmpfile diff --git a/validations/library/undercloud_conf.py b/validations/library/undercloud_conf.py index 5d82bee0a..f4a525855 100644 --- a/validations/library/undercloud_conf.py +++ b/validations/library/undercloud_conf.py @@ -14,12 +14,54 @@ # License for the specific language governing permissions and limitations # under the License. -import ConfigParser +try: + import configparser as ConfigParser +except ImportError: + import ConfigParser + from os import path from ansible.module_utils.basic import * # noqa +def check_arguments(undercloud_conf_path, ignore_missing): + '''Validate format of arguments + + return: (True, errors) if file can be opened + (False, errors) if ignore_missing or check failed + ''' + + errors = [] + + if path.exists(undercloud_conf_path) and path.isfile(undercloud_conf_path): + return (True, errors) + else: + if not ignore_missing: + errors.append('Could not open the undercloud.conf file at {}' + .format(undercloud_conf_path)) + return (False, errors) + + +def get_result(undercloud_conf_path): + '''Get result from undercloud file''' + + result = {} + errors = [] + + try: + config = ConfigParser.SafeConfigParser() + config.read(undercloud_conf_path) + except ConfigParser.MissingSectionHeaderError: + return (['File contains no section headers.'], {}) + except ConfigParser.ParsingError: + return (['File contains parsing errors.'], {}) + + sections = ['DEFAULT'] + config.sections() + result = dict(((section, dict(config.items(section))) + for section in sections)) + return (errors, result) + + def main(): module = AnsibleModule(argument_spec=dict( undercloud_conf_path=dict(required=True, type='str'), @@ -27,24 +69,25 @@ def main(): )) undercloud_conf_path = module.params.get('undercloud_conf_path') + ignore_missing = module.params.get('ignore_missing') - if path.exists(undercloud_conf_path) and path.isfile(undercloud_conf_path): - config = ConfigParser.SafeConfigParser() - config.read(undercloud_conf_path) + # Check arguments + file_exists, errors = check_arguments(undercloud_conf_path, ignore_missing) - sections = ['DEFAULT'] + config.sections() - result = dict(((section, dict(config.items(section))) - for section in sections)) + if errors: + module.fail_json(msg='\n'.join(errors)) + elif file_exists: + # Get result + errors, result = get_result(undercloud_conf_path) - module.exit_json(changed=False, - ansible_facts={u'undercloud_conf': result}) - elif module.params.get('ignore_missing'): + if errors: + module.fail_json(msg='\n'.join(errors)) + else: + module.exit_json(changed=False, + ansible_facts={u'undercloud_conf': result}) + else: module.exit_json(changed=False, ansible_facts={u'undercloud_conf': {'DEFAULT': {}}}) - else: - module.fail_json(msg="Could not open the undercloud.conf file at '%s'" - % undercloud_conf_path) - if __name__ == '__main__': main()