From 363c85f9a9170ff447417cb3890a54d08ab5b34e Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Tue, 27 Jun 2023 14:41:19 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=20=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=A4=9A=E4=B8=AA=E6=BA=AF=E6=BA=90=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=87=BA=E7=8E=B0=E9=94=99=E8=AF=AF=201.?= =?UTF-8?q?=E9=A6=96=E4=B8=AA=E9=85=8D=E7=BD=AE=E5=86=85=E5=AE=B9=E9=94=99?= =?UTF-8?q?=E8=AF=AF=EF=BC=8C=E4=B8=8D=E4=BC=9A=E5=8C=BA=E5=88=86=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E5=92=8C=E6=88=90=E5=8A=9F=E7=9A=84=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=EF=BC=8C=E7=9B=B4=E6=8E=A5400=E6=8A=A5?= =?UTF-8?q?=E9=94=99=202.=E4=B8=AD=E9=97=B4=E4=BD=8D=E7=BD=AE=E7=9A=84?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=86=85=E5=AE=B9=E9=94=99=E8=AF=AF=EF=BC=8C?= =?UTF-8?q?=E8=AF=A5=E9=85=8D=E7=BD=AE=E5=89=8D=E7=9A=84=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=88=90=E5=8A=9F=EF=BC=8C=E4=B9=8B=E5=90=8E=E7=9A=84=E5=88=99?= =?UTF-8?q?=E4=B8=8D=E4=BC=9A=E8=BF=9B=E8=A1=8C=E6=B7=BB=E5=8A=A0=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E4=B8=94=E5=87=BA=E7=8E=B0400=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit code修改: 在进行手动输入和主机导入的循环中,在解析完content后,如果出现空content或不是json格式的内容后,将其文件路径直接放入failed_conf中 期望结果: 区分添加成功的溯源配置和添加失败的溯源配置,且返回相应的错误码给前端,200为全部成功,206为部分成功,400为全部失败 --- .../controllers/management_controller.py | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/gala-ragdoll/ragdoll/controllers/management_controller.py b/gala-ragdoll/ragdoll/controllers/management_controller.py index d886db50..2eac6320 100644 --- a/gala-ragdoll/ragdoll/controllers/management_controller.py +++ b/gala-ragdoll/ragdoll/controllers/management_controller.py @@ -96,17 +96,15 @@ def add_management_confs_in_domain(body=None): # noqa: E501 return base_rsp, codeNum content_string = object_parse.parse_conf_to_json(d_conf.file_path, d_conf.contents) if not content_string or not json.loads(content_string): - codeNum = 400 - base_rsp = BaseResponse(codeNum, "Input configuration content verification failed, " + - "please check the config.") - return base_rsp, codeNum - # create the file and expected value in domain - feature_path = yang_module.get_feature_by_real_path(domain, d_conf.file_path) - result = conf_tools.wirteFileInPath(feature_path, content_string + '\n') - if result: - successConf.append(d_conf.file_path) - else: failedConf.append(d_conf.file_path) + else: + # create the file and expected value in domain + feature_path = yang_module.get_feature_by_real_path(domain, d_conf.file_path) + result = conf_tools.wirteFileInPath(feature_path, content_string + '\n') + if result: + successConf.append(d_conf.file_path) + else: + failedConf.append(d_conf.file_path) # content is empty if len(contents_list_null) > 0: @@ -164,16 +162,14 @@ def add_management_confs_in_domain(body=None): # noqa: E501 content_string = object_parse.parse_conf_to_json(file_path, content) # create the file and expected value in domain if not content_string or not json.loads(content_string): - codeNum = 400 - base_rsp = BaseResponse(codeNum, "Input configuration content verification failed," + - "please check the config in the host.") - return base_rsp, codeNum - feature_path = yang_module.get_feature_by_real_path(domain, file_path) - result = conf_tools.wirteFileInPath(feature_path, content_string) - if result: - successConf.append(file_path) - else: failedConf.append(file_path) + else: + feature_path = yang_module.get_feature_by_real_path(domain, file_path) + result = conf_tools.wirteFileInPath(feature_path, content_string + '\n') + if result: + successConf.append(file_path) + else: + failedConf.append(file_path) # git commit message if len(successConf) > 0: -- Gitee From 60b0fe7cfe6df91cc4d08ef4de23dc7a7e1eaced Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Tue, 4 Jul 2023 09:48:50 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 --- .../ragdoll/config_model/hosts_config.py | 107 ++++++++++++++++++ .../ragdoll/test/test_hosts_config.py | 75 ++++++++++++ gala-ragdoll/ragdoll/utils/object_parse.py | 47 +++++--- .../yang_modules/openEuler-hosts.yang | 69 +++++++++++ 4 files changed, 280 insertions(+), 18 deletions(-) create mode 100644 gala-ragdoll/ragdoll/config_model/hosts_config.py create mode 100644 gala-ragdoll/ragdoll/test/test_hosts_config.py create mode 100644 gala-ragdoll/yang_modules/openEuler-hosts.yang diff --git a/gala-ragdoll/ragdoll/config_model/hosts_config.py b/gala-ragdoll/ragdoll/config_model/hosts_config.py new file mode 100644 index 00000000..3e7da68b --- /dev/null +++ b/gala-ragdoll/ragdoll/config_model/hosts_config.py @@ -0,0 +1,107 @@ +# Author: Lay +# Description: default +# Date: 2023/6/8 13:44 +import re +import json + +NOT_SYNCHRONIZE = "NOT SYNCHRONIZE" +SYNCHRONIZED = "SYNCHRONIZED" + +ipv4 = re.compile('^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$') + +ipv6 = re.compile('^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|' + '(([0-9A-Fa-f]{1,4}:){1,7}:)|' + '(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|' + '(([0-9A-Fa-f]{1,4}:){5}(:[0-9A-Fa-f]{1,4}){1,2})|' + '(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){1,3})|' + '(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){1,4})|' + '(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){1,5})|' + '([0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){1,6})|' + '(:(:[0-9A-Fa-f]{1,4}){1,7})|' + '(([0-9A-Fa-f]{1,4}:){6}(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' + '(([0-9A-Fa-f]{1,4}:){5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' + '(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){0,1}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|' + '2[0-4]\\d|25[0-5])){3})|' + '(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}:(\\d|[1-9]\\d|1\\d{2}|' + '2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' + '(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}:(\\d|[1-9]\\d|1\\d{2}|' + '2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' + '([0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){0,4}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' + '(:(:[0-9A-Fa-f]{1,4}){0,5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}))$') + + +class HostsConfig: + + def __int__(self): + pass + + def _parse_network_conf_to_dict(self, conf_info): + + res = dict() + error_conf = False + + conf_info_list = conf_info.split("\n") + for line in conf_info_list: + if line.strip() == '' or line.strip()[0] in '#': + continue + ip_domain = re.split("\s+", line) + if len(ip_domain) == 1: + error_conf = True + break + ip = ip_domain[0] + if ipv4.match(ip) or ipv6.match(ip): + list_value = ip_domain[1:] + str_value = " ".join(list_value) + res[ip] = str_value + else: + error_conf = True + break + + return error_conf, res + + def parse_res_to_json(self, conf_info): + conf_json = "" + + error_conf, dict_res = self._parse_network_conf_to_dict(conf_info.strip()) + if not error_conf: + conf_json = json.dumps(dict_res, indent=4, ensure_ascii=False) + + return conf_json + + def conf_compare(self, dst_conf, src_conf): + """ + desc: 比较dst_conf和src_conf是否相同,dst_conf和src_conf均为序列化后的配置信息。 + return:dst_conf和src_conf相同返回SYNCHRONIZED + dst_conf和src_conf不同返回NOT_SYNCHRONIZE + """ + res = SYNCHRONIZED + dst_conf_dict = json.loads(dst_conf) + src_conf_dict = json.loads(src_conf) + + dst_conf_keys = dst_conf_dict.keys() + src_conf_keys = src_conf_dict.keys() + + for src_key in src_conf_keys: + if src_key not in dst_conf_keys: + res = NOT_SYNCHRONIZE + break + if str(dst_conf_dict[src_key]) != str(src_conf_dict[src_key]): + res = NOT_SYNCHRONIZE + break + return res + + def read_conf(self, conf_json): + """ + desc: 将json格式的配置文件内容结构化。 + """ + conf_dict = json.loads(conf_json) + return conf_dict + + def write_conf(self, conf_dict): + content = "" + conf_dict_keys = conf_dict.keys() + for key in conf_dict_keys: + if conf_dict[key] is not None: + conf_item = " ".join((key, str(conf_dict[key]))).replace('\n', '\n\t') + content = content + conf_item + "\n" + return content diff --git a/gala-ragdoll/ragdoll/test/test_hosts_config.py b/gala-ragdoll/ragdoll/test/test_hosts_config.py new file mode 100644 index 00000000..49c01927 --- /dev/null +++ b/gala-ragdoll/ragdoll/test/test_hosts_config.py @@ -0,0 +1,75 @@ +# Author: Lay +# Description: default +# Date: 2023/6/16 17:42 +import importlib +import json + +config_text = " # Loopback entries; do not change.\n" \ + " # For historical reasons, localhost precedes localhost.localdomain:\n" \ + "127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4\n" \ + "::1 localhost localhost.localdomain localhost6 localhost6.localdomain6\n" \ + "# See hosts(5) for proper format and other examples:\n" \ + "# 192.168.1.10 foo.mydomain.org foo\n" \ + " # 192.168.1.13 bar.mydomain.org bar " + +dst_conf = '{\n' \ + '"127.0.0.1": "localhost localhost.localdomain localhost4 localhost4.localdomain4",\n' \ + '"::1": "localhost localhost.localdomain localhost6 localhost6.localdomain6"\n' \ + '}' + +BASE_PATH = "ragdoll.config_model." +CONFIG_MODEL_NAME = "Config" +conf_type = "network" +PROJECT_NAME = "_config" + +from ragdoll.test import BaseTestCase + + +class TestNetworkConfig(BaseTestCase): + + def import_network_config_model(self): + conf_model = "" + project_name = conf_type + PROJECT_NAME + project_path = BASE_PATH + project_name + model_name = conf_type.capitalize() + CONFIG_MODEL_NAME + + try: + project = importlib.import_module(project_path) + except ImportError: + conf_model = "" + else: + _conf_model_class = getattr(project, model_name, None) + if _conf_model_class: + conf_model = _conf_model_class() + return conf_model + + def test_parse_res_to_json(self): + conf_model = self.import_network_config_model() + conf_json_str = conf_model.parse_res_to_json(config_text) + print("conf_json_str : {}".format(conf_json_str)) + + conf_json = json.loads(conf_json_str) + self.assertEqual(len(conf_json.keys()), 2) + + def test_conf_compare(self): + conf_model = self.import_network_config_model() + src_conf = conf_model.parse_res_to_json(config_text) + res = conf_model.conf_compare(dst_conf, src_conf) + print("res : {}".format(res)) + self.assertTrue(res == "SYNCHRONIZED") + + def test_write_conf(self): + conf_model = self.import_network_config_model() + conf_dict = conf_model.read_conf(dst_conf) + print("read_conf : {}".format(conf_dict)) + content = conf_model.write_conf(conf_dict) + print("content : {}".format(content)) + conf_content = "127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4\n" \ + "::1 localhost localhost.localdomain localhost6 localhost6.localdomain6\n" + self.assertTrue(content == conf_content) + + +if __name__ == '__main__': + import unittest + + unittest.main() diff --git a/gala-ragdoll/ragdoll/utils/object_parse.py b/gala-ragdoll/ragdoll/utils/object_parse.py index 3ee1c6e3..c7a59623 100644 --- a/gala-ragdoll/ragdoll/utils/object_parse.py +++ b/gala-ragdoll/ragdoll/utils/object_parse.py @@ -1,10 +1,8 @@ -import os import json import importlib from ragdoll.utils.yang_module import YangModule - BASE_PATH = "ragdoll.config_model." CONFIG_MODEL_NAME = "Config" PROJECT_NAME = "_config" @@ -18,26 +16,26 @@ class ObjectParse(object): """ desc: Create a structured model corresponding to the configuration type. - example: + example: param: conf_type: ini return: IniConfig() """ conf_model = "" - project_name = conf_type + PROJECT_NAME # example: ini_config - project_path = BASE_PATH + project_name # example: ragdoll.config_model.ini_config - model_name = conf_type.capitalize() + CONFIG_MODEL_NAME # example: IniConfig + project_name = conf_type + PROJECT_NAME # example: ini_config + project_path = BASE_PATH + project_name # example: ragdoll.config_model.ini_config + model_name = conf_type.capitalize() + CONFIG_MODEL_NAME # example: IniConfig try: project = importlib.import_module(project_path) except ImportError: conf_model = "" else: - _conf_model_class = getattr(project, model_name, None) # example: IniConfig + _conf_model_class = getattr(project, model_name, None) # example: IniConfig if _conf_model_class: - conf_model = _conf_model_class() # example: IniConfig() + conf_model = _conf_model_class() # example: IniConfig() return conf_model - + def get_conf_type_by_conf_path(self, conf_path): yang_model = self._yang_modules.getModuleByFilePath(conf_path) if not yang_model: @@ -45,7 +43,7 @@ class ObjectParse(object): _conf_type = self._yang_modules.getTypeInModdule([yang_model]) conf_type = _conf_type[yang_model.name()] return conf_type - + def parse_model_to_json(self, d_model): """ desc: convert object to json. @@ -53,11 +51,9 @@ class ObjectParse(object): conf_json = "" conf_dict = d_model.conf - conf_json = json.dumps(conf_dict, indent = 4, ensure_ascii= False) - - return conf_json - + conf_json = json.dumps(conf_dict, indent=4, ensure_ascii=False) + return conf_json def parse_conf_to_json(self, conf_path, conf_info): """ @@ -70,7 +66,12 @@ class ObjectParse(object): # create conf model conf_model = self.create_conf_model_by_type(conf_type) - # load yang model info + # parse config file whose key-type is ip address + if conf_type == "hosts": + conf_json = conf_model.parse_res_to_json(conf_info) + return conf_json + + # load yang model info yang_info = self._yang_modules.getModuleByFilePath(conf_path) conf_model.load_yang_model(yang_info) @@ -91,11 +92,21 @@ class ObjectParse(object): # create conf model conf_model = self.create_conf_model_by_type(conf_type) + if conf_type == "hosts": + conf_dict = conf_model.read_conf(json_list) + conf_info = conf_model.write_conf(conf_dict) + return conf_info + + # load yang model info + yang_info = self._yang_modules.getModuleByFilePath(conf_path) + spacer_info = self._yang_modules.getSpacerInModdule([yang_info]) # load conf info(json) to model conf_model.read_json(json_list) - - # to content - conf_info = conf_model.write_conf() + if conf_type == "ssh": + conf_info = conf_model.write_conf(spacer_info) + else: + # to content + conf_info = conf_model.write_conf() return conf_info diff --git a/gala-ragdoll/yang_modules/openEuler-hosts.yang b/gala-ragdoll/yang_modules/openEuler-hosts.yang new file mode 100644 index 00000000..73bd82ef --- /dev/null +++ b/gala-ragdoll/yang_modules/openEuler-hosts.yang @@ -0,0 +1,69 @@ +/****************************************************** +* Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. +* Module description & tree structure +******************************************************/ +module openEuler-hosts { + namespace "urn:huawei:yang:openEuler-hosts"; + prefix "ip"; + + organization + "Huawei Technologies Co., Ltd."; + + contact + "Huawei Industrial Base + Bantian, Longgang + Shenzhen 518129 + People's Republic of China + Website: http://www.huawei.com + Email: support@huawei.com"; + + description + "This module contains a collection of YANG definitions for + hosts. + The real path is : /etc/hosts"; + + revision 2021-05-13 { + description "Initial revision."; + reference ""; + } + + // extension statements + // feature statements + // identity statements + // typedef statements + // grouping statements + // data definition statements + // augment statements + // rpc statements + // notification statements + + extension path{ + argument "filePath"; + description "The real path corresponding to the repo file."; + } + + extension type{ + argument "type"; + description "The type of this configuration file."; + } + + extension spacer{ + argument "spacer"; + description "Spacer between configuration item and configuration value."; + } + + container hostconf { + + description "the repo file in yum modules."; + + container hosts { + + description "The file name is host"; + + ip:path "openEuler:/etc/hosts"; + ip:type "hosts"; + ip:spacer ""; + } + } + +} \ No newline at end of file -- Gitee From 4724c1ba4a6811efa3f2b3e57ee545a55e616357 Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Tue, 4 Jul 2023 13:57:58 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 --- .../ragdoll/config_model/hosts_config.py | 37 +++++++++++-------- .../ragdoll/test/test_hosts_config.py | 12 +++--- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/gala-ragdoll/ragdoll/config_model/hosts_config.py b/gala-ragdoll/ragdoll/config_model/hosts_config.py index 3e7da68b..00642b18 100644 --- a/gala-ragdoll/ragdoll/config_model/hosts_config.py +++ b/gala-ragdoll/ragdoll/config_model/hosts_config.py @@ -1,6 +1,3 @@ -# Author: Lay -# Description: default -# Date: 2023/6/8 13:44 import re import json @@ -18,16 +15,20 @@ ipv6 = re.compile('^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|' '(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){1,5})|' '([0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){1,6})|' '(:(:[0-9A-Fa-f]{1,4}){1,7})|' - '(([0-9A-Fa-f]{1,4}:){6}(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' - '(([0-9A-Fa-f]{1,4}:){5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' - '(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){0,1}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|' - '2[0-4]\\d|25[0-5])){3})|' + '(([0-9A-Fa-f]{1,4}:){6}(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])' + '(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' + '(([0-9A-Fa-f]{1,4}:){5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])' + '(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' + '(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4})' + '{0,1}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' '(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}:(\\d|[1-9]\\d|1\\d{2}|' '2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' '(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}:(\\d|[1-9]\\d|1\\d{2}|' '2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' - '([0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){0,4}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' - '(:(:[0-9A-Fa-f]{1,4}){0,5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}))$') + '([0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){0,4}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])' + '(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|' + '(:(:[0-9A-Fa-f]{1,4}){0,5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])' + '(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}))$') class HostsConfig: @@ -35,7 +36,8 @@ class HostsConfig: def __int__(self): pass - def _parse_network_conf_to_dict(self, conf_info): + @staticmethod + def _parse_network_conf_to_dict(conf_info): res = dict() error_conf = False @@ -68,12 +70,13 @@ class HostsConfig: return conf_json - def conf_compare(self, dst_conf, src_conf): + @staticmethod + def conf_compare(dst_conf, src_conf): """ - desc: 比较dst_conf和src_conf是否相同,dst_conf和src_conf均为序列化后的配置信息。 - return:dst_conf和src_conf相同返回SYNCHRONIZED + desc: 比较dst_conf和src_conf是否相同,dst_conf和src_conf均为序列化后的配置信息。 + return:dst_conf和src_conf相同返回SYNCHRONIZED dst_conf和src_conf不同返回NOT_SYNCHRONIZE - """ + """ res = SYNCHRONIZED dst_conf_dict = json.loads(dst_conf) src_conf_dict = json.loads(src_conf) @@ -90,14 +93,16 @@ class HostsConfig: break return res - def read_conf(self, conf_json): + @staticmethod + def read_conf(conf_json): """ desc: 将json格式的配置文件内容结构化。 """ conf_dict = json.loads(conf_json) return conf_dict - def write_conf(self, conf_dict): + @staticmethod + def write_conf(conf_dict): content = "" conf_dict_keys = conf_dict.keys() for key in conf_dict_keys: diff --git a/gala-ragdoll/ragdoll/test/test_hosts_config.py b/gala-ragdoll/ragdoll/test/test_hosts_config.py index 49c01927..dd41eea4 100644 --- a/gala-ragdoll/ragdoll/test/test_hosts_config.py +++ b/gala-ragdoll/ragdoll/test/test_hosts_config.py @@ -4,7 +4,7 @@ import importlib import json -config_text = " # Loopback entries; do not change.\n" \ +CONFIG_TEXT = " # Loopback entries; do not change.\n" \ " # For historical reasons, localhost precedes localhost.localdomain:\n" \ "127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4\n" \ "::1 localhost localhost.localdomain localhost6 localhost6.localdomain6\n" \ @@ -12,7 +12,7 @@ config_text = " # Loopback entries; do not change.\n" \ "# 192.168.1.10 foo.mydomain.org foo\n" \ " # 192.168.1.13 bar.mydomain.org bar " -dst_conf = '{\n' \ +DST_CONF = '{\n' \ '"127.0.0.1": "localhost localhost.localdomain localhost4 localhost4.localdomain4",\n' \ '"::1": "localhost localhost.localdomain localhost6 localhost6.localdomain6"\n' \ '}' @@ -45,7 +45,7 @@ class TestNetworkConfig(BaseTestCase): def test_parse_res_to_json(self): conf_model = self.import_network_config_model() - conf_json_str = conf_model.parse_res_to_json(config_text) + conf_json_str = conf_model.parse_res_to_json(CONFIG_TEXT) print("conf_json_str : {}".format(conf_json_str)) conf_json = json.loads(conf_json_str) @@ -53,14 +53,14 @@ class TestNetworkConfig(BaseTestCase): def test_conf_compare(self): conf_model = self.import_network_config_model() - src_conf = conf_model.parse_res_to_json(config_text) - res = conf_model.conf_compare(dst_conf, src_conf) + src_conf = conf_model.parse_res_to_json(CONFIG_TEXT) + res = conf_model.conf_compare(DST_CONF, src_conf) print("res : {}".format(res)) self.assertTrue(res == "SYNCHRONIZED") def test_write_conf(self): conf_model = self.import_network_config_model() - conf_dict = conf_model.read_conf(dst_conf) + conf_dict = conf_model.read_conf(DST_CONF) print("read_conf : {}".format(conf_dict)) content = conf_model.write_conf(conf_dict) print("content : {}".format(content)) -- Gitee From f40ed795457c5ad9e762882d9b3662ce604e28ba Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Tue, 4 Jul 2023 14:26:31 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 --- gala-ragdoll/ragdoll/test/test_hosts_config.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/gala-ragdoll/ragdoll/test/test_hosts_config.py b/gala-ragdoll/ragdoll/test/test_hosts_config.py index dd41eea4..b1cbe17f 100644 --- a/gala-ragdoll/ragdoll/test/test_hosts_config.py +++ b/gala-ragdoll/ragdoll/test/test_hosts_config.py @@ -19,7 +19,7 @@ DST_CONF = '{\n' \ BASE_PATH = "ragdoll.config_model." CONFIG_MODEL_NAME = "Config" -conf_type = "network" +CONF_TYPE = "network" PROJECT_NAME = "_config" from ragdoll.test import BaseTestCase @@ -27,11 +27,12 @@ from ragdoll.test import BaseTestCase class TestNetworkConfig(BaseTestCase): - def import_network_config_model(self): + @staticmethod + def import_network_config_model(): conf_model = "" - project_name = conf_type + PROJECT_NAME + project_name = CONF_TYPE + PROJECT_NAME project_path = BASE_PATH + project_name - model_name = conf_type.capitalize() + CONFIG_MODEL_NAME + model_name = CONF_TYPE.capitalize() + CONFIG_MODEL_NAME try: project = importlib.import_module(project_path) @@ -46,7 +47,6 @@ class TestNetworkConfig(BaseTestCase): def test_parse_res_to_json(self): conf_model = self.import_network_config_model() conf_json_str = conf_model.parse_res_to_json(CONFIG_TEXT) - print("conf_json_str : {}".format(conf_json_str)) conf_json = json.loads(conf_json_str) self.assertEqual(len(conf_json.keys()), 2) @@ -55,15 +55,12 @@ class TestNetworkConfig(BaseTestCase): conf_model = self.import_network_config_model() src_conf = conf_model.parse_res_to_json(CONFIG_TEXT) res = conf_model.conf_compare(DST_CONF, src_conf) - print("res : {}".format(res)) self.assertTrue(res == "SYNCHRONIZED") def test_write_conf(self): conf_model = self.import_network_config_model() conf_dict = conf_model.read_conf(DST_CONF) - print("read_conf : {}".format(conf_dict)) content = conf_model.write_conf(conf_dict) - print("content : {}".format(content)) conf_content = "127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4\n" \ "::1 localhost localhost.localdomain localhost6 localhost6.localdomain6\n" self.assertTrue(content == conf_content) -- Gitee From 314079055e55f83c2796a61cbdfd95e9a80e7962 Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Fri, 14 Jul 2023 16:09:45 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 --- .../ragdoll/config_model/hosts_config.py | 16 ++++++++++++++++ gala-ragdoll/ragdoll/test/test_hosts_config.py | 5 ++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/gala-ragdoll/ragdoll/config_model/hosts_config.py b/gala-ragdoll/ragdoll/config_model/hosts_config.py index 00642b18..a06285fa 100644 --- a/gala-ragdoll/ragdoll/config_model/hosts_config.py +++ b/gala-ragdoll/ragdoll/config_model/hosts_config.py @@ -1,3 +1,19 @@ +# ****************************************************************************** +# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. +# licensed under the 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. +# ******************************************************************************/ +""" +Date: 2023-07-14 +Author: genglei +Description: hosts config handler +""" import re import json diff --git a/gala-ragdoll/ragdoll/test/test_hosts_config.py b/gala-ragdoll/ragdoll/test/test_hosts_config.py index b1cbe17f..68d748ea 100644 --- a/gala-ragdoll/ragdoll/test/test_hosts_config.py +++ b/gala-ragdoll/ragdoll/test/test_hosts_config.py @@ -1,6 +1,5 @@ -# Author: Lay -# Description: default -# Date: 2023/6/16 17:42 +# coding: utf-8 + import importlib import json -- Gitee From 74f5b73bcba04a947f03be960b4673323b729476 Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Fri, 14 Jul 2023 16:46:06 +0800 Subject: [PATCH 06/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 --- .../ragdoll/config_model/hosts_config.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/gala-ragdoll/ragdoll/config_model/hosts_config.py b/gala-ragdoll/ragdoll/config_model/hosts_config.py index a06285fa..00642b18 100644 --- a/gala-ragdoll/ragdoll/config_model/hosts_config.py +++ b/gala-ragdoll/ragdoll/config_model/hosts_config.py @@ -1,19 +1,3 @@ -# ****************************************************************************** -# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. -# licensed under the 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. -# ******************************************************************************/ -""" -Date: 2023-07-14 -Author: genglei -Description: hosts config handler -""" import re import json -- Gitee From 618ace7c661e1476444e63ce3554b77598eee4a0 Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Wed, 19 Jul 2023 10:00:31 +0800 Subject: [PATCH 07/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 --- .../ragdoll/config_model/hosts_config.py | 51 ++++++++++--------- gala-ragdoll/ragdoll/utils/object_parse.py | 9 ---- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/gala-ragdoll/ragdoll/config_model/hosts_config.py b/gala-ragdoll/ragdoll/config_model/hosts_config.py index 00642b18..87e79262 100644 --- a/gala-ragdoll/ragdoll/config_model/hosts_config.py +++ b/gala-ragdoll/ragdoll/config_model/hosts_config.py @@ -1,6 +1,8 @@ import re import json +from ragdoll.utils.yang_module import YangModule + NOT_SYNCHRONIZE = "NOT SYNCHRONIZE" SYNCHRONIZED = "SYNCHRONIZED" @@ -33,11 +35,11 @@ ipv6 = re.compile('^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|' class HostsConfig: - def __int__(self): - pass + def __init__(self): + self.conf = dict() + self.yang = dict() - @staticmethod - def _parse_network_conf_to_dict(conf_info): + def _parse_network_conf_to_dict(self, conf_info): res = dict() error_conf = False @@ -61,22 +63,26 @@ class HostsConfig: return error_conf, res - def parse_res_to_json(self, conf_info): - conf_json = "" + def load_yang_model(self, yang_info): + yang_module = YangModule() + xpath = yang_module.getXpathInModule(yang_info) # get all xpath in yang_info + + for d_xpath in xpath: + real_path = d_xpath.split('/') + option = real_path[2] + self.yang[option] = '' + def read_conf(self, conf_info): error_conf, dict_res = self._parse_network_conf_to_dict(conf_info.strip()) if not error_conf: - conf_json = json.dumps(dict_res, indent=4, ensure_ascii=False) + self.conf = dict_res - return conf_json - - @staticmethod - def conf_compare(dst_conf, src_conf): + def conf_compare(self, dst_conf, src_conf): """ - desc: 比较dst_conf和src_conf是否相同,dst_conf和src_conf均为序列化后的配置信息。 - return:dst_conf和src_conf相同返回SYNCHRONIZED + desc: 比较dst_conf和src_conf是否相同,dst_conf和src_conf均为序列化后的配置信息。 + return:dst_conf和src_conf相同返回SYNCHRONIZED dst_conf和src_conf不同返回NOT_SYNCHRONIZE - """ + """ res = SYNCHRONIZED dst_conf_dict = json.loads(dst_conf) src_conf_dict = json.loads(src_conf) @@ -93,20 +99,17 @@ class HostsConfig: break return res - @staticmethod - def read_conf(conf_json): + def read_json(self, conf_json): """ - desc: 将json格式的配置文件内容结构化。 + desc: 将json格式的配置文件内容结构化成Class conf成员。 """ conf_dict = json.loads(conf_json) - return conf_dict + self.conf = conf_dict - @staticmethod - def write_conf(conf_dict): + def write_conf(self): content = "" - conf_dict_keys = conf_dict.keys() - for key in conf_dict_keys: - if conf_dict[key] is not None: - conf_item = " ".join((key, str(conf_dict[key]))).replace('\n', '\n\t') + for key, value in self.conf: + if value is not None: + conf_item = " ".join((key, str(value))).replace('\n', '\n\t') content = content + conf_item + "\n" return content diff --git a/gala-ragdoll/ragdoll/utils/object_parse.py b/gala-ragdoll/ragdoll/utils/object_parse.py index c7a59623..fde2be8c 100644 --- a/gala-ragdoll/ragdoll/utils/object_parse.py +++ b/gala-ragdoll/ragdoll/utils/object_parse.py @@ -66,11 +66,6 @@ class ObjectParse(object): # create conf model conf_model = self.create_conf_model_by_type(conf_type) - # parse config file whose key-type is ip address - if conf_type == "hosts": - conf_json = conf_model.parse_res_to_json(conf_info) - return conf_json - # load yang model info yang_info = self._yang_modules.getModuleByFilePath(conf_path) conf_model.load_yang_model(yang_info) @@ -92,10 +87,6 @@ class ObjectParse(object): # create conf model conf_model = self.create_conf_model_by_type(conf_type) - if conf_type == "hosts": - conf_dict = conf_model.read_conf(json_list) - conf_info = conf_model.write_conf(conf_dict) - return conf_info # load yang model info yang_info = self._yang_modules.getModuleByFilePath(conf_path) -- Gitee From 7b3f89e9a3388261ce18234c62a0c2e80367ab32 Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Wed, 19 Jul 2023 10:25:00 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 --- gala-ragdoll/ragdoll/config_model/hosts_config.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/gala-ragdoll/ragdoll/config_model/hosts_config.py b/gala-ragdoll/ragdoll/config_model/hosts_config.py index 87e79262..f0db8933 100644 --- a/gala-ragdoll/ragdoll/config_model/hosts_config.py +++ b/gala-ragdoll/ragdoll/config_model/hosts_config.py @@ -39,7 +39,8 @@ class HostsConfig: self.conf = dict() self.yang = dict() - def _parse_network_conf_to_dict(self, conf_info): + @staticmethod + def _parse_network_conf_to_dict(conf_info): res = dict() error_conf = False @@ -77,12 +78,8 @@ class HostsConfig: if not error_conf: self.conf = dict_res - def conf_compare(self, dst_conf, src_conf): - """ - desc: 比较dst_conf和src_conf是否相同,dst_conf和src_conf均为序列化后的配置信息。 - return:dst_conf和src_conf相同返回SYNCHRONIZED - dst_conf和src_conf不同返回NOT_SYNCHRONIZE - """ + @staticmethod + def conf_compare(dst_conf, src_conf): res = SYNCHRONIZED dst_conf_dict = json.loads(dst_conf) src_conf_dict = json.loads(src_conf) -- Gitee From 163976d98902ef8fca739243baa6dccbabb94f3b Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Wed, 26 Jul 2023 10:52:06 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 --- .../ragdoll/config_model/hosts_config.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/gala-ragdoll/ragdoll/config_model/hosts_config.py b/gala-ragdoll/ragdoll/config_model/hosts_config.py index f0db8933..217da64d 100644 --- a/gala-ragdoll/ragdoll/config_model/hosts_config.py +++ b/gala-ragdoll/ragdoll/config_model/hosts_config.py @@ -1,3 +1,19 @@ +# ****************************************************************************** +# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. +# licensed under the 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: 2023-07-19 11:20:00 +Author: GengLei +Description: /etc/hosts config handler +""" import re import json -- Gitee From 73d1fdd8fd0de9c8eef1b288a5d8d7cbc83f77d9 Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Thu, 27 Jul 2023 10:20:25 +0800 Subject: [PATCH 10/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 --- gala-ragdoll/ragdoll/config_model/hosts_config.py | 2 ++ gala-ragdoll/ragdoll/test/test_hosts_config.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/gala-ragdoll/ragdoll/config_model/hosts_config.py b/gala-ragdoll/ragdoll/config_model/hosts_config.py index 217da64d..39e23b59 100644 --- a/gala-ragdoll/ragdoll/config_model/hosts_config.py +++ b/gala-ragdoll/ragdoll/config_model/hosts_config.py @@ -68,6 +68,7 @@ class HostsConfig: ip_domain = re.split("\s+", line) if len(ip_domain) == 1: error_conf = True + print("ip_domain contains incorrect formatting") break ip = ip_domain[0] if ipv4.match(ip) or ipv6.match(ip): @@ -76,6 +77,7 @@ class HostsConfig: res[ip] = str_value else: error_conf = True + print("ip does not meet the ipv4 or ipv6 format") break return error_conf, res diff --git a/gala-ragdoll/ragdoll/test/test_hosts_config.py b/gala-ragdoll/ragdoll/test/test_hosts_config.py index 68d748ea..251b1757 100644 --- a/gala-ragdoll/ragdoll/test/test_hosts_config.py +++ b/gala-ragdoll/ragdoll/test/test_hosts_config.py @@ -3,6 +3,8 @@ import importlib import json +from ragdoll.test import BaseTestCase + CONFIG_TEXT = " # Loopback entries; do not change.\n" \ " # For historical reasons, localhost precedes localhost.localdomain:\n" \ "127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4\n" \ @@ -18,11 +20,9 @@ DST_CONF = '{\n' \ BASE_PATH = "ragdoll.config_model." CONFIG_MODEL_NAME = "Config" -CONF_TYPE = "network" +CONF_TYPE = "hosts" PROJECT_NAME = "_config" -from ragdoll.test import BaseTestCase - class TestNetworkConfig(BaseTestCase): -- Gitee From c9370f293ad1499f41b116d007aa7c4c65e5cf9c Mon Sep 17 00:00:00 2001 From: leigengisoftstone Date: Thu, 27 Jul 2023 14:15:18 +0800 Subject: [PATCH 11/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5/etc/hosts=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于/etc/hosts这种类型的配置文件的存在,后端会根据该文件名走他相应的解析流程,解析流程包括匹配用户入参的k是否为ipv4和ipv6地址,空格后是否包含相应的v的值,最终将配置转化为json格式存入git仓库 新增gala-ragdoll日志记录 --- gala-ragdoll/config/gala-ragdoll.conf | 6 + .../ragdoll/config_model/hosts_config.py | 5 +- gala-ragdoll/ragdoll/log/__init__.py | 3 + gala-ragdoll/ragdoll/log/log.py | 132 ++++++++++++++++++ gala-ragdoll/ragdoll/utils/conf_tools.py | 34 ++++- 5 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 gala-ragdoll/ragdoll/log/__init__.py create mode 100644 gala-ragdoll/ragdoll/log/log.py diff --git a/gala-ragdoll/config/gala-ragdoll.conf b/gala-ragdoll/config/gala-ragdoll.conf index 1de2620c..e95243df 100644 --- a/gala-ragdoll/config/gala-ragdoll.conf +++ b/gala-ragdoll/config/gala-ragdoll.conf @@ -16,3 +16,9 @@ sync_port = 11114 [ragdoll] port = 11114 + +[log] +log_level = INFO +log_dir = /var/log/aops +max_bytes = 31457280 +backup_count = 40 diff --git a/gala-ragdoll/ragdoll/config_model/hosts_config.py b/gala-ragdoll/ragdoll/config_model/hosts_config.py index 39e23b59..7e4453f7 100644 --- a/gala-ragdoll/ragdoll/config_model/hosts_config.py +++ b/gala-ragdoll/ragdoll/config_model/hosts_config.py @@ -17,6 +17,7 @@ Description: /etc/hosts config handler import re import json +from ragdoll.log.log import LOGGER from ragdoll.utils.yang_module import YangModule NOT_SYNCHRONIZE = "NOT SYNCHRONIZE" @@ -68,7 +69,7 @@ class HostsConfig: ip_domain = re.split("\s+", line) if len(ip_domain) == 1: error_conf = True - print("ip_domain contains incorrect formatting") + LOGGER.info("ip_domain contains incorrect formatting") break ip = ip_domain[0] if ipv4.match(ip) or ipv6.match(ip): @@ -77,7 +78,7 @@ class HostsConfig: res[ip] = str_value else: error_conf = True - print("ip does not meet the ipv4 or ipv6 format") + LOGGER.info("ip does not meet the ipv4 or ipv6 format") break return error_conf, res diff --git a/gala-ragdoll/ragdoll/log/__init__.py b/gala-ragdoll/ragdoll/log/__init__.py new file mode 100644 index 00000000..01886c6b --- /dev/null +++ b/gala-ragdoll/ragdoll/log/__init__.py @@ -0,0 +1,3 @@ +# Author: Lay +# Description: default +# Date: 2023/7/27 10:45 diff --git a/gala-ragdoll/ragdoll/log/log.py b/gala-ragdoll/ragdoll/log/log.py new file mode 100644 index 00000000..d050dc27 --- /dev/null +++ b/gala-ragdoll/ragdoll/log/log.py @@ -0,0 +1,132 @@ +#!/usr/bin/python3 +# ****************************************************************************** +# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. +# licensed under the 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: +Author: +Description: log module. +""" +import os +import stat +import logging +from concurrent_log_handler import ConcurrentRotatingFileHandler + +from ragdoll.utils.conf_tools import ConfTools + + +class Logger: + """ + Logger class. + """ + + def __init__(self): + """ + Class instance initialization. + """ + log_conf = ConfTools.load_log_conf() + log_dir = log_conf.get("log_dir") + if not os.path.exists(log_dir): + os.makedirs(log_dir, mode=0o644) + + self.__log_name = os.path.join(log_dir, "ragdoll.log") + self.__log_level = log_conf.get("log_level") + self.__max_bytes = log_conf.get("max_bytes") + self.__backup_count = log_conf.get("backup_count") + self.__log_format = logging.Formatter( + "%(asctime)s %(levelname)s %(module)s/%(funcName)s/%(lineno)s: %(message)s" + ) + + self.check() + + def check(self): + """ + check whether parameter is valid. + """ + self.__check_integer(self.__max_bytes, "max bytes") + self.__check_integer(self.__backup_count, "backup count") + + @staticmethod + def __check_integer(arg, comment): + """ + check parameter of which type is int. + + Args: + arg(int): parameter need to be checked. + comment(str): decription. + + Raises: + ValueError + """ + if not isinstance(arg, int) or arg <= 0: + raise ValueError("Invalid arg: %s: %r" % (comment, arg)) + + def __create_logger(self): + """ + create logger and set log level. + + Returns: + logger + """ + logger = logging.getLogger(__name__) + logger.setLevel(self.__log_level) + return logger + + def __console_logger(self): + """ + create stream handler for logging to console. + + Returns: + StreamHandler + """ + # output log to the console + console_handler = logging.StreamHandler() + console_handler.setFormatter(self.__log_format) + # console_handler.setLevel(level='DEBUG') + + return console_handler + + def __file_rotate_logger(self): + """ + create rotated file handler for logging to file using size rotating. + + Returns: + ConcurrentRotatingFileHandler + """ + # log logrotate + rotate_handler = ConcurrentRotatingFileHandler( + filename=self.__log_name, + mode="a", + maxBytes=self.__max_bytes, + backupCount=self.__backup_count, + encoding="utf-8", + chmod=(stat.S_IRUSR | stat.S_IWUSR), + ) + rotate_handler.setFormatter(self.__log_format) + + return rotate_handler + + def get_logger(self): + """ + get logger with handler. + + Returns: + logger object + """ + logger = self.__create_logger() + + logger.addHandler(self.__console_logger()) + logger.addHandler(self.__file_rotate_logger()) + + return logger + + +LOGGER = Logger().get_logger() diff --git a/gala-ragdoll/ragdoll/utils/conf_tools.py b/gala-ragdoll/ragdoll/utils/conf_tools.py index 1669b5b7..c9503c85 100644 --- a/gala-ragdoll/ragdoll/utils/conf_tools.py +++ b/gala-ragdoll/ragdoll/utils/conf_tools.py @@ -45,19 +45,19 @@ WPERM = 2 XPERM = 1 SPERM = 0 - NOTFOUND = "NOT FOUND" NOTSYNCHRONIZE = "NOT SYNCHRONIZE" SYNCHRONIZED = "SYNCHRONIZED" CONFIG = "/etc/ragdoll/gala-ragdoll.conf" + class SyncRes(Enum): SUCCESS = "SUCCESS" FAILED = "FAILED" -class ConfTools(object): +class ConfTools(object): """ desc: convert the configuration items controlled in the domain into dict storage """ @@ -204,9 +204,9 @@ class ConfTools(object): d_real_conf = RealConf(path=x_path, real_value=d_path_value) real_conf.append(d_real_conf) - real_conf_path = RealConfPath(domain_name = domainName, - host_id = hostId, - real_conf = real_conf) + real_conf_path = RealConfPath(domain_name=domainName, + host_id=hostId, + real_conf=real_conf) res.append(real_conf_path) return res @@ -358,11 +358,11 @@ class ConfTools(object): ll_res = gitTools.run_shell_return_output(cmd).decode() print("ll_res is : {}".format(ll_res)) ll_res_list = ll_res.split(SPACE) - + fileType = ll_res_list[0] permssions = "0" for perm in range(0, PERMISSION): - items = fileType[1 + perm * PERMISSION : (perm + 1) * PERMISSION + 1] + items = fileType[1 + perm * PERMISSION: (perm + 1) * PERMISSION + 1] value = 0 for d_item in items: d_item_value = self.switch_perm(d_item) @@ -638,3 +638,23 @@ class ConfTools(object): cf.read(conf_path, encoding="utf-8") port = cf.get("ragdoll", "port") return port + + @staticmethod + def load_log_conf(): + """ + desc: get the log configuration + """ + cf = configparser.ConfigParser() + if os.path.exists(CONFIG): + cf.read(CONFIG, encoding="utf-8") + else: + parent = os.path.dirname(os.path.realpath(__file__)) + conf_path = os.path.join(parent, "../../config/gala-ragdoll.conf") + cf.read(conf_path, encoding="utf-8") + log_level = cf.get("log", "log_level") + log_dir = cf.get("log", "log_dir") + max_bytes = cf.get("log", "max_bytes") + backup_count = cf.get("log", "backup_count") + log_conf = {"log_level": log_level, "log_dir": log_dir, "max_bytes": int(max_bytes), + "backup_count": int(backup_count)} + return log_conf -- Gitee