diff --git a/common/config_helper.py b/common/config_helper.py index a27e5ccd..e34fb63c 100644 --- a/common/config_helper.py +++ b/common/config_helper.py @@ -104,8 +104,8 @@ def build_configuration(self): global_ssh_port = self.input_with_default("oceanbase host ssh_port", "22") global_home_path = self.input_with_default("oceanbase install home_path", const.OB_INSTALL_DIR_DEFAULT) default_data_dir = os.path.join(global_home_path, "store") - global_data_dir = self.input_with_default("oceanbase data_dir", default_data_dir) - global_redo_dir = self.input_with_default("oceanbase redo_dir", default_data_dir) + global_data_dir = default_data_dir + global_redo_dir = default_data_dir tenant_sys_config = {"user": self.sys_tenant_user, "password": self.sys_tenant_password} global_config = {"ssh_username": global_ssh_username, "ssh_password": global_ssh_password, "ssh_port": global_ssh_port, "ssh_key_file": "", "home_path": global_home_path, "data_dir": global_data_dir, "redo_dir": global_redo_dir} new_config = {"obcluster": {"ob_cluster_name": ob_cluster_name, "db_host": self.db_host, "db_port": self.db_port, "tenant_sys": tenant_sys_config, "servers": {"nodes": nodes_config, "global": global_config}}} diff --git a/common/tool.py b/common/tool.py index 0ab14007..797d19d0 100644 --- a/common/tool.py +++ b/common/tool.py @@ -214,6 +214,93 @@ def passwd_format(passwd): return "'{}'".format(passwd.replace("'", "'\"'\"'")) +class ConfigOptionsParserUtil(object): + def __init__(self): + self.config_dict = {} + self.key_mapping = { + 'db_host': 'obcluster.db_host', + 'db_port': 'obcluster.db_port', + 'tenant_sys.user': 'obcluster.tenant_sys.user', + 'tenant_sys.password': 'obcluster.tenant_sys.password', + 'ssh_username': 'obcluster.servers.global.ssh_username', + 'ssh_password': 'obcluster.servers.global.ssh_password', + 'ssh_port': 'obcluster.servers.global.ssh_port', + 'home_path': 'obcluster.servers.global.home_path', + 'obproxy_home_path': 'obproxy.servers.global.home_path', + } + + def set_nested_value(self, d, keys, value): + """Recursively set the value in a nested dictionary.""" + if len(keys) > 1: + if 'nodes' in keys[0]: + try: + # Handle nodes + parts = keys[0].split('[') + base_key = parts[0] + index = int(parts[1].rstrip(']')) + if base_key not in d: + d[base_key] = [] + while len(d[base_key]) <= index: + d[base_key].append({}) + self.set_nested_value(d[base_key][index], keys[1:], value) + except (IndexError, ValueError) as e: + raise ValueError(f"Invalid node index in key '{keys[0]}'") from e + else: + if keys[0] not in d: + d[keys[0]] = {} + d[keys[0]] = self.set_nested_value(d[keys[0]], keys[1:], value) + else: + d[keys[0]] = value + return d + + def parse_config(self, input_array): + for item in input_array: + try: + key, value = item.split('=', 1) + # Map short keys to full keys if needed + if key in self.key_mapping: + key = self.key_mapping[key] + keys = key.split('.') + self.set_nested_value(self.config_dict, keys, value) + except ValueError: + raise ValueError(f"Invalid input format for item '{item}'") + + self.config_dict = self.add_default_values(self.config_dict) + return self.config_dict + + def add_default_values(self, d): + if isinstance(d, dict): + for k, v in d.items(): + if k == 'login': + if 'password' not in v: + v['password'] = '' + elif k == 'tenant_sys': + if 'password' not in v: + v['password'] = '' + elif k == 'global': + if 'ssh_username' not in v: + v['ssh_username'] = '' + if 'ssh_password' not in v: + v['ssh_password'] = '' + elif k == 'servers': + # Ensure 'nodes' is present and initialized as an empty list + if 'nodes' not in v: + v['nodes'] = [] + if 'global' not in v: + v['global'] = {} + self.add_default_values(v['global']) + for node in v['nodes']: + if isinstance(node, dict): + self.add_default_values(node) + elif isinstance(v, dict): + self.add_default_values(v) + elif isinstance(v, list): + for node in v: + if isinstance(node, dict): + self.add_default_values(node) + return d + + class DirectoryUtil(object): @staticmethod diff --git a/config.py b/config.py index bd32cbbe..6cbc1a92 100644 --- a/config.py +++ b/config.py @@ -17,7 +17,7 @@ from __future__ import absolute_import, division, print_function import os -from common.tool import DirectoryUtil +from common.tool import ConfigOptionsParserUtil, DirectoryUtil from stdio import SafeStdio import oyaml as yaml import pathlib @@ -148,17 +148,20 @@ def load_config_with_defaults(self, defaults_dict): class ConfigManager(Manager): - def __init__(self, config_file=None, stdio=None): + def __init__(self, config_file=None, stdio=None, config_env_list=[]): default_config_path = os.path.join(os.path.expanduser("~"), ".obdiag", "config.yml") - - if config_file is None or not os.path.exists(config_file): - config_file = default_config_path - pathlib.Path(os.path.dirname(default_config_path)).mkdir(parents=True, exist_ok=True) - with open(default_config_path, 'w') as f: - f.write(DEFAULT_CONFIG_DATA) - super(ConfigManager, self).__init__(config_file, stdio) - self.config_file = config_file - self.config_data = self.load_config() + if config_env_list is None or len(config_env_list) == 0: + if config_file is None or not os.path.exists(config_file): + config_file = default_config_path + pathlib.Path(os.path.dirname(default_config_path)).mkdir(parents=True, exist_ok=True) + with open(default_config_path, 'w') as f: + f.write(DEFAULT_CONFIG_DATA) + super(ConfigManager, self).__init__(config_file, stdio) + self.config_file = config_file + self.config_data = self.load_config() + else: + parser = ConfigOptionsParserUtil() + self.config_data = parser.parse_config(config_env_list) def _safe_get(self, dictionary, *keys, default=None): """Safe way to retrieve nested values from dictionaries""" diff --git a/core.py b/core.py index 95cb1206..675a7720 100644 --- a/core.py +++ b/core.py @@ -57,13 +57,12 @@ from colorama import Fore, Style from common.config_helper import ConfigHelper -from common.tool import Util from common.tool import TimeUtils class ObdiagHome(object): - def __init__(self, stdio=None, config_path=os.path.expanduser('~/.obdiag/config.yml'), inner_config_change_map=None): + def __init__(self, stdio=None, config_path=os.path.expanduser('~/.obdiag/config.yml'), inner_config_change_map=None, custom_config_env_list=None): self._optimize_manager = None self.stdio = None self._stdio_func = None @@ -80,13 +79,7 @@ def __init__(self, stdio=None, config_path=os.path.expanduser('~/.obdiag/config. if self.inner_config_manager.config.get("obdiag") is not None and self.inner_config_manager.config.get("obdiag").get("logger") is not None and self.inner_config_manager.config.get("obdiag").get("logger").get("silent") is not None: stdio.set_silent(self.inner_config_manager.config.get("obdiag").get("logger").get("silent")) self.set_stdio(stdio) - if config_path: - if os.path.exists(os.path.abspath(config_path)): - config_path = config_path - else: - stdio.error('The option you provided with -c: {0} is not exist.'.format(config_path)) - return - self.config_manager = ConfigManager(config_path, stdio) + self.config_manager = ConfigManager(config_path, stdio, custom_config_env_list) if ( self.inner_config_manager.config.get("obdiag") is not None and self.inner_config_manager.config.get("obdiag").get("basic") is not None diff --git a/diag_cmd.py b/diag_cmd.py index 95d5dc14..79c0b0cc 100644 --- a/diag_cmd.py +++ b/diag_cmd.py @@ -261,14 +261,8 @@ def do_command(self): ROOT_IO.verbose('cmd: %s' % self.prev_cmd) ROOT_IO.verbose('opts: %s' % self.opts) config_path = os.path.expanduser('~/.obdiag/config.yml') - custom_config = Util.get_option(self.opts, 'c') - if custom_config: - if os.path.exists(os.path.abspath(custom_config)): - config_path = custom_config - else: - ROOT_IO.error('The option you provided with -c: {0} is a non-existent configuration file path.'.format(custom_config)) - return - obdiag = ObdiagHome(stdio=ROOT_IO, config_path=custom_config, inner_config_change_map=self.inner_config_change_map) + custom_config_env_list = Util.get_option(self.opts, 'config') + obdiag = ObdiagHome(stdio=ROOT_IO, config_path=config_path, inner_config_change_map=self.inner_config_change_map, custom_config_env_list=custom_config_env_list) obdiag.set_options(self.opts) obdiag.set_cmds(self.cmds) ret = self._do_command(obdiag) @@ -417,6 +411,7 @@ def __init__(self): self.parser.add_option('--encrypt', type='string', help="Whether the returned results need to be encrypted, choices=[true, false]", default="false") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherAllCommand, self).init(cmd, args) @@ -439,6 +434,7 @@ def __init__(self): self.parser.add_option('--encrypt', type='string', help="Whether the returned results need to be encrypted, choices=[true, false]", default="false") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherLogCommand, self).init(cmd, args) @@ -455,6 +451,7 @@ def __init__(self): super(ObdiagGatherParameterCommand, self).__init__('parameter', 'Gather oceanbase parameters from oceanbase database') self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherParameterCommand, self).init(cmd, args) @@ -471,6 +468,7 @@ def __init__(self): super(ObdiagGatherVariableCommand, self).__init__('variable', 'Gather oceanbase variables from oceanbase database') self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherVariableCommand, self).init(cmd, args) @@ -487,6 +485,7 @@ def __init__(self): super(ObdiagGatherSysStatCommand, self).__init__('sysstat', 'Gather Host information') self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherSysStatCommand, self).init(cmd, args) @@ -504,6 +503,7 @@ def __init__(self): self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherStackCommand, self).init(cmd, args) @@ -522,6 +522,7 @@ def __init__(self): self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('--scope', type='string', help="perf type constrains, choices=[sample, flame, pstack, all]", default='all') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherPerfCommand, self).init(cmd, args) @@ -542,6 +543,7 @@ def __init__(self): self.parser.add_option('--encrypt', type='string', help="Whether the returned results need to be encrypted, choices=[true, false]", default="false") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherSlogCommand, self).init(cmd, args) @@ -562,6 +564,7 @@ def __init__(self): self.parser.add_option('--encrypt', type='string', help="Whether the returned results need to be encrypted, choices=[true, false]", default="false") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherClogCommand, self).init(cmd, args) @@ -583,6 +586,7 @@ def __init__(self): self.parser.add_option('--since', type='string', help="Specify time range that from 'n' [d]ays, 'n' [h]ours or 'n' [m]inutes. before to now. format: . example: 1h.", default='30m') self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherAwrCommand, self).init(cmd, args) @@ -601,6 +605,7 @@ def __init__(self): self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('--env', type='string', help='''env, eg: "{db_connect='-h127.0.0.1 -P2881 -utest@test -p****** -Dtest'}"''') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherPlanMonitorCommand, self).init(cmd, args) @@ -623,6 +628,7 @@ def __init__(self): self.parser.add_option('--encrypt', type='string', help="Whether the returned results need to be encrypted, choices=[true, false]", default="false") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherObproxyLogCommand, self).init(cmd, args) @@ -657,6 +663,7 @@ def __init__(self): self.parser.add_option('--env', type='string', help='env, eg: "{env1=xxx, env2=xxx}"') self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherSceneRunCommand, self).init(cmd, args) @@ -678,8 +685,8 @@ def __init__(self): self.parser.add_option('--from', type='string', help="specify the start of the time range. format: 'yyyy-mm-dd hh:mm:ss'") self.parser.add_option('--to', type='string', help="specify the end of the time range. format: 'yyyy-mm-dd hh:mm:ss'") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') - self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherAshReportCommand, self).init(cmd, args) @@ -699,6 +706,7 @@ def __init__(self): self.parser.add_option('--password', type='string', help="The password for the database user. If not specified, an attempt will be made to connect without a password.", default='') self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./obdiag_gather_report') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagGatherTableDumpHandler, self).init(cmd, args) @@ -719,8 +727,9 @@ def __init__(self): self.parser.add_option('--log_level', type='string', help="oceanbase logs greater than or equal to this level will be analyze, choices=[DEBUG, TRACE, INFO, WDIAG, WARN, EDIAG, ERROR]") self.parser.add_option('--files', action="append", type='string', help="specify files") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') - self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) self.parser.add_option('--since', type='string', help="Specify time range that from 'n' [d]ays, 'n' [h]ours or 'n' [m]inutes. before to now. format: . example: 1h.", default='30m') + self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagAnalyzeLogCommand, self).init(cmd, args) @@ -746,6 +755,7 @@ def __init__(self): self.parser.add_option('--output', type='string', help="Print the result to the maximum output line on the screen", default=60) self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagAnalyzeFltTraceCommand, self).init(cmd, args) @@ -762,6 +772,7 @@ def __init__(self): self.parser.add_option('--file', type='string', help="specify initialization parameter file") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagAnalyzeParameterDiffCommand, self).init(cmd, args) @@ -778,6 +789,7 @@ def __init__(self): self.parser.add_option('--file', type='string', help="specify initialization parameter file") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagAnalyzeParameterDefaultCommand, self).init(cmd, args) @@ -801,6 +813,7 @@ def __init__(self): self.parser.add_option('--file', type='string', help="specify initialization parameter file") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagAnalyzeVariableDiffCommand, self).init(cmd, args) @@ -835,6 +848,7 @@ def __init__(self): self.parser.add_option('--store_dir', type='string', help='the dir to store result, current dir by default.', default='./obdiag_analyze/') self.parser.add_option('--elapsed_time', type='string', help='The minimum threshold for filtering execution time, measured in microseconds.', default=100000) self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagAnalyzeSQLCommand, self).init(cmd, args) @@ -858,6 +872,7 @@ def __init__(self): self.parser.add_option('--output', type='string', help="The format of the output results, choices=[json, html]", default='html') self.parser.add_option('--store_dir', type='string', help='the dir to store result, current dir by default.', default='./obdiag_analyze/') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagAnalyzeSQLReviewCommand, self).init(cmd, args) @@ -877,6 +892,7 @@ def __init__(self): self.parser.add_option('--store_dir', type='string', help='the dir to store check result, current dir by default.', default='./check_report/') self.parser.add_option('--report_type', type='string', help='The type of the check report, support "table", "json", "xml", "yaml". default table', default='table') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') def init(self, cmd, args): super(ObdiagCheckCommand, self).init(cmd, args) @@ -897,6 +913,7 @@ def __init__(self): self.parser.add_option('--store_dir', type='string', help='the dir to store rca result, current dir by default.', default='./rca/') self.parser.add_option('--input_parameters', action='callback', type='string', callback=self._input_parameters_scene, help='input parameters of scene') self.parser.add_option('-c', type='string', help='obdiag custom config', default=os.path.expanduser('~/.obdiag/config.yml')) + self.parser.add_option('--config', action="append", type="string", help='config options Format: --config key=value') self.scene_input_param_map = {} def _input_parameters_scene(self, option, opt_str, value, parser): diff --git a/test/common/test_config_parse.py b/test/common/test_config_parse.py new file mode 100644 index 00000000..2b47900e --- /dev/null +++ b/test/common/test_config_parse.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -* +# Copyright (c) 2022 OceanBase +# OceanBase Diagnostic Tool is licensed under Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# http://license.coscl.org.cn/MulanPSL2 +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. + +""" +@time: 2024/8/6 +@file: test_config_parse.py +@desc: +""" + +import unittest +from common.tool import ConfigOptionsParserUtil + + +class TestConfigParser(unittest.TestCase): + + def setUp(self): + self.parser = ConfigOptionsParserUtil() + + def test_valid_input_case1(self): + input_array = [ + 'ocp.login.url=http://xx.xx.xx.xx:xx', + 'ocp.login.user=admin', + 'obcluster.ob_cluster_name=test', + 'obcluster.db_host=192.168.1.1', + 'obcluster.db_port=2881', + 'obcluster.tenant_sys.user=root@sys', + 'obcluster.servers.nodes[0].ip=192.168.1.1', + 'obcluster.servers.nodes[1].ip=192.168.1.2', + 'obcluster.servers.nodes[2].ip=192.168.1.3', + 'obcluster.servers.global.ssh_username=test', + 'obcluster.servers.global.ssh_password=test', + 'obcluster.servers.global.home_path=/root/observer', + 'obproxy.obproxy_cluster_name=obproxy', + 'obproxy.servers.nodes[0].ip=192.168.1.4', + 'obproxy.servers.nodes[1].ip=192.168.1.5', + 'obproxy.servers.nodes[2].ip=192.168.1.6', + 'obproxy.servers.global.ssh_username=test', + 'obproxy.servers.global.ssh_password=test', + 'obproxy.servers.global.home_path=/root/obproxy', + ] + + expected_output = { + 'ocp': {'login': {'url': 'http://xx.xx.xx.xx:xx', 'user': 'admin', 'password': ''}}, + 'obcluster': { + 'ob_cluster_name': 'test', + 'db_host': '192.168.1.1', + 'db_port': '2881', + 'tenant_sys': {'user': 'root@sys', 'password': ''}, + 'servers': {'global': {'ssh_username': 'test', 'ssh_password': 'test', 'home_path': '/root/observer'}, 'nodes': [{'ip': '192.168.1.1'}, {'ip': '192.168.1.2'}, {'ip': '192.168.1.3'}]}, + }, + 'obproxy': {'obproxy_cluster_name': 'obproxy', 'servers': {'global': {'ssh_username': 'test', 'ssh_password': 'test', 'home_path': '/root/obproxy'}, 'nodes': [{'ip': '192.168.1.4'}, {'ip': '192.168.1.5'}, {'ip': '192.168.1.6'}]}}, + } + + parsed_config = self.parser.parse_config(input_array) + self.assertEqual(parsed_config, expected_output) + + def test_valid_input_case2(self): + input_array = [ + 'ocp.login.url=http://xx.xx.xx.xx:xx', + 'ocp.login.user=admin', + 'obcluster.ob_cluster_name=test', + 'obcluster.db_host=192.168.1.1', + 'obcluster.db_port=2881', + 'obcluster.tenant_sys.user=root@sys', + 'obcluster.servers.nodes[0].ip=192.168.1.1', + 'obcluster.servers.nodes[0].ssh_username=test2', + 'obcluster.servers.nodes[0].ssh_password=test2', + 'obcluster.servers.nodes[0].home_path=/root/test/observer', + 'obcluster.servers.nodes[1].ip=192.168.1.2', + 'obcluster.servers.nodes[2].ip=192.168.1.3', + 'obcluster.servers.global.ssh_username=test', + 'obcluster.servers.global.ssh_password=test', + 'obcluster.servers.global.home_path=/root/observer', + 'obproxy.obproxy_cluster_name=obproxy', + 'obproxy.servers.nodes[0].ip=192.168.1.4', + 'obproxy.servers.nodes[1].ip=192.168.1.5', + 'obproxy.servers.nodes[2].ip=192.168.1.6', + 'obproxy.servers.global.ssh_username=test', + 'obproxy.servers.global.ssh_password=test', + 'obproxy.servers.global.home_path=/root/obproxy', + ] + + expected_output = { + 'ocp': {'login': {'url': 'http://xx.xx.xx.xx:xx', 'user': 'admin', 'password': ''}}, + 'obcluster': { + 'ob_cluster_name': 'test', + 'db_host': '192.168.1.1', + 'db_port': '2881', + 'tenant_sys': {'user': 'root@sys', 'password': ''}, + 'servers': { + 'global': {'ssh_username': 'test', 'ssh_password': 'test', 'home_path': '/root/observer'}, + 'nodes': [{'home_path': '/root/test/observer', 'ip': '192.168.1.1', 'ssh_username': 'test2', 'ssh_password': 'test2'}, {'ip': '192.168.1.2'}, {'ip': '192.168.1.3'}], + }, + }, + 'obproxy': {'obproxy_cluster_name': 'obproxy', 'servers': {'global': {'ssh_username': 'test', 'ssh_password': 'test', 'home_path': '/root/obproxy'}, 'nodes': [{'ip': '192.168.1.4'}, {'ip': '192.168.1.5'}, {'ip': '192.168.1.6'}]}}, + } + + parsed_config = self.parser.parse_config(input_array) + self.assertEqual(parsed_config, expected_output) + + def test_invalid_format(self): + input_array = ['ocp.login.url=http://xx.xx.xx.xx:xx', 'invalid_format_string'] + with self.assertRaises(ValueError): + self.parser.parse_config(input_array) + + def test_invalid_node_index(self): + input_array = ['ocp.login.url=http://xx.xx.xx.xx:xx', 'obcluster.servers.nodes[not_a_number].ip=192.168.1.1'] + with self.assertRaises(ValueError): + self.parser.parse_config(input_array) + + +if __name__ == '__main__': + unittest.main()