8171 lines
322 KiB
Diff
8171 lines
322 KiB
Diff
|
|
diff --git a/config/gala-ragdoll.conf b/config/gala-ragdoll.conf
|
|||
|
|
index 57ace32..8cb693a 100644
|
|||
|
|
--- a/config/gala-ragdoll.conf
|
|||
|
|
+++ b/config/gala-ragdoll.conf
|
|||
|
|
@@ -5,21 +5,57 @@ user_email = "user_email"
|
|||
|
|
|
|||
|
|
[collect]
|
|||
|
|
collect_address = "http://0.0.0.0"
|
|||
|
|
-collect_api = "/demo/collectConf"
|
|||
|
|
-collect_port = 11114
|
|||
|
|
+collect_api = "/manage/config/collect"
|
|||
|
|
+collect_port = 11111
|
|||
|
|
|
|||
|
|
[sync]
|
|||
|
|
sync_address = "http://0.0.0.0"
|
|||
|
|
-sync_api = "/demo/syncConf"
|
|||
|
|
-sync_port = 11114
|
|||
|
|
+sync_api = "/manage/config/sync"
|
|||
|
|
+batch_sync_address = "http://0.0.0.0"
|
|||
|
|
+batch_sync_api = "/manage/config/batch/sync"
|
|||
|
|
+sync_port = 11111
|
|||
|
|
|
|||
|
|
[objectFile]
|
|||
|
|
object_file_address = "http://0.0.0.0"
|
|||
|
|
object_file_api = "/manage/config/objectfile"
|
|||
|
|
object_file_port = 11111
|
|||
|
|
|
|||
|
|
+[sync_status]
|
|||
|
|
+host_sync_status_address = "http://0.0.0.0"
|
|||
|
|
+add_host_sync_status_api = "/manage/host/sync/status/add"
|
|||
|
|
+delete_host_sync_status_api = "/manage/host/sync/status/delete"
|
|||
|
|
+delete_all_host_sync_status_api = "/manage/all/host/sync/status/delete"
|
|||
|
|
+host_sync_status_port = 11111
|
|||
|
|
+
|
|||
|
|
+[conf_trace]
|
|||
|
|
+conf_trace_mgmt_address = "http://0.0.0.0"
|
|||
|
|
+conf_trace_mgmt_api = "/conftrace/mgmt"
|
|||
|
|
+conf_trace_delete_api = "/conftrace/delete"
|
|||
|
|
+conf_trace_port = 11111
|
|||
|
|
+
|
|||
|
|
[ragdoll]
|
|||
|
|
-port = 11114
|
|||
|
|
+ip=127.0.0.1
|
|||
|
|
+port=11114
|
|||
|
|
+
|
|||
|
|
+[mysql]
|
|||
|
|
+ip=127.0.0.1
|
|||
|
|
+port=3306
|
|||
|
|
+database_name=aops
|
|||
|
|
+engine_format=mysql+pymysql://@%s:%s/%s
|
|||
|
|
+pool_size=100
|
|||
|
|
+pool_recycle=7200
|
|||
|
|
+
|
|||
|
|
+[redis]
|
|||
|
|
+ip=127.0.0.1
|
|||
|
|
+port=6379
|
|||
|
|
+
|
|||
|
|
+[uwsgi]
|
|||
|
|
+wsgi-file=manage.py
|
|||
|
|
+daemonize=/var/log/aops/uwsgi/ragdoll.log
|
|||
|
|
+http-timeout=600
|
|||
|
|
+harakiri=600
|
|||
|
|
+processes=2
|
|||
|
|
+gevent=100
|
|||
|
|
|
|||
|
|
[log]
|
|||
|
|
log_level = INFO
|
|||
|
|
diff --git a/gala-ragdoll.spec b/gala-ragdoll.spec
|
|||
|
|
index bdde9ce..33bbcdb 100644
|
|||
|
|
--- a/gala-ragdoll.spec
|
|||
|
|
+++ b/gala-ragdoll.spec
|
|||
|
|
@@ -45,6 +45,9 @@ mkdir %{buildroot}/%{python3_sitelib}/ragdoll/config
|
|||
|
|
install config/*.conf %{buildroot}/%{python3_sitelib}/ragdoll/config
|
|||
|
|
mkdir -p %{buildroot}/%{_prefix}/lib/systemd/system
|
|||
|
|
install service/gala-ragdoll.service %{buildroot}/%{_prefix}/lib/systemd/system
|
|||
|
|
+install service/ragdoll %{buildroot}/%{_prefix}/bin/
|
|||
|
|
+install service/ragdoll-filetrace %{buildroot}/%{_prefix}/bin/
|
|||
|
|
+install service/ragdoll-filetrace.service %{buildroot}/%{_prefix}/lib/systemd/system
|
|||
|
|
|
|||
|
|
|
|||
|
|
%pre
|
|||
|
|
@@ -67,7 +70,11 @@ fi
|
|||
|
|
%license LICENSE
|
|||
|
|
/%{_sysconfdir}/ragdoll/gala-ragdoll.conf
|
|||
|
|
%{_bindir}/ragdoll
|
|||
|
|
+%{_bindir}/ragdoll-filetrace
|
|||
|
|
%{_prefix}/lib/systemd/system/gala-ragdoll.service
|
|||
|
|
+%{_prefix}/lib/systemd/system/ragdoll-filetrace.service
|
|||
|
|
+%{_prefix}/bin/ragdoll
|
|||
|
|
+%{_prefix}/bin/ragdoll-filetrace
|
|||
|
|
|
|||
|
|
|
|||
|
|
%files -n python3-gala-ragdoll
|
|||
|
|
@@ -77,6 +84,9 @@ fi
|
|||
|
|
|
|||
|
|
|
|||
|
|
%changelog
|
|||
|
|
+* Thu June 27 2024 zhangdaolong<zhangdaolong@isoftstone.com> - v1.4.0-4
|
|||
|
|
+- Added real-time monitoring file function
|
|||
|
|
+
|
|||
|
|
* Mon Apr 17 2023 wenxin<shusheng.wen@outlook.com> - v1.3.0-3
|
|||
|
|
- update the host id validate method for ragdoll
|
|||
|
|
|
|||
|
|
diff --git a/ragdoll/__main__.py b/ragdoll/__main__.py
|
|||
|
|
deleted file mode 100644
|
|||
|
|
index df65acb..0000000
|
|||
|
|
--- a/ragdoll/__main__.py
|
|||
|
|
+++ /dev/null
|
|||
|
|
@@ -1,60 +0,0 @@
|
|||
|
|
-#!/usr/bin/env python3
|
|||
|
|
-
|
|||
|
|
-import connexion
|
|||
|
|
-import configparser
|
|||
|
|
-import os
|
|||
|
|
-import ast
|
|||
|
|
-
|
|||
|
|
-from ragdoll import encoder
|
|||
|
|
-from ragdoll.const.conf_handler_const import CONFIG
|
|||
|
|
-from ragdoll.utils.yang_module import YangModule
|
|||
|
|
-from ragdoll.utils.prepare import Prepare
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def main():
|
|||
|
|
- # prepare to load config
|
|||
|
|
- load_prepare()
|
|||
|
|
- # load yang modules
|
|||
|
|
- load_yang()
|
|||
|
|
- # load port for ragdoll
|
|||
|
|
- ragdoll_port = load_port()
|
|||
|
|
- app = connexion.App(__name__, specification_dir='./swagger/')
|
|||
|
|
- app.app.json_encoder = encoder.JSONEncoder
|
|||
|
|
- app.add_api('swagger.yaml', arguments={'title': 'Configuration traceability'})
|
|||
|
|
- app.run(port=ragdoll_port)
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def load_prepare():
|
|||
|
|
- git_dir, git_user_name, git_user_email = load_conf()
|
|||
|
|
- prepare = Prepare(git_dir)
|
|||
|
|
- prepare.mdkir_git_warehose(git_user_name, git_user_email)
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def load_yang():
|
|||
|
|
- yang_modules = YangModule()
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def load_conf():
|
|||
|
|
- cf = configparser.ConfigParser()
|
|||
|
|
- if os.path.exists(CONFIG):
|
|||
|
|
- cf.read(CONFIG, encoding="utf-8")
|
|||
|
|
- else:
|
|||
|
|
- cf.read("config/gala-ragdoll.conf", encoding="utf-8")
|
|||
|
|
- git_dir = ast.literal_eval(cf.get("git", "git_dir"))
|
|||
|
|
- git_user_name = ast.literal_eval(cf.get("git", "user_name"))
|
|||
|
|
- git_user_email = ast.literal_eval(cf.get("git", "user_email"))
|
|||
|
|
- return git_dir, git_user_name, git_user_email
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def load_port():
|
|||
|
|
- cf = configparser.ConfigParser()
|
|||
|
|
- if os.path.exists(CONFIG):
|
|||
|
|
- cf.read(CONFIG, encoding="utf-8")
|
|||
|
|
- else:
|
|||
|
|
- cf.read("config/gala-ragdoll.conf", encoding="utf-8")
|
|||
|
|
- ragdoll_port = cf.get("ragdoll", "port")
|
|||
|
|
- return ragdoll_port
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-if __name__ == '__main__':
|
|||
|
|
- main()
|
|||
|
|
diff --git a/ragdoll/conf/__init__.py b/ragdoll/conf/__init__.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..0c44a2e
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/conf/__init__.py
|
|||
|
|
@@ -0,0 +1,26 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: __init__.py.py
|
|||
|
|
+@Time: 2024/3/4 9:37
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+from vulcanus.conf import Config
|
|||
|
|
+
|
|||
|
|
+from ragdoll.conf import default_config
|
|||
|
|
+from ragdoll.conf.constant import MANAGER_CONFIG_PATH
|
|||
|
|
+
|
|||
|
|
+# read manager configuration
|
|||
|
|
+configuration = Config(MANAGER_CONFIG_PATH, default_config)
|
|||
|
|
+
|
|||
|
|
diff --git a/ragdoll/conf/constant.py b/ragdoll/conf/constant.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..a14a0f9
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/conf/constant.py
|
|||
|
|
@@ -0,0 +1,53 @@
|
|||
|
|
+#!/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: manager constant
|
|||
|
|
+"""
|
|||
|
|
+import os
|
|||
|
|
+
|
|||
|
|
+from ragdoll.utils.git_tools import GitTools
|
|||
|
|
+
|
|||
|
|
+BASE_CONFIG_PATH = "/etc/ragdoll/"
|
|||
|
|
+
|
|||
|
|
+# path of manager configuration
|
|||
|
|
+MANAGER_CONFIG_PATH = os.path.join(BASE_CONFIG_PATH, 'gala-ragdoll.conf')
|
|||
|
|
+
|
|||
|
|
+TARGETDIR = GitTools().target_dir
|
|||
|
|
+
|
|||
|
|
+# domain
|
|||
|
|
+CREATE_DOMAIN = "/domain/createDomain"
|
|||
|
|
+DELETE_DOMAIN = "/domain/deleteDomain"
|
|||
|
|
+QUERY_DOMAIN = "/domain/queryDomain"
|
|||
|
|
+
|
|||
|
|
+# host
|
|||
|
|
+ADD_HOST_IN_DOMAIN = "/host/addHost"
|
|||
|
|
+DELETE_HOST_IN_DOMAIN = "/host/deleteHost"
|
|||
|
|
+GET_HOST_BY_DOMAIN = "/host/getHost"
|
|||
|
|
+
|
|||
|
|
+# management conf
|
|||
|
|
+ADD_MANAGEMENT_CONFS_IN_DOMAIN = "/management/addManagementConf"
|
|||
|
|
+UPLOAD_MANAGEMENT_CONFS_IN_DOMAIN = "/management/uploadManagementConf"
|
|||
|
|
+DELETE_MANAGEMENT_CONFS_IN_DOMAIN = "/management/deleteManagementConf"
|
|||
|
|
+GET_MANAGEMENT_CONFS_IN_DOMAIN = "/management/getManagementConf"
|
|||
|
|
+QUERY_CHANGELOG_OF_MANAGEMENT_CONFS_IN_DOMAIN = "/management/queryManageConfChange"
|
|||
|
|
+
|
|||
|
|
+# confs
|
|||
|
|
+GET_SYNC_STATUS = "/confs/getDomainStatus"
|
|||
|
|
+QUERY_EXCEPTED_CONFS = "/confs/queryExpectedConfs"
|
|||
|
|
+QUERY_REAL_CONFS = "/confs/queryRealConfs"
|
|||
|
|
+SYNC_CONF_TO_HOST_FROM_DOMAIN = "/confs/syncConf"
|
|||
|
|
+QUERY_SUPPORTED_CONFS = "/confs/querySupportedConfs"
|
|||
|
|
+COMPARE_CONF_DIFF = "/confs/domain/diff"
|
|||
|
|
+BATCH_SYNC_CONF_TO_HOST_FROM_DOMAIN = "/confs/batch/syncConf"
|
|||
|
|
diff --git a/ragdoll/conf/default_config.py b/ragdoll/conf/default_config.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..094111a
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/conf/default_config.py
|
|||
|
|
@@ -0,0 +1,59 @@
|
|||
|
|
+#!/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: default config of manager
|
|||
|
|
+"""
|
|||
|
|
+git = {"GIT_DIR": "/home/confTraceTest", "USER_NAME": "user_name", "USER_EMAIL": "user_email"}
|
|||
|
|
+
|
|||
|
|
+collect = {
|
|||
|
|
+ "COLLECT_ADDRESS": "http://127.0.0.1",
|
|||
|
|
+ "COLLECT_API": "/manage/config/collect",
|
|||
|
|
+ "COLLECT_PORT": 11111
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+sync = {
|
|||
|
|
+ "SYNC_ADDRESS": "http://127.0.0.1",
|
|||
|
|
+ "SYNC_API": "/manage/config/sync",
|
|||
|
|
+ "BATCH_SYNC_ADDRESS": "http://127.0.0.1",
|
|||
|
|
+ "BATCH_SYNC_API": "/manage/config/batch/sync",
|
|||
|
|
+ "SYNC_PORT": 11111
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+objectFile = {"OBJECT_FILE_ADDRESS": "http://127.0.0.1", "OBJECT_FILE_API": "/manage/config/objectfile",
|
|||
|
|
+ "OBJECT_FILE_PORT": 11111}
|
|||
|
|
+
|
|||
|
|
+sync_status = {
|
|||
|
|
+ "HOST_SYNC_STATUS_ADDRESS": "http://127.0.0.1",
|
|||
|
|
+ "ADD_HOST_SYNC_STATUS_API": "/manage/host/sync/status/add",
|
|||
|
|
+ "DELETE_HOST_SYNC_STATUS_API": "/manage/host/sync/status/delete",
|
|||
|
|
+ "HOST_SYNC_STATUS_PORT": 11111
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+ragdoll = {"IP": "127.0.0.1", "PORT": 11114}
|
|||
|
|
+
|
|||
|
|
+mysql = {
|
|||
|
|
+ "IP": "127.0.0.1",
|
|||
|
|
+ "PORT": 3306,
|
|||
|
|
+ "DATABASE_NAME": "aops",
|
|||
|
|
+ "ENGINE_FORMAT": "mysql+pymysql://@%s:%s/%s",
|
|||
|
|
+ "POOL_SIZE": 100,
|
|||
|
|
+ "POOL_RECYCLE": 7200,
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+redis = {"IP": "127.0.0.1", "PORT": 6379}
|
|||
|
|
+
|
|||
|
|
+log = {"LOG_LEVEL": "INFO", "LOG_DIR": "/var/log/aops", "MAX_BYTES": 31457280, "BACKUP_COUNT": 40}
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
diff --git a/ragdoll/config_model/bash_config.py b/ragdoll/config_model/bash_config.py
|
|||
|
|
index 73c1777..36016a9 100644
|
|||
|
|
--- a/ragdoll/config_model/bash_config.py
|
|||
|
|
+++ b/ragdoll/config_model/bash_config.py
|
|||
|
|
@@ -59,8 +59,7 @@ class BashConfig(BaseHandlerConfig):
|
|||
|
|
dst_conf_dict = json.loads(dst_conf)
|
|||
|
|
src_conf_dict = json.loads(src_conf)
|
|||
|
|
for src_conf in src_conf_dict:
|
|||
|
|
- str_src_conf = str(src_conf)
|
|||
|
|
- if str(dst_conf_dict).find(str_src_conf) == -1:
|
|||
|
|
+ if src_conf not in dst_conf_dict:
|
|||
|
|
res = NOT_SYNCHRONIZE
|
|||
|
|
break
|
|||
|
|
return res
|
|||
|
|
diff --git a/ragdoll/config_model/fstab_config.py b/ragdoll/config_model/fstab_config.py
|
|||
|
|
index 416fd62..5f712e8 100644
|
|||
|
|
--- a/ragdoll/config_model/fstab_config.py
|
|||
|
|
+++ b/ragdoll/config_model/fstab_config.py
|
|||
|
|
@@ -13,7 +13,6 @@
|
|||
|
|
import re
|
|||
|
|
from ragdoll.config_model.base_handler_config import BaseHandlerConfig
|
|||
|
|
from ragdoll.const.conf_handler_const import FSTAB_COLUMN_NUM
|
|||
|
|
-from ragdoll.log.log import LOGGER
|
|||
|
|
|
|||
|
|
class FstabConfig(BaseHandlerConfig):
|
|||
|
|
"""
|
|||
|
|
diff --git a/ragdoll/config_model/hostname_config.py b/ragdoll/config_model/hostname_config.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..bca3877
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/config_model/hostname_config.py
|
|||
|
|
@@ -0,0 +1,68 @@
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (C) 2023 isoftstone Technologies Co., Ltd. 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:23:00
|
|||
|
|
+Author: liulei
|
|||
|
|
+Description: text type config analyze
|
|||
|
|
+"""
|
|||
|
|
+import json
|
|||
|
|
+
|
|||
|
|
+from ragdoll.config_model.base_handler_config import BaseHandlerConfig
|
|||
|
|
+from ragdoll.const.conf_handler_const import SYNCHRONIZED, NOT_SYNCHRONIZE
|
|||
|
|
+from ragdoll.log.log import LOGGER
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class HostnameConfig(BaseHandlerConfig):
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def parse_conf_to_dict(conf_info):
|
|||
|
|
+ """
|
|||
|
|
+ 将配置信息conf_info转为list,但是并未校验配置项是否合法
|
|||
|
|
+ """
|
|||
|
|
+ conf_dict_list = list()
|
|||
|
|
+
|
|||
|
|
+ conf_list = conf_info.strip().splitlines()
|
|||
|
|
+ for line in conf_list:
|
|||
|
|
+ if line is None or line.strip() == '' or line.strip()[0] in '#;':
|
|||
|
|
+ continue
|
|||
|
|
+
|
|||
|
|
+ strip_line = str(line.strip()).replace("\t", " ")
|
|||
|
|
+ conf_dict_list.append(strip_line)
|
|||
|
|
+ return conf_dict_list
|
|||
|
|
+
|
|||
|
|
+ def read_conf(self, conf_info):
|
|||
|
|
+ conf_dict_list = self.parse_conf_to_dict(conf_info)
|
|||
|
|
+ if conf_dict_list:
|
|||
|
|
+ self.conf = conf_dict_list
|
|||
|
|
+
|
|||
|
|
+ def write_conf(self):
|
|||
|
|
+ content = ""
|
|||
|
|
+ for value in self.conf:
|
|||
|
|
+ if value is not None:
|
|||
|
|
+ content = content + value + "\n"
|
|||
|
|
+ return content
|
|||
|
|
+
|
|||
|
|
+ def conf_compare(self, src_conf, dst_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)
|
|||
|
|
+ if not dst_conf_dict or not src_conf_dict:
|
|||
|
|
+ res = NOT_SYNCHRONIZE
|
|||
|
|
+ return res
|
|||
|
|
+ if dst_conf_dict[0] != src_conf_dict[0]:
|
|||
|
|
+ res = NOT_SYNCHRONIZE
|
|||
|
|
+
|
|||
|
|
+ return res
|
|||
|
|
diff --git a/ragdoll/config_model/hosts_config.py b/ragdoll/config_model/hosts_config.py
|
|||
|
|
index 50660ec..1bc9452 100644
|
|||
|
|
--- a/ragdoll/config_model/hosts_config.py
|
|||
|
|
+++ b/ragdoll/config_model/hosts_config.py
|
|||
|
|
@@ -19,7 +19,6 @@ import json
|
|||
|
|
|
|||
|
|
from ragdoll.config_model.base_handler_config import BaseHandlerConfig
|
|||
|
|
from ragdoll.log.log import LOGGER
|
|||
|
|
-from ragdoll.utils.yang_module import YangModule
|
|||
|
|
from ragdoll.const.conf_handler_const import NOT_SYNCHRONIZE, 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?)$')
|
|||
|
|
@@ -64,7 +63,7 @@ class HostsConfig(BaseHandlerConfig):
|
|||
|
|
ip_domain = re.split("\s+", line)
|
|||
|
|
if len(ip_domain) == 1:
|
|||
|
|
error_conf = True
|
|||
|
|
- LOGGER.warning("ip_domain contains incorrect formatting")
|
|||
|
|
+ LOGGER.error("Ip_domain contains incorrect formatting")
|
|||
|
|
break
|
|||
|
|
ip = ip_domain[0]
|
|||
|
|
if ipv4.match(ip) or ipv6.match(ip):
|
|||
|
|
@@ -73,7 +72,7 @@ class HostsConfig(BaseHandlerConfig):
|
|||
|
|
res[ip] = str_value
|
|||
|
|
else:
|
|||
|
|
error_conf = True
|
|||
|
|
- LOGGER.warning("ip does not meet the ipv4 or ipv6 format")
|
|||
|
|
+ LOGGER.error("Ip does not meet the ipv4 or ipv6 format")
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
return error_conf, res
|
|||
|
|
@@ -84,7 +83,7 @@ class HostsConfig(BaseHandlerConfig):
|
|||
|
|
self.conf = dict_res
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
- def conf_compare(dst_conf, src_conf):
|
|||
|
|
+ def conf_compare(src_conf, dst_conf):
|
|||
|
|
res = SYNCHRONIZED
|
|||
|
|
dst_conf_dict = json.loads(dst_conf)
|
|||
|
|
src_conf_dict = json.loads(src_conf)
|
|||
|
|
diff --git a/ragdoll/config_model/ini_config.py b/ragdoll/config_model/ini_config.py
|
|||
|
|
index 9f9dc3d..0c0bd50 100644
|
|||
|
|
--- a/ragdoll/config_model/ini_config.py
|
|||
|
|
+++ b/ragdoll/config_model/ini_config.py
|
|||
|
|
@@ -11,7 +11,6 @@
|
|||
|
|
# ******************************************************************************/
|
|||
|
|
|
|||
|
|
import re
|
|||
|
|
-import json
|
|||
|
|
import copy
|
|||
|
|
from collections import OrderedDict as _default_dict
|
|||
|
|
|
|||
|
|
diff --git a/ragdoll/config_model/sshd_config.py b/ragdoll/config_model/sshd_config.py
|
|||
|
|
index e499bb2..35ed872 100644
|
|||
|
|
--- a/ragdoll/config_model/sshd_config.py
|
|||
|
|
+++ b/ragdoll/config_model/sshd_config.py
|
|||
|
|
@@ -83,7 +83,7 @@ class SshdConfig():
|
|||
|
|
self.conf = conf_list
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
- def conf_compare(dst_conf, src_conf):
|
|||
|
|
+ def conf_compare(src_conf, dst_conf):
|
|||
|
|
"""
|
|||
|
|
desc: 比较dst_conf和src_conf是否相同,dst_conf和src_conf均为序列化后的配置信息。
|
|||
|
|
return:dst_conf和src_conf相同返回SYNCHRONIZED
|
|||
|
|
@@ -93,9 +93,9 @@ class SshdConfig():
|
|||
|
|
dst_conf_dict = json.loads(dst_conf)
|
|||
|
|
src_conf_dict = json.loads(src_conf)
|
|||
|
|
|
|||
|
|
- for dst_conf in dst_conf_dict:
|
|||
|
|
- str_dst_conf = str(dst_conf)
|
|||
|
|
- if str(src_conf_dict).find(str_dst_conf) == -1:
|
|||
|
|
+ for src_conf in src_conf_dict:
|
|||
|
|
+ str_src_conf = str(src_conf)
|
|||
|
|
+ if str(dst_conf_dict).find(str_src_conf) == -1:
|
|||
|
|
res = NOT_SYNCHRONIZE
|
|||
|
|
break
|
|||
|
|
return res
|
|||
|
|
diff --git a/ragdoll/config_model/text_config.py b/ragdoll/config_model/text_config.py
|
|||
|
|
index dadd915..dd4165a 100644
|
|||
|
|
--- a/ragdoll/config_model/text_config.py
|
|||
|
|
+++ b/ragdoll/config_model/text_config.py
|
|||
|
|
@@ -44,5 +44,4 @@ class TextConfig(BaseHandlerConfig):
|
|||
|
|
for value in self.conf:
|
|||
|
|
if value is not None:
|
|||
|
|
content = content + value + "\n"
|
|||
|
|
- content = content + '\n'
|
|||
|
|
return content
|
|||
|
|
\ No newline at end of file
|
|||
|
|
diff --git a/ragdoll/confs_manage/__init__.py b/ragdoll/confs_manage/__init__.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..25b0334
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/confs_manage/__init__.py
|
|||
|
|
@@ -0,0 +1,18 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: __init__.py.py
|
|||
|
|
+@Time: 2024/3/11 9:01
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
diff --git a/ragdoll/confs_manage/view.py b/ragdoll/confs_manage/view.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..4280fa7
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/confs_manage/view.py
|
|||
|
|
@@ -0,0 +1,479 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: view.py
|
|||
|
|
+@Time: 2024/3/11 9:01
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+import os
|
|||
|
|
+
|
|||
|
|
+import connexion
|
|||
|
|
+from vulcanus.restful.resp.state import SUCCEED, SERVER_ERROR, PARAM_ERROR, NO_DATA
|
|||
|
|
+from vulcanus.restful.response import BaseResponse
|
|||
|
|
+
|
|||
|
|
+from ragdoll.conf.constant import TARGETDIR
|
|||
|
|
+from ragdoll.const.conf_files import yang_conf_list
|
|||
|
|
+from ragdoll.const.conf_handler_const import NOT_SYNCHRONIZE
|
|||
|
|
+from ragdoll.function.verify.confs import GetSyncStatusSchema, QueryExceptedConfsSchema, QueryRealConfsSchema, \
|
|||
|
|
+ SyncConfToHostFromDomainSchema, QuerySupportedConfsSchema, CompareConfDiffSchema, \
|
|||
|
|
+ BatchSyncConfToHostFromDomainSchema
|
|||
|
|
+from ragdoll.log.log import LOGGER
|
|||
|
|
+from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
+from ragdoll.utils.format import Format
|
|||
|
|
+from ragdoll.utils.host_tools import HostTools
|
|||
|
|
+from ragdoll.utils.yang_module import YangModule
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class GetTheSyncStatusOfDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=GetSyncStatusSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """
|
|||
|
|
+ get the status of the domain
|
|||
|
|
+ get the status of whether the domain has been synchronized # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body:
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: SyncStatus
|
|||
|
|
+ """
|
|||
|
|
+ access_token = connexion.request.headers.get("access_token")
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ ip = params.get("ip")
|
|||
|
|
+ # check domain
|
|||
|
|
+ base_rsp, code_num = Format.check_domain_param(domain)
|
|||
|
|
+ if code_num != 200:
|
|||
|
|
+ return base_rsp, code_num
|
|||
|
|
+
|
|||
|
|
+ # get manage confs in domain
|
|||
|
|
+ code_num, code_string, manage_confs = Format.get_domain_conf(domain)
|
|||
|
|
+ if not manage_confs:
|
|||
|
|
+ return self.response(code=SUCCEED, message=code_string, data=manage_confs)
|
|||
|
|
+
|
|||
|
|
+ # get real conf in host
|
|||
|
|
+ host_id = Format.get_host_id_by_ip(ip, domain)
|
|||
|
|
+ real_conf_res_text = Format.get_realconf_by_domain_and_host(domain, [host_id], access_token)
|
|||
|
|
+ if real_conf_res_text is None:
|
|||
|
|
+ return self.response(code=SERVER_ERROR, message="get real conf failed")
|
|||
|
|
+
|
|||
|
|
+ # compare manage conf with real conf
|
|||
|
|
+ sync_status = Format.diff_mangeconf_with_realconf(domain, real_conf_res_text, manage_confs)
|
|||
|
|
+
|
|||
|
|
+ # deal with not found files
|
|||
|
|
+ man_conf_list = []
|
|||
|
|
+ for d_man_conf in manage_confs:
|
|||
|
|
+ man_conf_list.append(d_man_conf.get("file_path").split(":")[-1])
|
|||
|
|
+ for d_host in sync_status["hostStatus"]:
|
|||
|
|
+ d_sync_status = d_host["syncStatus"]
|
|||
|
|
+ file_list = []
|
|||
|
|
+ for d_file in d_sync_status:
|
|||
|
|
+ file_path = d_file["file_path"]
|
|||
|
|
+ file_list.append(file_path)
|
|||
|
|
+ for d_man_conf in man_conf_list:
|
|||
|
|
+ if d_man_conf in file_list:
|
|||
|
|
+ continue
|
|||
|
|
+ else:
|
|||
|
|
+ comp_res = "NOT FOUND"
|
|||
|
|
+ conf_is_synced = {"file_path": d_man_conf, "isSynced": comp_res}
|
|||
|
|
+ d_sync_status.append(conf_is_synced)
|
|||
|
|
+ return self.response(code=SUCCEED, message="successfully get the sync status of domain", data=sync_status)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class QueryExceptedConfs(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=QueryExceptedConfsSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """
|
|||
|
|
+ query the supported configurations in the current project
|
|||
|
|
+ queryExpectedConfs # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :rtype: List[ExceptedConfInfo]
|
|||
|
|
+ """
|
|||
|
|
+ # 直接从入参中读取domain列表
|
|||
|
|
+ domain_names = params.get("domainNames")
|
|||
|
|
+ if len(domain_names) == 0:
|
|||
|
|
+ code_num = PARAM_ERROR
|
|||
|
|
+ code_string = "The current domain does not exist, please create the domain first."
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ all_domain_expected_files = []
|
|||
|
|
+ yang_modules = YangModule()
|
|||
|
|
+ for d_domain in domain_names:
|
|||
|
|
+ domain_path = os.path.join(TARGETDIR, d_domain["domainName"])
|
|||
|
|
+ expected_conf_lists = {"domainName": d_domain["domainName"], "confBaseInfos": []}
|
|||
|
|
+ # Traverse all files in the source management repository
|
|||
|
|
+ for root, dirs, files in os.walk(domain_path):
|
|||
|
|
+ # Domain also contains host cache files, so we need to add hierarchical judgment for root
|
|||
|
|
+ if len(files) > 0 and len(root.split('/')) > 3:
|
|||
|
|
+ if "hostRecord.txt" in files:
|
|||
|
|
+ continue
|
|||
|
|
+ for d_file in files:
|
|||
|
|
+ feature = os.path.join(root.split('/')[-1], d_file)
|
|||
|
|
+ d_module = yang_modules.getModuleByFeature(feature)
|
|||
|
|
+ file_lists = yang_modules.getFilePathInModdule(yang_modules.module_list)
|
|||
|
|
+ file_path = file_lists.get(d_module.name()).split(":")[-1]
|
|||
|
|
+ d_file_path = os.path.join(root, d_file)
|
|||
|
|
+ expected_value = Format.get_file_content_by_read(d_file_path)
|
|||
|
|
+ conf_base_info = {"filePath": file_path, "expectedContents": expected_value}
|
|||
|
|
+ expected_conf_lists.get("confBaseInfos").append(conf_base_info)
|
|||
|
|
+ all_domain_expected_files.append(expected_conf_lists)
|
|||
|
|
+
|
|||
|
|
+ LOGGER.debug("all_domain_expected_files is : {}".format(all_domain_expected_files))
|
|||
|
|
+
|
|||
|
|
+ if len(all_domain_expected_files) == 0:
|
|||
|
|
+ code_num = PARAM_ERROR
|
|||
|
|
+ code_string = "The current domain does not exist, please create the domain first."
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ return self.response(code=SUCCEED, message="Successfully get the expected configuration file information.",
|
|||
|
|
+ data=all_domain_expected_files)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class QueryRealConfs(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=QueryRealConfsSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """
|
|||
|
|
+ query the real configuration value in the current hostId node
|
|||
|
|
+
|
|||
|
|
+ query the real configuration value in the current hostId node # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body:
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: List[RealConfInfo]
|
|||
|
|
+ """
|
|||
|
|
+ access_token = connexion.request.headers.get("access_token")
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ host_list = params.get("hostIds")
|
|||
|
|
+
|
|||
|
|
+ check_res = Format.domainCheck(domain)
|
|||
|
|
+ if not check_res:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "Failed to verify the input parameter, please check the input parameters."
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+ # check the domain is Exist
|
|||
|
|
+ is_exist = Format.isDomainExist(domain)
|
|||
|
|
+ if not is_exist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "The current domain does not exist, please create the domain first."
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+ # check whether the host is configured in the domain
|
|||
|
|
+ is_host_list_exist = Format.isHostInDomain(domain)
|
|||
|
|
+ LOGGER.debug("is_host_list_exist is : {}".format(is_host_list_exist))
|
|||
|
|
+ if not is_host_list_exist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "The host information is not set in the current domain. Please add the host information first"
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+ # get all hosts managed by the current domain.
|
|||
|
|
+ # If host_list is empty, query all hosts in the current domain.
|
|||
|
|
+ # If host_list is not empty, the actual contents of the currently given host are queried.
|
|||
|
|
+ exist_host = []
|
|||
|
|
+ failed_host = []
|
|||
|
|
+ if len(host_list) > 0:
|
|||
|
|
+ host_tool = HostTools()
|
|||
|
|
+ exist_host, failed_host = host_tool.getHostExistStatus(domain, host_list)
|
|||
|
|
+ else:
|
|||
|
|
+ res_text = Format.get_hostinfo_by_domain(domain)
|
|||
|
|
+ if len(res_text) == 0:
|
|||
|
|
+ code_num = NO_DATA
|
|||
|
|
+ code_string = "The host currently controlled in the domain is empty. Please add host information to " \
|
|||
|
|
+ "the domain. "
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ if len(exist_host) == 0 or len(failed_host) == len(host_list):
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "The host information is not set in the current domain. Please add the host information first"
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+ # get the management conf in domain
|
|||
|
|
+ res = Format.get_realconf_by_domain_and_host(domain, exist_host, access_token)
|
|||
|
|
+ if len(res) == 0:
|
|||
|
|
+ codeNum = NO_DATA
|
|||
|
|
+ codeString = "Real configuration query failed. The failure reason is : The real configuration does not " \
|
|||
|
|
+ "found. "
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+ return self.response(code=SUCCEED, message="Successfully query real confs", data=res)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class SyncConfToHostFromDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=SyncConfToHostFromDomainSchema, token=True)
|
|||
|
|
+ def put(self, **params):
|
|||
|
|
+ """
|
|||
|
|
+ synchronize the configuration information of the configuration domain to the host # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body:
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: List[HostSyncResult]
|
|||
|
|
+ """
|
|||
|
|
+ access_token = connexion.request.headers.get("access_token")
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ sync_list = params.get("syncList")
|
|||
|
|
+
|
|||
|
|
+ host_sync_confs = dict()
|
|||
|
|
+
|
|||
|
|
+ for sync in sync_list:
|
|||
|
|
+ host_sync_confs[sync["hostId"]] = sync["syncConfigs"]
|
|||
|
|
+
|
|||
|
|
+ # check the input domain
|
|||
|
|
+ check_res = Format.domainCheck(domain)
|
|||
|
|
+ if not check_res:
|
|||
|
|
+ code_num = PARAM_ERROR
|
|||
|
|
+ code_string = "Failed to verify the input parameter, please check the input parameters."
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ # check whether the domain exists
|
|||
|
|
+ is_exist = Format.isDomainExist(domain)
|
|||
|
|
+ if not is_exist:
|
|||
|
|
+ code_num = NO_DATA
|
|||
|
|
+ code_string = "The current domain does not exist, please create the domain first."
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ # get the management host in domain
|
|||
|
|
+ res_host_text = Format.get_hostinfo_by_domain(domain)
|
|||
|
|
+ if len(res_host_text) == 0:
|
|||
|
|
+ code_num = NO_DATA
|
|||
|
|
+ code_string = "The host currently controlled in the domain is empty. Please add host information to the " \
|
|||
|
|
+ "domain. "
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ # Check whether the host is in the managed host list
|
|||
|
|
+ exist_host = []
|
|||
|
|
+ if len(host_sync_confs) > 0:
|
|||
|
|
+ host_ids = host_sync_confs.keys()
|
|||
|
|
+ for host_id in host_ids:
|
|||
|
|
+ for d_host in res_host_text:
|
|||
|
|
+ if host_id == d_host.get("host_id"):
|
|||
|
|
+ exist_host.append(host_id)
|
|||
|
|
+ else:
|
|||
|
|
+ for d_host in res_host_text:
|
|||
|
|
+ temp_host = {}
|
|||
|
|
+ temp_host["hostId"] = d_host.get("host_id")
|
|||
|
|
+ exist_host.append(temp_host)
|
|||
|
|
+ LOGGER.debug("exist_host is : {}".format(exist_host))
|
|||
|
|
+
|
|||
|
|
+ if len(exist_host) == 0:
|
|||
|
|
+ code_num = PARAM_ERROR
|
|||
|
|
+ code_string = "The host information is not set in the current domain. Please add the host information first"
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ # get the management conf in domain
|
|||
|
|
+ man_conf_res_text = Format.get_manageconf_by_domain(domain)
|
|||
|
|
+ LOGGER.debug("man_conf_res_text is : {}".format(man_conf_res_text))
|
|||
|
|
+ manage_confs = man_conf_res_text.get("conf_files")
|
|||
|
|
+
|
|||
|
|
+ # Deserialize and reverse parse the expected configuration
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ # 组装入参
|
|||
|
|
+ file_path_infos = dict()
|
|||
|
|
+ for host_id in exist_host:
|
|||
|
|
+ sync_confs = host_sync_confs.get(host_id)
|
|||
|
|
+ for d_man_conf in manage_confs:
|
|||
|
|
+ file_path = d_man_conf.get("file_path").split(":")[-1]
|
|||
|
|
+ if file_path in sync_confs:
|
|||
|
|
+ contents = d_man_conf.get("contents")
|
|||
|
|
+ file_path_infos[file_path] = contents
|
|||
|
|
+
|
|||
|
|
+ code_num, code_string, sync_res = Format.deal_batch_sync_res(conf_tools, exist_host, file_path_infos,
|
|||
|
|
+ access_token)
|
|||
|
|
+ if code_num != 200:
|
|||
|
|
+ return self.response(code=SERVER_ERROR, message=code_string, data=sync_res)
|
|||
|
|
+ return self.response(code=SUCCEED, message=code_string, data=sync_res)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class QuerySupportedConfs(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=QuerySupportedConfsSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """
|
|||
|
|
+ query supported configuration list # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body:
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: List
|
|||
|
|
+ """
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ check_res = Format.domainCheck(domain)
|
|||
|
|
+ if not check_res:
|
|||
|
|
+ code_num = PARAM_ERROR
|
|||
|
|
+ code_string = "Failed to verify the input parameter, please check the input parameters."
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ is_exist = Format.isDomainExist(domain)
|
|||
|
|
+ if not is_exist:
|
|||
|
|
+ code_num = NO_DATA
|
|||
|
|
+ code_string = "The current domain does not exist, please create the domain first."
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ conf_files = Format.get_manageconf_by_domain(domain)
|
|||
|
|
+ conf_files = conf_files.get("conf_files")
|
|||
|
|
+ if len(conf_files) == 0:
|
|||
|
|
+ return yang_conf_list
|
|||
|
|
+
|
|||
|
|
+ exist_conf_list = []
|
|||
|
|
+ for conf in conf_files:
|
|||
|
|
+ exist_conf_list.append(conf.get('file_path'))
|
|||
|
|
+
|
|||
|
|
+ return list(set(yang_conf_list).difference(set(exist_conf_list)))
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class CompareConfDiff(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=CompareConfDiffSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """
|
|||
|
|
+ compare conf different, return host sync status
|
|||
|
|
+
|
|||
|
|
+ :param body:
|
|||
|
|
+ :type body: dict
|
|||
|
|
+
|
|||
|
|
+ :rtype:
|
|||
|
|
+ """
|
|||
|
|
+ expected_confs_resp_list = params.get("expectedConfsResp")
|
|||
|
|
+ domain_result = params.get("domainResult")
|
|||
|
|
+ expected_confs_resp_dict = Format.deal_expected_confs_resp(expected_confs_resp_list)
|
|||
|
|
+
|
|||
|
|
+ real_conf_res_text_dict = Format.deal_domain_result(domain_result)
|
|||
|
|
+ # 循环real_conf_res_text_list 取出每一个domain的domain_result与expected_confs_resp_dict的expected_confs_resp做对比
|
|||
|
|
+ sync_result = []
|
|||
|
|
+ for domain_name, real_conf_res_text_list in real_conf_res_text_dict.items():
|
|||
|
|
+ expected_confs_resp = expected_confs_resp_dict.get(domain_name)
|
|||
|
|
+ sync_status = Format.diff_mangeconf_with_realconf_for_db(domain_name, real_conf_res_text_list,
|
|||
|
|
+ expected_confs_resp)
|
|||
|
|
+ domain_name = sync_status["domainName"]
|
|||
|
|
+ host_status_list = sync_status["hostStatus"]
|
|||
|
|
+
|
|||
|
|
+ for signal_status in host_status_list:
|
|||
|
|
+ host_id = signal_status["hostId"]
|
|||
|
|
+ domain_host_sync_status = 1
|
|||
|
|
+ sync_status_list = signal_status["syncStatus"]
|
|||
|
|
+ for single_sync_status in sync_status_list:
|
|||
|
|
+ if single_sync_status["isSynced"] == NOT_SYNCHRONIZE:
|
|||
|
|
+ domain_host_sync_status = 0
|
|||
|
|
+ break
|
|||
|
|
+ single_domain_host_status = {"domain_name": domain_name, "host_id": host_id,
|
|||
|
|
+ "sync_status": domain_host_sync_status}
|
|||
|
|
+ sync_result.append(single_domain_host_status)
|
|||
|
|
+ return self.response(code=SUCCEED, message="successfully compare conf diff", data=sync_result)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class BatchSyncConfToHostFromDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=BatchSyncConfToHostFromDomainSchema, token=True)
|
|||
|
|
+ def put(self, **params):
|
|||
|
|
+ """
|
|||
|
|
+ synchronize the configuration information of the configuration domain to the host # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body:
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: List[HostSyncResult]
|
|||
|
|
+ """
|
|||
|
|
+ access_token = connexion.request.headers.get("access_token")
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ host_ids = params.get("hostIds")
|
|||
|
|
+ # check domain
|
|||
|
|
+ base_rsp, code_num = Format.check_domain_param(domain)
|
|||
|
|
+ if code_num != 200:
|
|||
|
|
+ return base_rsp, code_num
|
|||
|
|
+
|
|||
|
|
+ # 根据domain和ip获取有哪些不同步的文件
|
|||
|
|
+ # get manage confs in domain
|
|||
|
|
+ code_num, code_string, manage_confs = Format.get_domain_conf(domain)
|
|||
|
|
+ if not manage_confs:
|
|||
|
|
+ return self.response(code=SUCCEED, message=code_string, data=manage_confs)
|
|||
|
|
+
|
|||
|
|
+ # get real conf in host
|
|||
|
|
+ real_conf_res_text = Format.get_realconf_by_domain_and_host(domain, host_ids, access_token)
|
|||
|
|
+ # compare manage conf with real conf
|
|||
|
|
+ sync_status = Format.diff_mangeconf_with_realconf(domain, real_conf_res_text, manage_confs)
|
|||
|
|
+ # 解析sync_status,取出未同步的数据
|
|||
|
|
+ host_sync_confs = dict()
|
|||
|
|
+ host_status = sync_status["hostStatus"]
|
|||
|
|
+ for host_result in host_status:
|
|||
|
|
+ host_id = host_result["hostId"]
|
|||
|
|
+ sync_status = host_result["syncStatus"]
|
|||
|
|
+ sync_configs = []
|
|||
|
|
+ for sync_result in sync_status:
|
|||
|
|
+ if sync_result["isSynced"] == NOT_SYNCHRONIZE:
|
|||
|
|
+ sync_configs.append(sync_result["file_path"])
|
|||
|
|
+ host_sync_confs[host_id] = sync_configs
|
|||
|
|
+
|
|||
|
|
+ # check the input domain
|
|||
|
|
+ check_res = Format.domainCheck(domain)
|
|||
|
|
+ if not check_res:
|
|||
|
|
+ code_num = PARAM_ERROR
|
|||
|
|
+ code_string = "Failed to verify the input parameter, please check the input parameters."
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ # check whether the domain exists
|
|||
|
|
+ is_exist = Format.isDomainExist(domain)
|
|||
|
|
+ if not is_exist:
|
|||
|
|
+ code_num = NO_DATA
|
|||
|
|
+ code_string = "The current domain does not exist, please create the domain first."
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ # get the management host in domain
|
|||
|
|
+ res_host_text = Format.get_hostinfo_by_domain(domain)
|
|||
|
|
+ if len(res_host_text) == 0:
|
|||
|
|
+ code_num = NO_DATA
|
|||
|
|
+ code_string = "The host currently controlled in the domain is empty. Please add host information to the " \
|
|||
|
|
+ "domain. "
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+ # Check whether the host is in the managed host list
|
|||
|
|
+ exist_host = []
|
|||
|
|
+ if len(host_sync_confs) > 0:
|
|||
|
|
+ host_ids = host_sync_confs.keys()
|
|||
|
|
+ for host_id in host_ids:
|
|||
|
|
+ for d_host in res_host_text:
|
|||
|
|
+ if host_id == d_host.get("host_id"):
|
|||
|
|
+ exist_host.append(host_id)
|
|||
|
|
+ else:
|
|||
|
|
+ for d_host in res_host_text:
|
|||
|
|
+ tmp_host = {"hostId": d_host.get("host_id")}
|
|||
|
|
+ exist_host.append(tmp_host)
|
|||
|
|
+ LOGGER.debug("exist_host is : {}".format(exist_host))
|
|||
|
|
+
|
|||
|
|
+ if len(exist_host) == 0:
|
|||
|
|
+ code_num = PARAM_ERROR
|
|||
|
|
+ code_string = "The host information is not set in the current domain. Please add the host information first"
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+
|
|||
|
|
+ # get the management conf in domain
|
|||
|
|
+ man_conf_res_text = Format.get_manageconf_by_domain(domain)
|
|||
|
|
+ LOGGER.debug("man_conf_res_text is : {}".format(man_conf_res_text))
|
|||
|
|
+ manage_confs = man_conf_res_text.get("conf_files")
|
|||
|
|
+
|
|||
|
|
+ # Deserialize and reverse parse the expected configuration
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ # 组装入参
|
|||
|
|
+ file_path_infos = dict()
|
|||
|
|
+ for host_id in exist_host:
|
|||
|
|
+ sync_confs = host_sync_confs.get(host_id)
|
|||
|
|
+ for d_man_conf in manage_confs:
|
|||
|
|
+ file_path = d_man_conf.get("file_path").split(":")[-1]
|
|||
|
|
+ if file_path in sync_confs:
|
|||
|
|
+ contents = d_man_conf.get("contents")
|
|||
|
|
+ file_path_infos[file_path] = contents
|
|||
|
|
+
|
|||
|
|
+ if not file_path_infos:
|
|||
|
|
+ code_num = PARAM_ERROR
|
|||
|
|
+ code_string = "No config needs to be synchronized"
|
|||
|
|
+ return self.response(code=code_num, message=code_string)
|
|||
|
|
+ code_num, code_string, sync_res = Format.deal_batch_sync_res(conf_tools, exist_host, file_path_infos,
|
|||
|
|
+ access_token)
|
|||
|
|
+
|
|||
|
|
+ if code_num != 200:
|
|||
|
|
+ return self.response(code=SERVER_ERROR, message=code_string, data=sync_res)
|
|||
|
|
+ return self.response(code=SUCCEED, message=code_string, data=sync_res)
|
|||
|
|
diff --git a/ragdoll/controllers/__init__.py b/ragdoll/controllers/__init__.py
|
|||
|
|
deleted file mode 100644
|
|||
|
|
index e69de29..0000000
|
|||
|
|
diff --git a/ragdoll/controllers/confs_controller.py b/ragdoll/controllers/confs_controller.py
|
|||
|
|
deleted file mode 100644
|
|||
|
|
index 44269f9..0000000
|
|||
|
|
--- a/ragdoll/controllers/confs_controller.py
|
|||
|
|
+++ /dev/null
|
|||
|
|
@@ -1,339 +0,0 @@
|
|||
|
|
-import connexion
|
|||
|
|
-import os
|
|||
|
|
-
|
|||
|
|
-from ragdoll.log.log import LOGGER
|
|||
|
|
-from ragdoll.models.base_response import BaseResponse # noqa: E501
|
|||
|
|
-from ragdoll.models.conf_host import ConfHost # noqa: E501
|
|||
|
|
-from ragdoll.models.domain_name import DomainName # noqa: E501
|
|||
|
|
-from ragdoll.models.excepted_conf_info import ExceptedConfInfo # noqa: E501
|
|||
|
|
-from ragdoll.models.sync_req import SyncReq
|
|||
|
|
-from ragdoll.models.sync_status import SyncStatus # noqa: E501
|
|||
|
|
-from ragdoll.models.conf_base_info import ConfBaseInfo
|
|||
|
|
-from ragdoll.models.conf_is_synced import ConfIsSynced
|
|||
|
|
-from ragdoll.models.host_sync_result import HostSyncResult
|
|||
|
|
-
|
|||
|
|
-from ragdoll.controllers.format import Format
|
|||
|
|
-from ragdoll.utils.git_tools import GitTools
|
|||
|
|
-from ragdoll.utils.yang_module import YangModule
|
|||
|
|
-from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
-from ragdoll.utils.host_tools import HostTools
|
|||
|
|
-from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
-from ragdoll.const.conf_files import yang_conf_list
|
|||
|
|
-
|
|||
|
|
-TARGETDIR = GitTools().target_dir
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def get_the_sync_status_of_domain(body=None): # noqa: E501
|
|||
|
|
- """
|
|||
|
|
- get the status of the domain
|
|||
|
|
- get the status of whether the domain has been synchronized # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body:
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: SyncStatus
|
|||
|
|
- """
|
|||
|
|
-
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = DomainName.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
- # check domain
|
|||
|
|
- code_num = 200
|
|||
|
|
- base_rsp = None
|
|||
|
|
- base_rsp, code_num = Format.check_domain_param(domain)
|
|||
|
|
- if code_num != 200:
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- # get manage confs in domain
|
|||
|
|
- LOGGER.debug("############## get the confs in domain ##############")
|
|||
|
|
- base_rsp, code_num, manage_confs = Format._get_domain_conf(domain)
|
|||
|
|
- if code_num != 200:
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- # get real conf in host
|
|||
|
|
- LOGGER.debug("############## query the real conf ##############")
|
|||
|
|
- host_ids = Format.get_hostid_list_by_domain(domain)
|
|||
|
|
- real_conf_res_text = Format.get_realconf_by_domain_and_host(domain, host_ids)
|
|||
|
|
-
|
|||
|
|
- # compare manage conf with real conf
|
|||
|
|
- sync_status = Format.diff_mangeconf_with_realconf(domain, real_conf_res_text, manage_confs)
|
|||
|
|
-
|
|||
|
|
- # deal with not found files
|
|||
|
|
- man_conf_list = []
|
|||
|
|
- for d_man_conf in manage_confs:
|
|||
|
|
- man_conf_list.append(d_man_conf.get("file_path").split(":")[-1])
|
|||
|
|
- for d_host in sync_status.host_status:
|
|||
|
|
- d_sync_status = d_host.sync_status
|
|||
|
|
- file_list = []
|
|||
|
|
- for d_file in d_sync_status:
|
|||
|
|
- file_path = d_file.file_path
|
|||
|
|
- file_list.append(file_path)
|
|||
|
|
- for d_man_conf in man_conf_list:
|
|||
|
|
- if d_man_conf in file_list:
|
|||
|
|
- continue
|
|||
|
|
- else:
|
|||
|
|
- comp_res = "NOT FOUND"
|
|||
|
|
- conf_is_synced = ConfIsSynced(file_path=d_man_conf,
|
|||
|
|
- is_synced=comp_res)
|
|||
|
|
- d_sync_status.append(conf_is_synced)
|
|||
|
|
-
|
|||
|
|
- return sync_status
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def query_excepted_confs(): # noqa: E501
|
|||
|
|
- """
|
|||
|
|
- query the supported configurations in the current project
|
|||
|
|
- queryExpectedConfs # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :rtype: List[ExceptedConfInfo]
|
|||
|
|
- """
|
|||
|
|
- # get all domain
|
|||
|
|
- LOGGER.debug("############## get all domain ##############")
|
|||
|
|
- cmd = "ls {}".format(TARGETDIR)
|
|||
|
|
- git_tools = GitTools()
|
|||
|
|
- res_domain = git_tools.run_shell_return_output(cmd).decode().split()
|
|||
|
|
-
|
|||
|
|
- if len(res_domain) == 0:
|
|||
|
|
- code_num = 400
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- success_domain = []
|
|||
|
|
- all_domain_expected_files = []
|
|||
|
|
- yang_modules = YangModule()
|
|||
|
|
- for d_domian in res_domain:
|
|||
|
|
- domain_path = os.path.join(TARGETDIR, d_domian)
|
|||
|
|
- expected_conf_lists = ExceptedConfInfo(domain_name=d_domian,
|
|||
|
|
- conf_base_infos=[])
|
|||
|
|
- # Traverse all files in the source management repository
|
|||
|
|
- for root, dirs, files in os.walk(domain_path):
|
|||
|
|
- # Domain also contains host cache files, so we need to add hierarchical judgment for root
|
|||
|
|
- if len(files) > 0 and len(root.split('/')) > 3:
|
|||
|
|
- if "hostRecord.txt" in files:
|
|||
|
|
- continue
|
|||
|
|
- for d_file in files:
|
|||
|
|
- feature = os.path.join(root.split('/')[-1], d_file)
|
|||
|
|
- d_module = yang_modules.getModuleByFeature(feature)
|
|||
|
|
- file_lists = yang_modules.getFilePathInModdule(yang_modules.module_list)
|
|||
|
|
- file_path = file_lists.get(d_module.name()).split(":")[-1]
|
|||
|
|
- d_file_path = os.path.join(root, d_file)
|
|||
|
|
- expected_value = Format.get_file_content_by_read(d_file_path)
|
|||
|
|
-
|
|||
|
|
- git_tools = GitTools()
|
|||
|
|
- git_message = git_tools.getLogMessageByPath(d_file_path)
|
|||
|
|
-
|
|||
|
|
- conf_base_info = ConfBaseInfo(file_path=file_path,
|
|||
|
|
- expected_contents=expected_value,
|
|||
|
|
- change_log=git_message)
|
|||
|
|
- expected_conf_lists.conf_base_infos.append(conf_base_info)
|
|||
|
|
- all_domain_expected_files.append(expected_conf_lists)
|
|||
|
|
-
|
|||
|
|
- LOGGER.debug("########################## expetedConfInfo ####################")
|
|||
|
|
- LOGGER.debug("all_domain_expected_files is : {}".format(all_domain_expected_files))
|
|||
|
|
- LOGGER.debug("########################## expetedConfInfo end ####################")
|
|||
|
|
-
|
|||
|
|
- if len(all_domain_expected_files) == 0:
|
|||
|
|
- code_num = 400
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- return all_domain_expected_files
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def query_real_confs(body=None): # noqa: E501
|
|||
|
|
- """
|
|||
|
|
- query the real configuration value in the current hostId node
|
|||
|
|
-
|
|||
|
|
- query the real configuration value in the current hostId node # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body:
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: List[RealConfInfo]
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = ConfHost.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
- host_list = body.host_ids
|
|||
|
|
-
|
|||
|
|
- check_res = Format.domainCheck(domain)
|
|||
|
|
- if not check_res:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- # check the domain is Exist
|
|||
|
|
- is_exist = Format.isDomainExist(domain)
|
|||
|
|
- if not is_exist:
|
|||
|
|
- code_num = 400
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- # check whether the host is configured in the domain
|
|||
|
|
- is_host_list_exist = Format.isHostInDomain(domain)
|
|||
|
|
- LOGGER.debug("is_host_list_exist is : {}".format(is_host_list_exist))
|
|||
|
|
- if not is_host_list_exist:
|
|||
|
|
- code_num = 400
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The host information is not set in the current domain." +
|
|||
|
|
- "Please add the host information first")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- # get all hosts managed by the current domain.
|
|||
|
|
- # If host_list is empty, query all hosts in the current domain.
|
|||
|
|
- # If host_list is not empty, the actual contents of the currently given host are queried.
|
|||
|
|
- exist_host = []
|
|||
|
|
- failed_host = []
|
|||
|
|
- if len(host_list) > 0:
|
|||
|
|
- host_tool = HostTools()
|
|||
|
|
- exist_host, failed_host = host_tool.getHostExistStatus(domain, host_list)
|
|||
|
|
- else:
|
|||
|
|
- LOGGER.debug("############## get the host in domain ##############")
|
|||
|
|
- res_text = Format.get_hostinfo_by_domain(domain)
|
|||
|
|
- if len(res_text) == 0:
|
|||
|
|
- code_num = 404
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The host currently controlled in the domain is empty." +
|
|||
|
|
- "Please add host information to the domain.")
|
|||
|
|
-
|
|||
|
|
- if len(exist_host) == 0 or len(failed_host) == len(host_list):
|
|||
|
|
- code_num = 400
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The host information is not set in the current domain." +
|
|||
|
|
- "Please add the host information first")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- # get the management conf in domain
|
|||
|
|
- LOGGER.debug("############## get the management conf in domain ##############")
|
|||
|
|
- res = Format.get_realconf_by_domain_and_host(domain, exist_host)
|
|||
|
|
- if len(res) == 0:
|
|||
|
|
- code_num = 400
|
|||
|
|
- res_text = "The real configuration does not found."
|
|||
|
|
- base_rsp = BaseResponse(code_num, "Real configuration query failed." +
|
|||
|
|
- "The failure reason is : " + res_text)
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- return res
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def sync_conf_to_host_from_domain(body=None): # noqa: E501
|
|||
|
|
- """
|
|||
|
|
- synchronize the configuration information of the configuration domain to the host # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body:
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: List[HostSyncResult]
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = SyncReq.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
- sync_list = body.sync_list
|
|||
|
|
-
|
|||
|
|
- host_sync_confs = dict()
|
|||
|
|
-
|
|||
|
|
- for sync in sync_list:
|
|||
|
|
- host_sync_confs[sync.host_id] = sync.sync_configs
|
|||
|
|
-
|
|||
|
|
- # check the input domain
|
|||
|
|
- check_res = Format.domainCheck(domain)
|
|||
|
|
- if not check_res:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- # check whether the domain exists
|
|||
|
|
- is_exist = Format.isDomainExist(domain)
|
|||
|
|
- if not is_exist:
|
|||
|
|
- code_num = 404
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- # get the management host in domain
|
|||
|
|
- res_host_text = Format.get_hostinfo_by_domain(domain)
|
|||
|
|
- if len(res_host_text) == 0:
|
|||
|
|
- code_num = 404
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The host currently controlled in the domain is empty." +
|
|||
|
|
- "Please add host information to the domain.")
|
|||
|
|
- # Check whether the host is in the managed host list
|
|||
|
|
- exist_host = []
|
|||
|
|
- if len(host_sync_confs) > 0:
|
|||
|
|
- host_ids = host_sync_confs.keys()
|
|||
|
|
- for host_id in host_ids:
|
|||
|
|
- for d_host in res_host_text:
|
|||
|
|
- if host_id == d_host.get("host_id"):
|
|||
|
|
- exist_host.append(host_id)
|
|||
|
|
- else:
|
|||
|
|
- for d_host in res_host_text:
|
|||
|
|
- temp_host = {}
|
|||
|
|
- temp_host["hostId"] = d_host.get("host_id")
|
|||
|
|
- exist_host.append(temp_host)
|
|||
|
|
- LOGGER.debug("exist_host is : {}".format(exist_host))
|
|||
|
|
-
|
|||
|
|
- if len(exist_host) == 0:
|
|||
|
|
- code_num = 400
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The host information is not set in the current domain." +
|
|||
|
|
- "Please add the host information first")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- # get the management conf in domain
|
|||
|
|
- LOGGER.debug("############## get management conf in domain ##############")
|
|||
|
|
- man_conf_res_text = Format.get_manageconf_by_domain(domain)
|
|||
|
|
- LOGGER.debug("man_conf_res_text is : {}".format(man_conf_res_text))
|
|||
|
|
- manage_confs = man_conf_res_text.get("conf_files")
|
|||
|
|
- LOGGER.debug("manage_confs is : {}".format(manage_confs))
|
|||
|
|
-
|
|||
|
|
- # Deserialize and reverse parse the expected configuration
|
|||
|
|
- conf_tools = ConfTools()
|
|||
|
|
- sync_res = []
|
|||
|
|
- for host_id in exist_host:
|
|||
|
|
- host_sync_result = HostSyncResult(host_id=host_id,
|
|||
|
|
- sync_result=[])
|
|||
|
|
- sync_confs = host_sync_confs.get(host_id)
|
|||
|
|
- for d_man_conf in manage_confs:
|
|||
|
|
- file_path = d_man_conf.get("file_path").split(":")[-1]
|
|||
|
|
- if file_path in sync_confs:
|
|||
|
|
- contents = d_man_conf.get("contents")
|
|||
|
|
- object_parse = ObjectParse()
|
|||
|
|
- Format.deal_sync_res(conf_tools, contents, file_path, host_id, host_sync_result, object_parse)
|
|||
|
|
- sync_res.append(host_sync_result)
|
|||
|
|
-
|
|||
|
|
- return sync_res
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def query_supported_confs(body=None):
|
|||
|
|
- """
|
|||
|
|
- query supported configuration list # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body:
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: List
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = DomainName.from_dict(connexion.request.get_json())
|
|||
|
|
-
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
-
|
|||
|
|
- check_res = Format.domainCheck(domain)
|
|||
|
|
- if not check_res:
|
|||
|
|
- code_num = 400
|
|||
|
|
- base_rsp = BaseResponse(code_num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- is_exist = Format.isDomainExist(domain)
|
|||
|
|
- if not is_exist:
|
|||
|
|
- code_num = 404
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- conf_files = Format.get_manageconf_by_domain(domain)
|
|||
|
|
- conf_files = conf_files.get("conf_files")
|
|||
|
|
- if len(conf_files) == 0:
|
|||
|
|
- return yang_conf_list
|
|||
|
|
-
|
|||
|
|
- exist_conf_list = []
|
|||
|
|
- for conf in conf_files:
|
|||
|
|
- exist_conf_list.append(conf.get('file_path'))
|
|||
|
|
-
|
|||
|
|
- return list(set(yang_conf_list).difference(set(exist_conf_list)))
|
|||
|
|
diff --git a/ragdoll/controllers/domain_controller.py b/ragdoll/controllers/domain_controller.py
|
|||
|
|
deleted file mode 100644
|
|||
|
|
index fd6a7e5..0000000
|
|||
|
|
--- a/ragdoll/controllers/domain_controller.py
|
|||
|
|
+++ /dev/null
|
|||
|
|
@@ -1,124 +0,0 @@
|
|||
|
|
-import connexion
|
|||
|
|
-import six
|
|||
|
|
-import os
|
|||
|
|
-import shutil
|
|||
|
|
-import logging
|
|||
|
|
-
|
|||
|
|
-from ragdoll.models.base_response import BaseResponse # noqa: E501
|
|||
|
|
-from ragdoll.models.domain import Domain # noqa: E501
|
|||
|
|
-from ragdoll import util
|
|||
|
|
-from ragdoll.controllers.format import Format
|
|||
|
|
-from ragdoll.utils.git_tools import GitTools
|
|||
|
|
-
|
|||
|
|
-TARGETDIR = GitTools().target_dir
|
|||
|
|
-
|
|||
|
|
-# logging.basicConfig(filename='log.log',
|
|||
|
|
-# format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
|
|||
|
|
-# datefmt='%Y-%m-%d %H:%M:%S %p',
|
|||
|
|
-# level=10)
|
|||
|
|
-
|
|||
|
|
-def create_domain(body=None): # noqa: E501
|
|||
|
|
- """create domain
|
|||
|
|
-
|
|||
|
|
- create domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body: domain info
|
|||
|
|
- :type body: list | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: BaseResponse
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = [Domain.from_dict(d) for d in connexion.request.get_json()] # noqa: E501
|
|||
|
|
-
|
|||
|
|
- if len(body) == 0:
|
|||
|
|
- base_rsp = BaseResponse(400, "The input domain cannot be empty, please check the domain.")
|
|||
|
|
- return base_rsp
|
|||
|
|
-
|
|||
|
|
- successDomain = []
|
|||
|
|
- failedDomain = []
|
|||
|
|
-
|
|||
|
|
- for domain in body:
|
|||
|
|
- tempDomainName = domain.domain_name
|
|||
|
|
- checkRes = Format.domainCheck(tempDomainName)
|
|||
|
|
- isExist = Format.isDomainExist(tempDomainName)
|
|||
|
|
- if isExist or not checkRes:
|
|||
|
|
- failedDomain.append(tempDomainName)
|
|||
|
|
- else:
|
|||
|
|
- successDomain.append(tempDomainName)
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, tempDomainName)
|
|||
|
|
- os.umask(0o077)
|
|||
|
|
- os.mkdir(domainPath)
|
|||
|
|
-
|
|||
|
|
- if len(failedDomain) == 0:
|
|||
|
|
- codeNum = 200
|
|||
|
|
- codeString = Format.spliceAllSuccString("domain", "created", successDomain)
|
|||
|
|
- else:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- if len(body) == 1:
|
|||
|
|
- if isExist:
|
|||
|
|
- codeString = "domain {} create failed because it has been existed.".format(failedDomain[0])
|
|||
|
|
- elif not checkRes:
|
|||
|
|
- codeString = "domain {} create failed because format is incorrect.".format(failedDomain[0])
|
|||
|
|
- else:
|
|||
|
|
- codeString = Format.splicErrorString("domain", "created", successDomain, failedDomain)
|
|||
|
|
-
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
-
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def delete_domain(domainName): # noqa: E501
|
|||
|
|
- """delete domain
|
|||
|
|
-
|
|||
|
|
- delete domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param domainName: the domain that needs to be deleted
|
|||
|
|
- :type domainName: List[str]
|
|||
|
|
-
|
|||
|
|
- :rtype: BaseResponse
|
|||
|
|
- """
|
|||
|
|
- if len(domainName) == 0:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The entered domian is empty")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- successDomain = []
|
|||
|
|
- failedDomain = []
|
|||
|
|
-
|
|||
|
|
- for tempDomainName in domainName:
|
|||
|
|
- checkRes = Format.domainCheck(tempDomainName)
|
|||
|
|
- isExist = Format.isDomainExist(tempDomainName)
|
|||
|
|
- if checkRes and isExist:
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, tempDomainName)
|
|||
|
|
- successDomain.append(tempDomainName)
|
|||
|
|
- shutil.rmtree(domainPath)
|
|||
|
|
- else:
|
|||
|
|
- failedDomain.append(tempDomainName)
|
|||
|
|
-
|
|||
|
|
- if len(failedDomain) == 0:
|
|||
|
|
- codeNum = 200
|
|||
|
|
- codeString = Format.spliceAllSuccString("domain", "delete", successDomain)
|
|||
|
|
- else:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- codeString = Format.splicErrorString("domain", "delete", successDomain, failedDomain)
|
|||
|
|
-
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def query_domain(): # noqa: E501
|
|||
|
|
- """
|
|||
|
|
- query the list of all configuration domain # noqa: E501
|
|||
|
|
- :rtype: List[Domain]
|
|||
|
|
- """
|
|||
|
|
- domain_list = []
|
|||
|
|
- cmd = "ls {}".format(TARGETDIR)
|
|||
|
|
- gitTools = GitTools()
|
|||
|
|
- ls_res = gitTools.run_shell_return_output(cmd).decode()
|
|||
|
|
- ll_list = ls_res.split('\n')
|
|||
|
|
- for d_ll in ll_list:
|
|||
|
|
- if d_ll:
|
|||
|
|
- domain = Domain(domain_name = d_ll)
|
|||
|
|
- domain_list.append(domain)
|
|||
|
|
-
|
|||
|
|
- return domain_list, 200
|
|||
|
|
diff --git a/ragdoll/controllers/format.py b/ragdoll/controllers/format.py
|
|||
|
|
deleted file mode 100644
|
|||
|
|
index 9676296..0000000
|
|||
|
|
--- a/ragdoll/controllers/format.py
|
|||
|
|
+++ /dev/null
|
|||
|
|
@@ -1,614 +0,0 @@
|
|||
|
|
-import os
|
|||
|
|
-import re
|
|||
|
|
-import json
|
|||
|
|
-import configparser
|
|||
|
|
-import ast
|
|||
|
|
-import requests
|
|||
|
|
-from ragdoll.log.log import LOGGER
|
|||
|
|
-
|
|||
|
|
-from ragdoll.const.conf_handler_const import NOT_SYNCHRONIZE, SYNCHRONIZED, CONFIG, \
|
|||
|
|
- DIRECTORY_FILE_PATH_LIST
|
|||
|
|
-from ragdoll.models import ConfSyncedRes
|
|||
|
|
-from ragdoll.models.base_response import BaseResponse # noqa: E501
|
|||
|
|
-from ragdoll.models.conf_file import ConfFile
|
|||
|
|
-from ragdoll.models.conf_files import ConfFiles
|
|||
|
|
-from ragdoll.models.realconf_base_info import RealconfBaseInfo
|
|||
|
|
-from ragdoll.models.real_conf_info import RealConfInfo # noqa: E501
|
|||
|
|
-from ragdoll.models.conf_is_synced import ConfIsSynced
|
|||
|
|
-from ragdoll.models.host_sync_status import HostSyncStatus
|
|||
|
|
-from ragdoll.models.single_config import SingleConfig
|
|||
|
|
-from ragdoll.models.sync_status import SyncStatus # noqa: E501
|
|||
|
|
-from ragdoll.models.host import Host # noqa: E501
|
|||
|
|
-from ragdoll.utils.host_tools import HostTools
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-class Format(object):
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def domainCheck(domainName):
|
|||
|
|
- res = True
|
|||
|
|
- if not re.match(r"^[A-Za-z0-9_\.-]*$", domainName) or domainName == "" or len(domainName) > 255:
|
|||
|
|
- res = False
|
|||
|
|
- return res
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def isDomainExist(domainName):
|
|||
|
|
- TARGETDIR = Format.get_git_dir()
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, domainName)
|
|||
|
|
- if os.path.exists(domainPath):
|
|||
|
|
- return True
|
|||
|
|
-
|
|||
|
|
- return False
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def spliceAllSuccString(obj, operation, succDomain):
|
|||
|
|
- """
|
|||
|
|
- docstring
|
|||
|
|
- """
|
|||
|
|
- codeString = "All {obj} {oper} successfully, {succ} {obj} in total.".format( \
|
|||
|
|
- obj=obj, oper=operation, succ=len(succDomain))
|
|||
|
|
- return codeString
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def splicErrorString(obj, operation, succDomain, failDomain):
|
|||
|
|
- """
|
|||
|
|
- docstring
|
|||
|
|
- """
|
|||
|
|
- codeString = "{succ} {obj} {oper} successfully, {fail} {obj} {oper} failed.".format( \
|
|||
|
|
- succ=len(succDomain), obj=obj, oper=operation, fail=len(failDomain))
|
|||
|
|
-
|
|||
|
|
- succString = "\n"
|
|||
|
|
- if len(succDomain) > 0:
|
|||
|
|
- succString = "These are successful: "
|
|||
|
|
- for succName in succDomain:
|
|||
|
|
- succString += succName + " "
|
|||
|
|
- succString += "."
|
|||
|
|
-
|
|||
|
|
- if len(failDomain) > 0:
|
|||
|
|
- failString = "These are failed: "
|
|||
|
|
- for failName in failDomain:
|
|||
|
|
- failString += failName + " "
|
|||
|
|
- return codeString + succString + failString
|
|||
|
|
-
|
|||
|
|
- return codeString + succString
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def two_abs_join(abs1, abs2):
|
|||
|
|
- """
|
|||
|
|
- Absolute path Joins two absolute paths together
|
|||
|
|
- :param abs1: main path
|
|||
|
|
- :param abs2: the spliced path
|
|||
|
|
- :return: together the path
|
|||
|
|
- """
|
|||
|
|
- # 1. Format path (change \\ in path to \)
|
|||
|
|
- abs2 = os.fspath(abs2)
|
|||
|
|
-
|
|||
|
|
- # 2. Split the path file
|
|||
|
|
- abs2 = os.path.splitdrive(abs2)[1]
|
|||
|
|
- # 3. Remove the beginning '/'
|
|||
|
|
- abs2 = abs2.strip('\\/') or abs2
|
|||
|
|
- return os.path.abspath(os.path.join(abs1, abs2))
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def isContainedHostIdInfile(f_file, content):
|
|||
|
|
- isContained = False
|
|||
|
|
- with open(f_file, 'r') as d_file:
|
|||
|
|
- for line in d_file.readlines():
|
|||
|
|
- line_dict = json.loads(str(ast.literal_eval(line)).replace("'", "\""))
|
|||
|
|
- if content == line_dict["host_id"]:
|
|||
|
|
- isContained = True
|
|||
|
|
- break
|
|||
|
|
- return isContained
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def addHostToFile(d_file, host):
|
|||
|
|
- info_json = json.dumps(str(host), sort_keys=False, indent=4, separators=(',', ': '))
|
|||
|
|
- os.umask(0o077)
|
|||
|
|
- with open(d_file, 'a+') as host_file:
|
|||
|
|
- host_file.write(info_json)
|
|||
|
|
- host_file.write("\n")
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def getSubDirFiles(path):
|
|||
|
|
- """
|
|||
|
|
- desc: Subdirectory records and files need to be logged to the successConf
|
|||
|
|
- """
|
|||
|
|
- fileRealPathList = []
|
|||
|
|
- fileXPathlist = []
|
|||
|
|
- for root, dirs, files in os.walk(path):
|
|||
|
|
- if len(files) > 0:
|
|||
|
|
- preXpath = root.split('/', 3)[3]
|
|||
|
|
- for d_file in files:
|
|||
|
|
- xpath = os.path.join(preXpath, d_file)
|
|||
|
|
- fileXPathlist.append(xpath)
|
|||
|
|
- realPath = os.path.join(root, d_file)
|
|||
|
|
- fileRealPathList.append(realPath)
|
|||
|
|
-
|
|||
|
|
- return fileRealPathList, fileXPathlist
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def isHostInDomain(domainName):
|
|||
|
|
- """
|
|||
|
|
- desc: Query domain Whether host information is configured in the domain
|
|||
|
|
- """
|
|||
|
|
- isHostInDomain = False
|
|||
|
|
- TARGETDIR = Format.get_git_dir()
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, domainName)
|
|||
|
|
- hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
- if os.path.isfile(hostPath):
|
|||
|
|
- isHostInDomain = True
|
|||
|
|
-
|
|||
|
|
- return isHostInDomain
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def isHostIdExist(hostPath, hostId):
|
|||
|
|
- """
|
|||
|
|
- desc: Query hostId exists within the current host domain management
|
|||
|
|
- """
|
|||
|
|
- isHostIdExist = False
|
|||
|
|
- if os.path.isfile(hostPath) and os.stat(hostPath).st_size > 0:
|
|||
|
|
- with open(hostPath) as h_file:
|
|||
|
|
- for line in h_file.readlines():
|
|||
|
|
- if hostId in line:
|
|||
|
|
- isHostIdExist = True
|
|||
|
|
- break
|
|||
|
|
-
|
|||
|
|
- return isHostIdExist
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def is_exists_file(d_file):
|
|||
|
|
- if os.path.exists(d_file):
|
|||
|
|
- return True
|
|||
|
|
- if os.path.islink(d_file):
|
|||
|
|
- LOGGER.debug("file: %s is a symlink, skipped!", d_file)
|
|||
|
|
- return False
|
|||
|
|
- LOGGER.error("file: %s does not exist.", d_file)
|
|||
|
|
- return False
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def get_file_content_by_readlines(d_file):
|
|||
|
|
- """
|
|||
|
|
- desc: remove empty lines and comments from d_file
|
|||
|
|
- """
|
|||
|
|
- res = []
|
|||
|
|
- try:
|
|||
|
|
- with open(d_file, 'r') as s_f:
|
|||
|
|
- lines = s_f.readlines()
|
|||
|
|
- for line in lines:
|
|||
|
|
- tmp = line.strip()
|
|||
|
|
- if not len(tmp) or tmp.startswith("#"):
|
|||
|
|
- continue
|
|||
|
|
- res.append(line)
|
|||
|
|
- except FileNotFoundError:
|
|||
|
|
- LOGGER.error(f"File not found: {d_file}")
|
|||
|
|
- except IOError as e:
|
|||
|
|
- LOGGER.error(f"IO error: {e}")
|
|||
|
|
- except Exception as e:
|
|||
|
|
- LOGGER.error(f"An error occurred: {e}")
|
|||
|
|
- return res
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def get_file_content_by_read(d_file):
|
|||
|
|
- """
|
|||
|
|
- desc: return a string after read the d_file
|
|||
|
|
- """
|
|||
|
|
- if not os.path.exists(d_file):
|
|||
|
|
- return ""
|
|||
|
|
- with open(d_file, 'r') as s_f:
|
|||
|
|
- lines = s_f.read()
|
|||
|
|
- return lines
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def rsplit(_str, seps):
|
|||
|
|
- """
|
|||
|
|
- Splits _str by the first sep in seps that is found from the right side.
|
|||
|
|
- Returns a tuple without the separator.
|
|||
|
|
- """
|
|||
|
|
- for idx, ch in enumerate(reversed(_str)):
|
|||
|
|
- if ch in seps:
|
|||
|
|
- return _str[0:-idx - 1], _str[-idx:]
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def arch_sep(package_string):
|
|||
|
|
- """
|
|||
|
|
- Helper method for finding if arch separator is '.' or '-'
|
|||
|
|
-
|
|||
|
|
- Args:
|
|||
|
|
- package_string (str): dash separated package string such as 'bash-4.2.39-3.el7'.
|
|||
|
|
-
|
|||
|
|
- Returns:
|
|||
|
|
- str: arch separator
|
|||
|
|
- """
|
|||
|
|
- return '.' if package_string.rfind('.') > package_string.rfind('-') else '-'
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def set_file_content_by_path(content, path):
|
|||
|
|
- res = 0
|
|||
|
|
- if os.path.exists(path):
|
|||
|
|
- with open(path, 'w+') as d_file:
|
|||
|
|
- for d_cont in content:
|
|||
|
|
- d_file.write(d_cont)
|
|||
|
|
- d_file.write("\n")
|
|||
|
|
- res = 1
|
|||
|
|
- return res
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def get_git_dir():
|
|||
|
|
- 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")
|
|||
|
|
- git_dir = ast.literal_eval(cf.get("git", "git_dir"))
|
|||
|
|
- return git_dir
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def get_hostinfo_by_domain(domainName):
|
|||
|
|
- """
|
|||
|
|
- desc: Query hostinfo by domainname
|
|||
|
|
- """
|
|||
|
|
- TARGETDIR = Format.get_git_dir()
|
|||
|
|
- hostlist = []
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, domainName)
|
|||
|
|
- hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
- if not os.path.isfile(hostPath) or os.stat(hostPath).st_size == 0:
|
|||
|
|
- return hostlist
|
|||
|
|
- try:
|
|||
|
|
- with open(hostPath, 'r') as d_file:
|
|||
|
|
- for line in d_file.readlines():
|
|||
|
|
- json_str = json.loads(line)
|
|||
|
|
- host_json = ast.literal_eval(json_str)
|
|||
|
|
- hostId = host_json["host_id"]
|
|||
|
|
- ip = host_json["ip"]
|
|||
|
|
- ipv6 = host_json["ipv6"]
|
|||
|
|
- host = Host(host_id=hostId, ip=ip, ipv6=ipv6)
|
|||
|
|
- hostlist.append(host.to_dict())
|
|||
|
|
- except OSError as err:
|
|||
|
|
- LOGGER.error("OS error: {0}".format(err))
|
|||
|
|
- return hostlist
|
|||
|
|
- if len(hostlist) == 0:
|
|||
|
|
- LOGGER.debug("hostlist is empty : {}".format(hostlist))
|
|||
|
|
- else:
|
|||
|
|
- LOGGER.debug("hostlist is : {}".format(hostlist))
|
|||
|
|
- return hostlist
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def get_manageconf_by_domain(domain):
|
|||
|
|
- expected_conf_lists = ConfFiles(domain_name=domain, conf_files=[])
|
|||
|
|
- TARGETDIR = Format.get_git_dir()
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
- from ragdoll.utils.yang_module import YangModule
|
|||
|
|
- for root, dirs, files in os.walk(domainPath):
|
|||
|
|
- if len(files) > 0 and len(root.split('/')) > 3:
|
|||
|
|
- if "hostRecord.txt" in files:
|
|||
|
|
- continue
|
|||
|
|
- for d_file in files:
|
|||
|
|
- d_file_path = os.path.join(root, d_file)
|
|||
|
|
- contents = Format.get_file_content_by_read(d_file_path)
|
|||
|
|
- feature = os.path.join(root.split('/')[-1], d_file)
|
|||
|
|
- yang_modules = YangModule()
|
|||
|
|
- d_module = yang_modules.getModuleByFeature(feature)
|
|||
|
|
- file_lists = yang_modules.getFilePathInModdule(yang_modules.module_list)
|
|||
|
|
- file_path = file_lists.get(d_module.name()).split(":")[-1]
|
|||
|
|
-
|
|||
|
|
- conf = ConfFile(file_path=file_path, contents=contents)
|
|||
|
|
- expected_conf_lists.conf_files.append(conf.to_dict())
|
|||
|
|
-
|
|||
|
|
- LOGGER.debug("expected_conf_lists is :{}".format(expected_conf_lists))
|
|||
|
|
- return expected_conf_lists.to_dict()
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def get_realconf_by_domain_and_host(domain, exist_host):
|
|||
|
|
- res = []
|
|||
|
|
- conf_files = Format.get_manageconf_by_domain(domain)
|
|||
|
|
-
|
|||
|
|
- # get the real conf in host
|
|||
|
|
- conf_list = []
|
|||
|
|
- from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
- from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
- conf_tools = ConfTools()
|
|||
|
|
- for d_conf in conf_files.get("conf_files"):
|
|||
|
|
- file_path = d_conf.get("file_path").split(":")[-1]
|
|||
|
|
- if file_path not in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
- conf_list.append(file_path)
|
|||
|
|
- else:
|
|||
|
|
- d_conf_cs = d_conf.get("contents")
|
|||
|
|
- d_conf_contents = json.loads(d_conf_cs)
|
|||
|
|
- for d_conf_key, d_conf_value in d_conf_contents.items():
|
|||
|
|
- conf_list.append(d_conf_key)
|
|||
|
|
- LOGGER.debug("############## get the real conf in host ##############")
|
|||
|
|
- get_real_conf_body = {}
|
|||
|
|
- get_real_conf_body_info = []
|
|||
|
|
- for d_host in exist_host:
|
|||
|
|
- get_real_conf_body_infos = {}
|
|||
|
|
- get_real_conf_body_infos["host_id"] = d_host
|
|||
|
|
- get_real_conf_body_infos["config_list"] = conf_list
|
|||
|
|
- get_real_conf_body_info.append(get_real_conf_body_infos)
|
|||
|
|
- get_real_conf_body["infos"] = get_real_conf_body_info
|
|||
|
|
- url = conf_tools.load_url_by_conf().get("collect_url")
|
|||
|
|
- headers = {"Content-Type": "application/json"}
|
|||
|
|
- try:
|
|||
|
|
- response = requests.post(url, data=json.dumps(get_real_conf_body), headers=headers) # post request
|
|||
|
|
- except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
- LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
- codeNum = 500
|
|||
|
|
- codeString = "Failed to obtain the actual configuration, please check the interface of config/collect."
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- resp = json.loads(response.text).get("data")
|
|||
|
|
- resp_code = json.loads(response.text).get("code")
|
|||
|
|
- if (resp_code != "200") and (resp_code != "206"):
|
|||
|
|
- return res
|
|||
|
|
-
|
|||
|
|
- if not resp or len(resp) == 0:
|
|||
|
|
- return res
|
|||
|
|
-
|
|||
|
|
- success_lists = {}
|
|||
|
|
- failed_lists = {}
|
|||
|
|
-
|
|||
|
|
- for d_res in resp:
|
|||
|
|
- d_host_id = d_res.get("host_id")
|
|||
|
|
- fail_files = d_res.get("fail_files")
|
|||
|
|
- if len(fail_files) > 0:
|
|||
|
|
- failed_lists["host_id"] = d_host_id
|
|||
|
|
- failed_lists_conf = []
|
|||
|
|
- for d_failed in fail_files:
|
|||
|
|
- failed_lists_conf.append(d_failed)
|
|||
|
|
- failed_lists["failed_conf"] = failed_lists_conf
|
|||
|
|
- failed_lists["success_conf"] = []
|
|||
|
|
- else:
|
|||
|
|
- success_lists["host_id"] = d_host_id
|
|||
|
|
- success_lists["success_conf"] = []
|
|||
|
|
- success_lists["failed_conf"] = []
|
|||
|
|
-
|
|||
|
|
- read_conf_info = RealConfInfo(domain_name=domain,
|
|||
|
|
- host_id=d_host_id,
|
|||
|
|
- conf_base_infos=[])
|
|||
|
|
- d_res_infos = d_res.get("infos")
|
|||
|
|
-
|
|||
|
|
- real_directory_conf = {}
|
|||
|
|
- real_directory_conf_list = {}
|
|||
|
|
- object_parse = ObjectParse()
|
|||
|
|
- for d_file in d_res_infos:
|
|||
|
|
- content = d_file.get("content")
|
|||
|
|
- file_path = d_file.get("path")
|
|||
|
|
- file_atrr = d_file.get("file_attr").get("mode")
|
|||
|
|
- file_owner = "({}, {})".format(d_file.get("file_attr").get("group"),
|
|||
|
|
- d_file.get("file_attr").get("owner"))
|
|||
|
|
- directory_flag = False
|
|||
|
|
- for dir_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
- if str(file_path).find(dir_path) != -1:
|
|||
|
|
- if real_directory_conf.get(dir_path) is None:
|
|||
|
|
- real_directory_conf_list[dir_path] = list()
|
|||
|
|
- real_directory_conf[dir_path] = RealconfBaseInfo(file_path=dir_path,
|
|||
|
|
- file_attr=file_atrr,
|
|||
|
|
- file_owner=file_owner,
|
|||
|
|
- conf_contens="")
|
|||
|
|
-
|
|||
|
|
- directory_conf = dict()
|
|||
|
|
- directory_conf["path"] = file_path
|
|||
|
|
- directory_conf["content"] = content
|
|||
|
|
- real_directory_conf_list.get(dir_path).append(directory_conf)
|
|||
|
|
- directory_flag = True
|
|||
|
|
- break
|
|||
|
|
- if not directory_flag:
|
|||
|
|
- Format.deal_conf_list_content(content, d_file, file_path, object_parse, read_conf_info)
|
|||
|
|
- if len(fail_files) > 0:
|
|||
|
|
- failed_lists.get("success_conf").append(file_path)
|
|||
|
|
- else:
|
|||
|
|
- success_lists.get("success_conf").append(file_path)
|
|||
|
|
-
|
|||
|
|
- for dir_path, dir_value in real_directory_conf_list.items():
|
|||
|
|
- content_string = object_parse.parse_directory_single_conf_to_json(dir_value,
|
|||
|
|
- real_directory_conf[
|
|||
|
|
- dir_path].file_path)
|
|||
|
|
- real_directory_conf[dir_path].conf_contens = content_string
|
|||
|
|
- real_conf_base_info = real_directory_conf.get(dir_path)
|
|||
|
|
-
|
|||
|
|
- read_conf_info.conf_base_infos.append(real_conf_base_info)
|
|||
|
|
- res.append(read_conf_info)
|
|||
|
|
- return res
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def deal_conf_list_content(content, d_file, file_path, object_parse, read_conf_info):
|
|||
|
|
- content_string = object_parse.parse_conf_to_json(file_path, content)
|
|||
|
|
- file_atrr = d_file.get("file_attr").get("mode")
|
|||
|
|
- file_owner = "({}, {})".format(d_file.get("file_attr").get("group"),
|
|||
|
|
- d_file.get("file_attr").get("owner"))
|
|||
|
|
- real_conf_base_info = RealconfBaseInfo(path=file_path,
|
|||
|
|
- file_path=file_path,
|
|||
|
|
- file_attr=file_atrr,
|
|||
|
|
- file_owner=file_owner,
|
|||
|
|
- conf_contens=content_string)
|
|||
|
|
- read_conf_info.conf_base_infos.append(real_conf_base_info)
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def check_domain_param(domain):
|
|||
|
|
- code_num = 200
|
|||
|
|
- base_resp = None
|
|||
|
|
- check_res = Format.domainCheck(domain)
|
|||
|
|
- if not check_res:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- # check the domian is exist
|
|||
|
|
- is_exist = Format.isDomainExist(domain)
|
|||
|
|
- if not is_exist:
|
|||
|
|
- code_num = 404
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, code_num
|
|||
|
|
-
|
|||
|
|
- # get the exist result of the host in domain
|
|||
|
|
- is_host_list_exist = Format.isHostInDomain(domain)
|
|||
|
|
- if not is_host_list_exist:
|
|||
|
|
- code_num = 404
|
|||
|
|
- base_rsp = BaseResponse(code_num, "The host information is not set in the current domain." +
|
|||
|
|
- "Please add the host information first")
|
|||
|
|
- return base_resp, code_num
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def get_hostid_list_by_domain(domain):
|
|||
|
|
- host_ids = []
|
|||
|
|
- res_text = Format.get_hostinfo_by_domain(domain)
|
|||
|
|
- if len(res_text) == 0:
|
|||
|
|
- return host_ids
|
|||
|
|
-
|
|||
|
|
- host_tools = HostTools()
|
|||
|
|
- host_ids = host_tools.getHostList(res_text)
|
|||
|
|
- return host_ids
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def _get_domain_conf(domain):
|
|||
|
|
- code_num = 200
|
|||
|
|
- base_resp = None
|
|||
|
|
- # get the host info in domain
|
|||
|
|
- LOGGER.debug("############## get the host in domain ##############")
|
|||
|
|
- host_ids = Format.get_hostid_list_by_domain(domain)
|
|||
|
|
- if not host_ids:
|
|||
|
|
- code_num = 404
|
|||
|
|
- base_resp = BaseResponse(code_num, "The host currently controlled in the domain is empty." +
|
|||
|
|
- "Please add host information to the domain.")
|
|||
|
|
- return base_resp, code_num, list()
|
|||
|
|
-
|
|||
|
|
- # get the managent conf in domain
|
|||
|
|
- LOGGER.debug("############## get the managent conf in domain ##############")
|
|||
|
|
- man_conf_res_text = Format.get_manageconf_by_domain(domain)
|
|||
|
|
- manage_confs = man_conf_res_text.get("conf_files")
|
|||
|
|
-
|
|||
|
|
- if len(manage_confs) == 0:
|
|||
|
|
- code_num = 404
|
|||
|
|
- base_resp = BaseResponse(code_num, "The configuration is not set in the current domain." +
|
|||
|
|
- "Please add the configuration information first.")
|
|||
|
|
- return base_resp, code_num, list()
|
|||
|
|
- return base_resp, code_num, manage_confs
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def diff_mangeconf_with_realconf(domain, real_conf_res_text, manage_confs):
|
|||
|
|
- sync_status = SyncStatus(domain_name=domain,
|
|||
|
|
- host_status=[])
|
|||
|
|
- from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
-
|
|||
|
|
- for d_real_conf in real_conf_res_text:
|
|||
|
|
- host_id = d_real_conf.host_id
|
|||
|
|
- host_sync_status = HostSyncStatus(host_id=host_id,
|
|||
|
|
- sync_status=[])
|
|||
|
|
- d_real_conf_base = d_real_conf.conf_base_infos
|
|||
|
|
- for d_conf in d_real_conf_base:
|
|||
|
|
- directory_conf_is_synced = ConfIsSynced(file_path="", is_synced="", single_conf=[])
|
|||
|
|
- d_conf_path = d_conf.file_path
|
|||
|
|
-
|
|||
|
|
- object_parse = ObjectParse()
|
|||
|
|
- # get the conf type and model
|
|||
|
|
- conf_type, conf_model = Format.get_conf_type_model(d_conf_path, object_parse)
|
|||
|
|
-
|
|||
|
|
- Format.deal_conf_sync_status(conf_model, d_conf, d_conf_path, directory_conf_is_synced,
|
|||
|
|
- host_sync_status, manage_confs)
|
|||
|
|
-
|
|||
|
|
- if len(directory_conf_is_synced.single_conf) > 0:
|
|||
|
|
- synced_flag = SYNCHRONIZED
|
|||
|
|
- for single_config in directory_conf_is_synced.single_conf:
|
|||
|
|
- if single_config.single_is_synced == SYNCHRONIZED:
|
|||
|
|
- continue
|
|||
|
|
- else:
|
|||
|
|
- synced_flag = NOT_SYNCHRONIZE
|
|||
|
|
- directory_conf_is_synced.is_synced = synced_flag
|
|||
|
|
- host_sync_status.sync_status.append(directory_conf_is_synced)
|
|||
|
|
- sync_status.host_status.append(host_sync_status)
|
|||
|
|
- return sync_status
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def deal_conf_sync_status(conf_model, d_conf, d_conf_path, directory_conf_is_synced, host_sync_status,
|
|||
|
|
- manage_confs):
|
|||
|
|
- comp_res = ""
|
|||
|
|
- if d_conf_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
- confContents = json.loads(d_conf.conf_contens)
|
|||
|
|
- directory_conf_contents = ""
|
|||
|
|
- for d_man_conf in manage_confs:
|
|||
|
|
- d_man_conf_path = d_man_conf.get("file_path")
|
|||
|
|
- if d_man_conf_path != d_conf_path:
|
|||
|
|
- # if d_man_conf_path not in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
- continue
|
|||
|
|
- else:
|
|||
|
|
- directory_conf_is_synced.file_path = d_conf_path
|
|||
|
|
- directory_conf_contents = d_man_conf.get("contents")
|
|||
|
|
-
|
|||
|
|
- directory_conf_contents_dict = json.loads(directory_conf_contents)
|
|||
|
|
-
|
|||
|
|
- for dir_conf_content_key, dir_conf_content_value in directory_conf_contents_dict.items():
|
|||
|
|
- if dir_conf_content_key not in confContents.keys():
|
|||
|
|
- single_conf = SingleConfig(single_file_path=dir_conf_content_key,
|
|||
|
|
- single_is_synced=NOT_SYNCHRONIZE)
|
|||
|
|
- directory_conf_is_synced.single_conf.append(single_conf)
|
|||
|
|
- else:
|
|||
|
|
- dst_conf = confContents.get(dir_conf_content_key)
|
|||
|
|
- comp_res = conf_model.conf_compare(dir_conf_content_value, dst_conf)
|
|||
|
|
- single_conf = SingleConfig(single_file_path=dir_conf_content_key, single_is_synced=comp_res)
|
|||
|
|
- directory_conf_is_synced.single_conf.append(single_conf)
|
|||
|
|
- else:
|
|||
|
|
- for d_man_conf in manage_confs:
|
|||
|
|
- if d_man_conf.get("file_path").split(":")[-1] != d_conf_path:
|
|||
|
|
- continue
|
|||
|
|
- comp_res = conf_model.conf_compare(d_man_conf.get("contents"), d_conf.conf_contens)
|
|||
|
|
- conf_is_synced = ConfIsSynced(file_path=d_conf_path,
|
|||
|
|
- is_synced=comp_res)
|
|||
|
|
- host_sync_status.sync_status.append(conf_is_synced)
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def get_conf_type_model(d_conf_path, object_parse):
|
|||
|
|
- for dir_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
- if str(d_conf_path).find(dir_path) != -1:
|
|||
|
|
- conf_type = object_parse.get_conf_type_by_conf_path(dir_path)
|
|||
|
|
- conf_model = object_parse.create_conf_model_by_type(conf_type)
|
|||
|
|
- else:
|
|||
|
|
- conf_type = object_parse.get_conf_type_by_conf_path(d_conf_path)
|
|||
|
|
- conf_model = object_parse.create_conf_model_by_type(conf_type)
|
|||
|
|
- return conf_type, conf_model
|
|||
|
|
-
|
|||
|
|
- @staticmethod
|
|||
|
|
- def deal_sync_res(conf_tools, contents, file_path, host_id, host_sync_result, object_parse):
|
|||
|
|
- sync_conf_url = conf_tools.load_url_by_conf().get("sync_url")
|
|||
|
|
- headers = {"Content-Type": "application/json"}
|
|||
|
|
- if file_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
- conf_sync_res_list = []
|
|||
|
|
- for directory_file_path, directory_content in json.loads(contents).items():
|
|||
|
|
- content = object_parse.parse_json_to_conf(directory_file_path, directory_content)
|
|||
|
|
- # Configuration to the host
|
|||
|
|
- data = {"host_id": host_id, "file_path": directory_file_path, "content": content}
|
|||
|
|
- try:
|
|||
|
|
- sync_response = requests.put(sync_conf_url, data=json.dumps(data), headers=headers)
|
|||
|
|
- except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
- LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
- codeNum = 500
|
|||
|
|
- codeString = "Failed to sync configuration, please check the interface of config/sync."
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- resp_code = json.loads(sync_response.text).get('code')
|
|||
|
|
- resp = json.loads(sync_response.text).get('data').get('resp')
|
|||
|
|
-
|
|||
|
|
- if resp_code == "200" and resp.get('sync_result') is True:
|
|||
|
|
- conf_sync_res_list.append("SUCCESS")
|
|||
|
|
- else:
|
|||
|
|
- conf_sync_res_list.append("FAILED")
|
|||
|
|
- if "FAILED" in conf_sync_res_list:
|
|||
|
|
- conf_sync_res = ConfSyncedRes(file_path=file_path, result="FAILED")
|
|||
|
|
- else:
|
|||
|
|
- conf_sync_res = ConfSyncedRes(file_path=file_path, result="SUCCESS")
|
|||
|
|
- host_sync_result.sync_result.append(conf_sync_res)
|
|||
|
|
- else:
|
|||
|
|
- content = object_parse.parse_json_to_conf(file_path, contents)
|
|||
|
|
- # Configuration to the host
|
|||
|
|
- data = {"host_id": host_id, "file_path": file_path, "content": content}
|
|||
|
|
- sync_response = requests.put(sync_conf_url, data=json.dumps(data), headers=headers)
|
|||
|
|
-
|
|||
|
|
- resp_code = json.loads(sync_response.text).get('code')
|
|||
|
|
- resp = json.loads(sync_response.text).get('data').get('resp')
|
|||
|
|
- conf_sync_res = ConfSyncedRes(file_path=file_path,
|
|||
|
|
- result="")
|
|||
|
|
- if resp_code == "200" and resp.get('sync_result') is True:
|
|||
|
|
- conf_sync_res.result = "SUCCESS"
|
|||
|
|
- else:
|
|||
|
|
- conf_sync_res.result = "FAILED"
|
|||
|
|
- host_sync_result.sync_result.append(conf_sync_res)
|
|||
|
|
diff --git a/ragdoll/controllers/host_controller.py b/ragdoll/controllers/host_controller.py
|
|||
|
|
deleted file mode 100644
|
|||
|
|
index 1f491fe..0000000
|
|||
|
|
--- a/ragdoll/controllers/host_controller.py
|
|||
|
|
+++ /dev/null
|
|||
|
|
@@ -1,273 +0,0 @@
|
|||
|
|
-import connexion
|
|||
|
|
-import six
|
|||
|
|
-import os
|
|||
|
|
-import json
|
|||
|
|
-import re
|
|||
|
|
-import ast
|
|||
|
|
-
|
|||
|
|
-from ragdoll.log.log import LOGGER
|
|||
|
|
-from ragdoll.models.base_response import BaseResponse # noqa: E501
|
|||
|
|
-from ragdoll.models.domain_name import DomainName # noqa: E501
|
|||
|
|
-from ragdoll.models.host import Host # noqa: E501
|
|||
|
|
-from ragdoll.models.host_infos import HostInfos # noqa: E501
|
|||
|
|
-from ragdoll import util
|
|||
|
|
-from ragdoll.controllers.format import Format
|
|||
|
|
-from ragdoll.utils.git_tools import GitTools
|
|||
|
|
-
|
|||
|
|
-TARGETDIR = GitTools().target_dir
|
|||
|
|
-
|
|||
|
|
-def add_host_in_domain(body=None): # noqa: E501
|
|||
|
|
- """add host in the configuration domain
|
|||
|
|
-
|
|||
|
|
- add host in the configuration domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body: domain info
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: BaseResponse
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = HostInfos.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
- host_infos = body.host_infos
|
|||
|
|
-
|
|||
|
|
- # check whether host_infos is empty
|
|||
|
|
- if len(host_infos) == 0:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Enter host info cannot be empty, please check the host info.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- checkRes = Format.domainCheck(domain)
|
|||
|
|
- if not checkRes:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- # check whether the domain exists
|
|||
|
|
- isExist = Format.isDomainExist(domain)
|
|||
|
|
- if not isExist:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- successHost = []
|
|||
|
|
- failedHost = []
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
-
|
|||
|
|
- # Check whether the current host exists in the domain.
|
|||
|
|
- for host in host_infos:
|
|||
|
|
- hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
- if os.path.isfile(hostPath):
|
|||
|
|
- isContained = Format.isContainedHostIdInfile(hostPath, host.host_id)
|
|||
|
|
- if isContained:
|
|||
|
|
- LOGGER.debug("##########isContained###############")
|
|||
|
|
- failedHost.append(host.host_id)
|
|||
|
|
- else:
|
|||
|
|
- Format.addHostToFile(hostPath, host)
|
|||
|
|
- successHost.append(host.host_id)
|
|||
|
|
- else:
|
|||
|
|
- Format.addHostToFile(hostPath, host)
|
|||
|
|
- successHost.append(host.host_id)
|
|||
|
|
-
|
|||
|
|
- if len(failedHost) == len(host_infos):
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The all host already exists in the administrative scope of the domain.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # Joining together the returned codenum codeMessage
|
|||
|
|
- if len(failedHost) == 0:
|
|||
|
|
- codeNum = 200
|
|||
|
|
- codeString = Format.spliceAllSuccString("host", "add hosts", successHost)
|
|||
|
|
- else:
|
|||
|
|
- codeNum = 202
|
|||
|
|
- codeString = Format.splicErrorString("host", "add hosts", successHost, failedHost)
|
|||
|
|
-
|
|||
|
|
- # git commit maessage
|
|||
|
|
- if len(host_infos) > 0:
|
|||
|
|
- git_tools = GitTools()
|
|||
|
|
- commit_code = git_tools.gitCommit("Add the host in {} domian, ".format(domain) +
|
|||
|
|
- "the host including : {}".format(successHost))
|
|||
|
|
-
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
-
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def delete_host_in_domain(body=None): # noqa: E501
|
|||
|
|
- """delete host in the configuration domain
|
|||
|
|
-
|
|||
|
|
- delete the host in the configuration domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body: domain info
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: BaseResponse
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = HostInfos.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
- hostInfos = body.host_infos
|
|||
|
|
-
|
|||
|
|
- # check the input domain
|
|||
|
|
- checkRes = Format.domainCheck(domain)
|
|||
|
|
- if not checkRes:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- # check whether the domain exists
|
|||
|
|
- isExist = Format.isDomainExist(domain)
|
|||
|
|
- if not isExist:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # Whether the host information added within the current domain is empty while ain exists
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
- hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
- if not os.path.isfile(hostPath) or (os.path.isfile(hostPath) and os.stat(hostPath).st_size == 0):
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The host information is not set in the current domain." +
|
|||
|
|
- "Please add the host information first")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # If the input host information is empty, the host information of the whole domain is cleared
|
|||
|
|
- if len(hostInfos) == 0:
|
|||
|
|
- if os.path.isfile(hostPath):
|
|||
|
|
- try:
|
|||
|
|
- os.remove(hostPath)
|
|||
|
|
- except OSError as ex:
|
|||
|
|
- #logging.error("the host delete failed")
|
|||
|
|
- codeNum = 500
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The host delete failed.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- codeNum = 200
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "All hosts are deleted in the current domain.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # If the domain exists, check whether the current input parameter host belongs to the corresponding
|
|||
|
|
- # domain. If the host is in the domain, the host is deleted. If the host is no longer in the domain,
|
|||
|
|
- # the host is added to the failure range
|
|||
|
|
- containedInHost = []
|
|||
|
|
- notContainedInHost = []
|
|||
|
|
- os.umask(0o077)
|
|||
|
|
- for hostInfo in hostInfos:
|
|||
|
|
- hostId = hostInfo.host_id
|
|||
|
|
- isContained = False
|
|||
|
|
- try:
|
|||
|
|
- with open(hostPath, 'r') as d_file:
|
|||
|
|
- lines = d_file.readlines()
|
|||
|
|
- with open(hostPath, 'w') as w_file:
|
|||
|
|
- for line in lines:
|
|||
|
|
- line_host_id = json.loads(str(ast.literal_eval(line)).replace("'", "\""))['host_id']
|
|||
|
|
- if hostId != line_host_id:
|
|||
|
|
- w_file.write(line)
|
|||
|
|
- else:
|
|||
|
|
- isContained = True
|
|||
|
|
- except OSError as err:
|
|||
|
|
- LOGGER.error("OS error: {0}".format(err))
|
|||
|
|
- codeNum = 500
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "OS error: {0}".format(err))
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- if isContained:
|
|||
|
|
- containedInHost.append(hostId)
|
|||
|
|
- else:
|
|||
|
|
- notContainedInHost.append(hostId)
|
|||
|
|
-
|
|||
|
|
- # All hosts do not belong to the domain
|
|||
|
|
- if len(notContainedInHost) == len(hostInfos):
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "All the host does not belong to the domain control, " +
|
|||
|
|
- "please enter the host again")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # Some hosts belong to domains, and some hosts do not belong to domains.
|
|||
|
|
- if len(notContainedInHost) == 0:
|
|||
|
|
- codeNum = 200
|
|||
|
|
- codeString = Format.spliceAllSuccString("host", "delete", containedInHost)
|
|||
|
|
- else:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- codeString = Format.splicErrorString("host", "delete", containedInHost, notContainedInHost)
|
|||
|
|
-
|
|||
|
|
- # git commit message
|
|||
|
|
- if len(containedInHost) > 0:
|
|||
|
|
- git_tools = GitTools()
|
|||
|
|
- commit_code = git_tools.gitCommit("Delet the host in {} domian, ".format(domain) +
|
|||
|
|
- "the host including : {}".format(containedInHost))
|
|||
|
|
-
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
-
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def get_host_by_domain_name(body=None): # noqa: E501
|
|||
|
|
- """get host by domainName
|
|||
|
|
-
|
|||
|
|
- get the host information of the configuration domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body: domain info
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: List[Host]
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = DomainName.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
-
|
|||
|
|
- # check the input domain
|
|||
|
|
- checkRes = Format.domainCheck(domain)
|
|||
|
|
- if not checkRes:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- # check whether the domain exists
|
|||
|
|
- isExist = Format.isDomainExist(domain)
|
|||
|
|
- if not isExist:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # The domain exists, but the host information is empty
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
- hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
- if not os.path.isfile(hostPath) or (os.path.isfile(hostPath) and os.stat(hostPath).st_size == 0):
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The host information is not set in the current domain." +
|
|||
|
|
- "Please add the host information first.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # The domain exists, and the host information exists and is not empty
|
|||
|
|
- hostlist = []
|
|||
|
|
- LOGGER.debug("hostPath is : {}".format(hostPath))
|
|||
|
|
- try:
|
|||
|
|
- with open(hostPath, 'r') as d_file:
|
|||
|
|
- for line in d_file.readlines():
|
|||
|
|
- json_str = json.loads(line)
|
|||
|
|
- host_json = ast.literal_eval(json_str)
|
|||
|
|
- hostId = host_json["host_id"]
|
|||
|
|
- ip = host_json["ip"]
|
|||
|
|
- ipv6 = host_json["ipv6"]
|
|||
|
|
- host = Host(host_id=hostId, ip=ip, ipv6=ipv6)
|
|||
|
|
- hostlist.append(host)
|
|||
|
|
- except OSError as err:
|
|||
|
|
- LOGGER.error("OS error: {0}".format(err))
|
|||
|
|
- codeNum = 500
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "OS error: {0}".format(err))
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # Joining together the returned codenum codeMessag
|
|||
|
|
- if len(hostlist) == 0:
|
|||
|
|
- codeNum = 500
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "Some unknown problems.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- else:
|
|||
|
|
- LOGGER.debug("hostlist is : {}".format(hostlist))
|
|||
|
|
- codeNum = 200
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "Get host info in the domain succeccfully")
|
|||
|
|
-
|
|||
|
|
- return hostlist
|
|||
|
|
diff --git a/ragdoll/controllers/management_controller.py b/ragdoll/controllers/management_controller.py
|
|||
|
|
deleted file mode 100644
|
|||
|
|
index 101802a..0000000
|
|||
|
|
--- a/ragdoll/controllers/management_controller.py
|
|||
|
|
+++ /dev/null
|
|||
|
|
@@ -1,601 +0,0 @@
|
|||
|
|
-import io
|
|||
|
|
-
|
|||
|
|
-import connexion
|
|||
|
|
-import os
|
|||
|
|
-import json
|
|||
|
|
-import requests
|
|||
|
|
-
|
|||
|
|
-from ragdoll.const.conf_handler_const import DIRECTORY_FILE_PATH_LIST
|
|||
|
|
-from ragdoll.log.log import LOGGER
|
|||
|
|
-from ragdoll.models.base_response import BaseResponse # noqa: E501
|
|||
|
|
-from ragdoll.models.confs import Confs
|
|||
|
|
-from ragdoll.models.conf_file import ConfFile
|
|||
|
|
-from ragdoll.models.conf_files import ConfFiles
|
|||
|
|
-from ragdoll.models.conf_base_info import ConfBaseInfo
|
|||
|
|
-from ragdoll.models.excepted_conf_info import ExceptedConfInfo
|
|||
|
|
-from ragdoll.models.domain_name import DomainName # noqa: E501
|
|||
|
|
-from ragdoll.models.manage_confs import ManageConfs
|
|||
|
|
-from ragdoll.controllers.format import Format
|
|||
|
|
-from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
-from ragdoll.utils.git_tools import GitTools
|
|||
|
|
-from ragdoll.utils.yang_module import YangModule
|
|||
|
|
-from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
-
|
|||
|
|
-TARGETDIR = GitTools().target_dir
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def add_management_confs_in_domain(body=None): # noqa: E501
|
|||
|
|
- """add management configuration items and expected values in the domain
|
|||
|
|
-
|
|||
|
|
- add management configuration items and expected values in the domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body: domain info
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: BaseResponse
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = Confs.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
- conf_files = body.conf_files
|
|||
|
|
-
|
|||
|
|
- # check the input domain
|
|||
|
|
- checkRes = Format.domainCheck(domain)
|
|||
|
|
- if not checkRes:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- # check whether the domain exists
|
|||
|
|
- isExist = Format.isDomainExist(domain)
|
|||
|
|
- if not isExist:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # check whether the conf_files is null
|
|||
|
|
- if len(conf_files) == 0:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The path of file can't be empty")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # Check all conf_files and check whether contents is empty. If so, the query actual configuration
|
|||
|
|
- # interface is called. If not, the conversion is performed directly.
|
|||
|
|
- # Content and host_id can be set to either content or host_id.
|
|||
|
|
- # If they are both empty, invalid input is returned.
|
|||
|
|
- contents_list_null = []
|
|||
|
|
- contents_list_non_null = []
|
|||
|
|
- for d_conf in conf_files:
|
|||
|
|
- if d_conf.contents:
|
|||
|
|
- contents_list_non_null.append(d_conf)
|
|||
|
|
- elif d_conf.host_id:
|
|||
|
|
- contents_list_null.append(d_conf)
|
|||
|
|
- else:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The input parameters are not compliant, " +
|
|||
|
|
- "please check the input parameters.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- successConf = []
|
|||
|
|
- failedConf = []
|
|||
|
|
- object_parse = ObjectParse()
|
|||
|
|
- yang_module = YangModule()
|
|||
|
|
- conf_tools = ConfTools()
|
|||
|
|
- # Content is not an empty scene and is directly analyed and parsed
|
|||
|
|
- if len(contents_list_non_null) > 0:
|
|||
|
|
- for d_conf in contents_list_non_null:
|
|||
|
|
- if not d_conf.contents.strip():
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The input parameters are not compliant, " +
|
|||
|
|
- "please check the input parameters.")
|
|||
|
|
- 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):
|
|||
|
|
- 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:
|
|||
|
|
- # get the real conf in host
|
|||
|
|
- LOGGER.debug("############## get the real conf in host ##############")
|
|||
|
|
- get_real_conf_body = {}
|
|||
|
|
- get_real_conf_body_info = []
|
|||
|
|
- LOGGER.debug("contents_list_null is : {}".format(contents_list_null))
|
|||
|
|
- exist_host = dict()
|
|||
|
|
- for d_conf in contents_list_null:
|
|||
|
|
- host_id = int(d_conf.host_id)
|
|||
|
|
- if host_id in exist_host:
|
|||
|
|
- if d_conf.file_path not in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
- exist_host[host_id].append(d_conf.file_path)
|
|||
|
|
- else:
|
|||
|
|
- codeNum, codeString, file_paths = object_parse.get_directory_files(d_conf, host_id)
|
|||
|
|
- if len(file_paths) == 0:
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- else:
|
|||
|
|
- for file_path in file_paths:
|
|||
|
|
- exist_host[host_id].append(file_path)
|
|||
|
|
- else:
|
|||
|
|
- if d_conf.file_path not in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
- conf_list = list()
|
|||
|
|
- conf_list.append(d_conf.file_path)
|
|||
|
|
- exist_host[host_id] = conf_list
|
|||
|
|
- else:
|
|||
|
|
- codeNum, codeString, file_paths = object_parse.get_directory_files(d_conf, host_id)
|
|||
|
|
- if len(file_paths) == 0:
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- else:
|
|||
|
|
- exist_host[host_id] = file_paths
|
|||
|
|
-
|
|||
|
|
- for k, v in exist_host.items():
|
|||
|
|
- confs = dict()
|
|||
|
|
- confs["host_id"] = k
|
|||
|
|
- confs["config_list"] = v
|
|||
|
|
- get_real_conf_body_info.append(confs)
|
|||
|
|
-
|
|||
|
|
- get_real_conf_body["infos"] = get_real_conf_body_info
|
|||
|
|
-
|
|||
|
|
- url = conf_tools.load_url_by_conf().get("collect_url")
|
|||
|
|
- headers = {"Content-Type": "application/json"}
|
|||
|
|
- try:
|
|||
|
|
- response = requests.post(url, data=json.dumps(get_real_conf_body), headers=headers) # post request
|
|||
|
|
- except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
- LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
- codeNum = 500
|
|||
|
|
- codeString = "Failed to obtain the actual configuration, please check the interface of config/collect."
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- response_code = json.loads(response.text).get("code")
|
|||
|
|
- if response_code == None:
|
|||
|
|
- codeNum = 500
|
|||
|
|
- codeString = "Failed to obtain the actual configuration, please check the interface of conf/collect."
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- if (response_code != "200") and (response_code != "206"):
|
|||
|
|
- codeNum = 500
|
|||
|
|
- codeString = "Failed to obtain the actual configuration, please check the file exists."
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- reps = json.loads(response.text).get("data")
|
|||
|
|
- if not reps or len(reps) == 0:
|
|||
|
|
- codeNum = 500
|
|||
|
|
- codeString = "Failed to obtain the actual configuration, please check the host info for conf/collect."
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- directory_d_file = []
|
|||
|
|
- directory_d_files = {}
|
|||
|
|
- for d_res in reps:
|
|||
|
|
- failedlist = d_res.get("fail_files")
|
|||
|
|
- if len(failedlist) > 0:
|
|||
|
|
- for d_failed in failedlist:
|
|||
|
|
- failedConf.append(d_failed)
|
|||
|
|
- continue
|
|||
|
|
- d_res_infos = d_res.get("infos")
|
|||
|
|
- for d_file in d_res_infos:
|
|||
|
|
- for dir_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
- if str(d_file.get("path")).find(dir_path) == -1:
|
|||
|
|
- file_path = d_file.get("path")
|
|||
|
|
- content = d_file.get("content")
|
|||
|
|
- 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):
|
|||
|
|
- 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)
|
|||
|
|
- else:
|
|||
|
|
- directory_d_file.append(d_file)
|
|||
|
|
- directory_d_files[dir_path] = directory_d_file
|
|||
|
|
- if len(directory_d_files) > 0:
|
|||
|
|
- for dir_path, directory_d_file in directory_d_files.items():
|
|||
|
|
- content_string = object_parse.parse_dir_conf_to_json(dir_path, directory_d_file)
|
|||
|
|
- if not content_string or not json.loads(content_string):
|
|||
|
|
- failedConf.append(dir_path)
|
|||
|
|
- else:
|
|||
|
|
- feature_path = yang_module.get_feature_by_real_path(domain, dir_path)
|
|||
|
|
- result = conf_tools.wirteFileInPath(feature_path, content_string + '\n')
|
|||
|
|
- if result:
|
|||
|
|
- successConf.append(dir_path)
|
|||
|
|
- else:
|
|||
|
|
- failedConf.append(dir_path)
|
|||
|
|
- # git commit message
|
|||
|
|
- if len(successConf) > 0:
|
|||
|
|
- git_tools = GitTools()
|
|||
|
|
- succ_conf = ""
|
|||
|
|
- for d_conf in successConf:
|
|||
|
|
- succ_conf = succ_conf + d_conf + " "
|
|||
|
|
- commit_code = git_tools.gitCommit("Add the conf in {} domian, ".format(domain) +
|
|||
|
|
- "the path including : {}".format(succ_conf))
|
|||
|
|
-
|
|||
|
|
- # Joinin together the returned codenum and codeMessage
|
|||
|
|
- LOGGER.debug("*******************************************")
|
|||
|
|
- LOGGER.debug("successConf is : {}".format(successConf))
|
|||
|
|
- LOGGER.debug("failedConf is : {}".format(failedConf))
|
|||
|
|
- if len(successConf) == 0:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- codeString = "All configurations failed to be added."
|
|||
|
|
- elif len(failedConf) > 0:
|
|||
|
|
- codeNum = 206
|
|||
|
|
- codeString = Format.splicErrorString("confs", "add management conf", successConf, failedConf)
|
|||
|
|
- else:
|
|||
|
|
- codeNum = 200
|
|||
|
|
- codeString = Format.spliceAllSuccString("confs", "add management conf", successConf)
|
|||
|
|
-
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
-
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def upload_management_confs_in_domain(): # noqa: E501
|
|||
|
|
- """upload management configuration items and expected values in the domain
|
|||
|
|
-
|
|||
|
|
- upload management configuration items and expected values in the domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body: file info
|
|||
|
|
- :type body: FileStorage
|
|||
|
|
-
|
|||
|
|
- :rtype: BaseResponse
|
|||
|
|
- """
|
|||
|
|
- file = connexion.request.files['file']
|
|||
|
|
- filePath = connexion.request.form.get("filePath")
|
|||
|
|
- domainName = connexion.request.form.get("domainName")
|
|||
|
|
-
|
|||
|
|
- # check the input domainName
|
|||
|
|
- checkRes = Format.domainCheck(domainName)
|
|||
|
|
- if not checkRes:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- # check whether the domainName exists
|
|||
|
|
- isExist = Format.isDomainExist(domainName)
|
|||
|
|
- if not isExist:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The current domain does not exist, please create the domain first.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # check whether the file is null
|
|||
|
|
- if file is None:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The file of conf can't be empty")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # check whether the conf is null
|
|||
|
|
- if filePath is None:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The conf body of conf can't be empty")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- successConf = []
|
|||
|
|
- failedConf = []
|
|||
|
|
- object_parse = ObjectParse()
|
|||
|
|
- yang_module = YangModule()
|
|||
|
|
- conf_tools = ConfTools()
|
|||
|
|
-
|
|||
|
|
- # content is file
|
|||
|
|
- if file:
|
|||
|
|
- if not filePath.strip():
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The input parameters are not compliant, " +
|
|||
|
|
- "please check the input parameters.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- try:
|
|||
|
|
- file_bytes = file.read()
|
|||
|
|
- if len(file_bytes) > 1024 * 1024:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The size of the uploaded file must be less than 1MB")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- byte_stream = io.BytesIO(file_bytes)
|
|||
|
|
-
|
|||
|
|
- # Read the contents of the byte stream
|
|||
|
|
- line_content = byte_stream.read().decode("UTF-8")
|
|||
|
|
- except OSError as err:
|
|||
|
|
- LOGGER.error("OS error: {}".format(err))
|
|||
|
|
- codeNum = 500
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "OS error: {0}".format(err))
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- except Exception as ex:
|
|||
|
|
- LOGGER.error("OS error: {}".format(ex))
|
|||
|
|
- codeNum = 500
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "read file error: {0}".format(ex))
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- content_string = object_parse.parse_conf_to_json(filePath, line_content)
|
|||
|
|
- if not content_string or not json.loads(content_string):
|
|||
|
|
- failedConf.append(filePath)
|
|||
|
|
- else:
|
|||
|
|
- # create the file and expected value in domain
|
|||
|
|
- feature_path = yang_module.get_feature_by_real_path(domainName, filePath)
|
|||
|
|
- result = conf_tools.wirteFileInPath(feature_path, content_string + '\n')
|
|||
|
|
- if result:
|
|||
|
|
- successConf.append(filePath)
|
|||
|
|
- else:
|
|||
|
|
- failedConf.append(filePath)
|
|||
|
|
-
|
|||
|
|
- # git commit message
|
|||
|
|
- if len(successConf) > 0:
|
|||
|
|
- git_tools = GitTools()
|
|||
|
|
- succ_conf = ""
|
|||
|
|
- for d_conf in successConf:
|
|||
|
|
- succ_conf = succ_conf + d_conf + " "
|
|||
|
|
- commit_code = git_tools.gitCommit("Add the conf in {} domian, ".format(domainName) +
|
|||
|
|
- "the path including : {}".format(succ_conf))
|
|||
|
|
-
|
|||
|
|
- # Joinin together the returned codenum and codeMessage
|
|||
|
|
- LOGGER.debug("*******************************************")
|
|||
|
|
- LOGGER.debug("successConf is : {}".format(successConf))
|
|||
|
|
- LOGGER.debug("failedConf is : {}".format(failedConf))
|
|||
|
|
- if len(successConf) == 0:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- codeString = "All configurations failed to be added."
|
|||
|
|
- elif len(failedConf) > 0:
|
|||
|
|
- codeNum = 206
|
|||
|
|
- codeString = Format.splicErrorString("confs", "add management conf", successConf, failedConf)
|
|||
|
|
- else:
|
|||
|
|
- codeNum = 200
|
|||
|
|
- codeString = Format.spliceAllSuccString("confs", "add management conf", successConf)
|
|||
|
|
-
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
-
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def delete_management_confs_in_domain(body=None): # noqa: E501
|
|||
|
|
- """delete management configuration items and expected values in the domain
|
|||
|
|
-
|
|||
|
|
- delete management configuration items and expected values in the domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body: domain info
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: BaseResponse
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = ManageConfs.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- # check whether the domain exists
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
-
|
|||
|
|
- # check the input domain
|
|||
|
|
- checkRes = Format.domainCheck(domain)
|
|||
|
|
- if not checkRes:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- isExist = Format.isDomainExist(domain)
|
|||
|
|
- if not isExist:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The current domain does not exist")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # Check whether path is null in advance
|
|||
|
|
- conf_files = body.conf_files
|
|||
|
|
- if len(conf_files) == 0:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "The conf_files path can't be empty")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
- # Conf to record successes and failures
|
|||
|
|
- successConf = []
|
|||
|
|
- failedConf = []
|
|||
|
|
-
|
|||
|
|
- # Check whether path exists in the domain. There are two possible paths :
|
|||
|
|
- # (1)xpath path
|
|||
|
|
- # (2) configuration item
|
|||
|
|
- domain_path = os.path.join(TARGETDIR, domain)
|
|||
|
|
- LOGGER.debug("conf_files is : {}".format(conf_files))
|
|||
|
|
-
|
|||
|
|
- yang_modules = YangModule()
|
|||
|
|
- module_lists = yang_modules.module_list
|
|||
|
|
- if len(module_lists) == 0:
|
|||
|
|
- base_rsp = BaseResponse(400, "The yang module does not exist")
|
|||
|
|
- return base_rsp
|
|||
|
|
-
|
|||
|
|
- file_path_list = yang_modules.getFilePathInModdule(module_lists)
|
|||
|
|
- LOGGER.debug("module_lists is : {}".format(module_lists))
|
|||
|
|
- for conf in conf_files:
|
|||
|
|
- module = yang_modules.getModuleByFilePath(conf.file_path)
|
|||
|
|
- features = yang_modules.getFeatureInModule(module)
|
|||
|
|
- features_path = os.path.join(domain_path, "/".join(features))
|
|||
|
|
- LOGGER.debug("domain_path is : {}".format(domain_path))
|
|||
|
|
-
|
|||
|
|
- if os.path.isfile(features_path):
|
|||
|
|
- LOGGER.debug("it's a normal file")
|
|||
|
|
- try:
|
|||
|
|
- os.remove(features_path)
|
|||
|
|
- except OSError as ex:
|
|||
|
|
- # logging.error("the path remove failed")
|
|||
|
|
- break
|
|||
|
|
- successConf.append(conf.file_path)
|
|||
|
|
- else:
|
|||
|
|
- failedConf.append(conf.file_path)
|
|||
|
|
-
|
|||
|
|
- # git commit message
|
|||
|
|
- if len(successConf) > 0:
|
|||
|
|
- git_tools = GitTools()
|
|||
|
|
- succ_conf = ""
|
|||
|
|
- for d_conf in successConf:
|
|||
|
|
- succ_conf = succ_conf + d_conf + " "
|
|||
|
|
- commit_code = git_tools.gitCommit("delete the conf in {} domian, ".format(domain) +
|
|||
|
|
- "the path including : {}".format(succ_conf))
|
|||
|
|
-
|
|||
|
|
- # Joinin together the returned codenum and codeMessage
|
|||
|
|
- if len(failedConf) == 0:
|
|||
|
|
- codeNum = 200
|
|||
|
|
- codeString = Format.spliceAllSuccString("confs", "delete management conf", successConf)
|
|||
|
|
- else:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- codeString = Format.splicErrorString("confs", "delete management conf", successConf, failedConf)
|
|||
|
|
- codeString += "\n The reason for the failure is: these paths do not exist."
|
|||
|
|
- base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
- # logging.info('delete management conf in {domain}'.format(domain=domain))
|
|||
|
|
-
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def get_management_confs_in_domain(body=None): # noqa: E501
|
|||
|
|
- """get management configuration items and expected values in the domain
|
|||
|
|
-
|
|||
|
|
- get management configuration items and expected values in the domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body: domain info
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: ConfFiles
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = DomainName.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- # Check whether the domain exists
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
-
|
|||
|
|
- # check the input domain
|
|||
|
|
- checkRes = Format.domainCheck(domain)
|
|||
|
|
- if not checkRes:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- isExist = Format.isDomainExist(domain)
|
|||
|
|
- if not isExist:
|
|||
|
|
- base_rsp = BaseResponse(400, "The current domain does not exist")
|
|||
|
|
- return base_rsp, 400
|
|||
|
|
-
|
|||
|
|
- # The parameters of the initial return value assignment
|
|||
|
|
- expected_conf_lists = ConfFiles(domain_name=domain,
|
|||
|
|
- conf_files=[])
|
|||
|
|
-
|
|||
|
|
- # get the path in domain
|
|||
|
|
- domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
-
|
|||
|
|
- # When there is a file path is the path of judgment for the configuration items
|
|||
|
|
- for root, dirs, files in os.walk(domainPath):
|
|||
|
|
- if len(files) > 0 and len(root.split('/')) > 3:
|
|||
|
|
- if "hostRecord.txt" in files:
|
|||
|
|
- continue
|
|||
|
|
- for d_file in files:
|
|||
|
|
- d_file_path = os.path.join(root, d_file)
|
|||
|
|
- contents = Format.get_file_content_by_read(d_file_path)
|
|||
|
|
- feature = os.path.join(root.split('/')[-1], d_file)
|
|||
|
|
- yang_modules = YangModule()
|
|||
|
|
- d_module = yang_modules.getModuleByFeature(feature)
|
|||
|
|
- file_lists = yang_modules.getFilePathInModdule(yang_modules.module_list)
|
|||
|
|
- file_path = file_lists.get(d_module.name()).split(":")[-1]
|
|||
|
|
-
|
|||
|
|
- conf = ConfFile(file_path=file_path, contents=contents)
|
|||
|
|
- expected_conf_lists.conf_files.append(conf)
|
|||
|
|
- LOGGER.debug("expected_conf_lists is :{}".format(expected_conf_lists))
|
|||
|
|
-
|
|||
|
|
- if len(expected_conf_lists.domain_name) > 0:
|
|||
|
|
- base_rsp = BaseResponse(200, "Get management configuration items and expected " +
|
|||
|
|
- "values in the domain succeccfully")
|
|||
|
|
- else:
|
|||
|
|
- base_rsp = BaseResponse(400, "The file is Null in this domain")
|
|||
|
|
-
|
|||
|
|
- return expected_conf_lists
|
|||
|
|
-
|
|||
|
|
-
|
|||
|
|
-def query_changelog_of_management_confs_in_domain(body=None): # noqa: E501
|
|||
|
|
- """query the change log of management config in domain
|
|||
|
|
-
|
|||
|
|
- query the change log of management config in domain # noqa: E501
|
|||
|
|
-
|
|||
|
|
- :param body: domain info
|
|||
|
|
- :type body: dict | bytes
|
|||
|
|
-
|
|||
|
|
- :rtype: ExceptedConfInfo
|
|||
|
|
- """
|
|||
|
|
- if connexion.request.is_json:
|
|||
|
|
- body = ManageConfs.from_dict(connexion.request.get_json()) # noqa: E501
|
|||
|
|
-
|
|||
|
|
- # check whether the domain exists
|
|||
|
|
- domain = body.domain_name
|
|||
|
|
- LOGGER.debug("body is : {}".format(body))
|
|||
|
|
-
|
|||
|
|
- # check the input domain
|
|||
|
|
- checkRes = Format.domainCheck(domain)
|
|||
|
|
- if not checkRes:
|
|||
|
|
- num = 400
|
|||
|
|
- base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
- return base_rsp, num
|
|||
|
|
-
|
|||
|
|
- isExist = Format.isDomainExist(domain)
|
|||
|
|
- if not isExist:
|
|||
|
|
- base_rsp = BaseResponse(400, "The current domain does not exist")
|
|||
|
|
- return base_rsp
|
|||
|
|
-
|
|||
|
|
- # Check whether path is empty in advance. If path is empty, the configuration in the
|
|||
|
|
- # entire domain is queried. Otherwise, the historical records of the specified file are queried.
|
|||
|
|
- conf_files = body.conf_files
|
|||
|
|
- LOGGER.debug("conf_files is : {}".format(conf_files))
|
|||
|
|
- LOGGER.debug("conf_files's type is : {}".format(type(conf_files)))
|
|||
|
|
- conf_files_list = []
|
|||
|
|
- if conf_files:
|
|||
|
|
- for d_conf in conf_files:
|
|||
|
|
- LOGGER.debug("d_conf is : {}".format(d_conf))
|
|||
|
|
- LOGGER.debug("d_conf type is : {}".format(type(d_conf)))
|
|||
|
|
- conf_files_list.append(d_conf.file_path)
|
|||
|
|
- success_conf = []
|
|||
|
|
- failed_conf = []
|
|||
|
|
- domain_path = os.path.join(TARGETDIR, domain)
|
|||
|
|
- expected_conf_lists = ExceptedConfInfo(domain_name=domain,
|
|||
|
|
- conf_base_infos=[])
|
|||
|
|
- yang_modules = YangModule()
|
|||
|
|
- for root, dirs, files in os.walk(domain_path):
|
|||
|
|
- conf_base_infos = []
|
|||
|
|
- if len(files) > 0 and len(root.split('/')) > 3:
|
|||
|
|
- if "hostRecord.txt" in files:
|
|||
|
|
- continue
|
|||
|
|
- confPath = root.split('/', 3)[3]
|
|||
|
|
- for d_file in files:
|
|||
|
|
- feature = os.path.join(root.split('/')[-1], d_file)
|
|||
|
|
- d_module = yang_modules.getModuleByFeature(feature)
|
|||
|
|
- file_lists = yang_modules.getFilePathInModdule(yang_modules.module_list)
|
|||
|
|
- file_path = file_lists.get(d_module.name()).split(":")[-1]
|
|||
|
|
- if (conf_files_list) and (file_path not in conf_files_list):
|
|||
|
|
- continue
|
|||
|
|
- d_file_path = os.path.join(root, d_file)
|
|||
|
|
- expectedValue = Format.get_file_content_by_read(d_file_path)
|
|||
|
|
- git_tools = GitTools()
|
|||
|
|
- gitMessage = git_tools.getLogMessageByPath(d_file_path)
|
|||
|
|
- if gitMessage and expectedValue:
|
|||
|
|
- success_conf.append(file_path)
|
|||
|
|
- else:
|
|||
|
|
- failed_conf.append(file_path)
|
|||
|
|
- conf_base_info = ConfBaseInfo(file_path=file_path,
|
|||
|
|
- expected_contents=expectedValue,
|
|||
|
|
- change_log=gitMessage)
|
|||
|
|
- expected_conf_lists.conf_base_infos.append(conf_base_info)
|
|||
|
|
-
|
|||
|
|
- LOGGER.debug("########################## expetedConfInfo ####################")
|
|||
|
|
- LOGGER.debug("expected_conf_lists is : {}".format(expected_conf_lists))
|
|||
|
|
- LOGGER.debug("########################## expetedConfInfo end ####################")
|
|||
|
|
-
|
|||
|
|
- if len(success_conf) == 0:
|
|||
|
|
- codeNum = 500
|
|||
|
|
- base_rsp = BaseResponse(codeNum, "Faled to uery the changelog of the configure in the domain.")
|
|||
|
|
- return base_rsp, codeNum
|
|||
|
|
- if len(failed_conf) > 0:
|
|||
|
|
- codeNum = 400
|
|||
|
|
- else:
|
|||
|
|
- codeNum = 200
|
|||
|
|
-
|
|||
|
|
- return expected_conf_lists, codeNum
|
|||
|
|
diff --git a/ragdoll/demo/conf_manage.py b/ragdoll/demo/conf_manage.py
|
|||
|
|
index d4f5d05..fb5fd78 100644
|
|||
|
|
--- a/ragdoll/demo/conf_manage.py
|
|||
|
|
+++ b/ragdoll/demo/conf_manage.py
|
|||
|
|
@@ -2,7 +2,6 @@ import requests
|
|||
|
|
import json
|
|||
|
|
|
|||
|
|
from ragdoll.log.log import LOGGER
|
|||
|
|
-from ragdoll.models.domain import Domain
|
|||
|
|
from ragdoll.models.domain_name import DomainName
|
|||
|
|
from ragdoll.models.conf import Conf
|
|||
|
|
from ragdoll.models.confs import Confs
|
|||
|
|
@@ -19,7 +18,7 @@ class ConfManage(object):
|
|||
|
|
contents_list = args.contents
|
|||
|
|
host_id_list = args.host_id
|
|||
|
|
if not domain_name or not file_path_list:
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for conf_add!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
conf_file = []
|
|||
|
|
@@ -32,7 +31,7 @@ class ConfManage(object):
|
|||
|
|
conf = Conf(file_path=file_path_list[i], host_id=host_id_list[i])
|
|||
|
|
conf_file.append(conf)
|
|||
|
|
else:
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error as invlid param!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
data = Confs(domain_name=domain_name, conf_files=conf_file)
|
|||
|
|
@@ -46,7 +45,7 @@ class ConfManage(object):
|
|||
|
|
def conf_query(self, args):
|
|||
|
|
domain_name = args.domain_name
|
|||
|
|
if not domain_name:
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for conf_query!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
data = DomainName(domain_name=domain_name)
|
|||
|
|
@@ -64,7 +63,7 @@ class ConfManage(object):
|
|||
|
|
domain_name = args.domain_name
|
|||
|
|
file_path_list = args.file_path
|
|||
|
|
if not domain_name or not file_path_list:
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for conf_delete!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
conf_files = []
|
|||
|
|
@@ -87,7 +86,7 @@ class ConfManage(object):
|
|||
|
|
domain_name = args.domain_name
|
|||
|
|
file_path_list = args.file_path
|
|||
|
|
if not domain_name or not file_path_list:
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for conf_changelog!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
conf_files = []
|
|||
|
|
diff --git a/ragdoll/demo/conf_sync.py b/ragdoll/demo/conf_sync.py
|
|||
|
|
index 42cefdf..ec293b6 100644
|
|||
|
|
--- a/ragdoll/demo/conf_sync.py
|
|||
|
|
+++ b/ragdoll/demo/conf_sync.py
|
|||
|
|
@@ -2,7 +2,6 @@ import requests
|
|||
|
|
import json
|
|||
|
|
|
|||
|
|
from ragdoll.log.log import LOGGER
|
|||
|
|
-from ragdoll.models.domain import Domain
|
|||
|
|
from ragdoll.models.domain_name import DomainName
|
|||
|
|
from ragdoll.models.conf_host import ConfHost
|
|||
|
|
from ragdoll.demo.conf import server_port
|
|||
|
|
@@ -14,7 +13,7 @@ class ConfSync(object):
|
|||
|
|
domain_name = args.domain_name
|
|||
|
|
host_id_list = args.host_id
|
|||
|
|
if not domain_name or not host_id_list:
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for sync_conf!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
host_ids = []
|
|||
|
|
@@ -34,7 +33,7 @@ class ConfSync(object):
|
|||
|
|
def sync_status(self, args):
|
|||
|
|
domain_name = args.domain_name
|
|||
|
|
if not domain_name:
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for sync_status!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
data = DomainName(domain_name=domain_name)
|
|||
|
|
@@ -52,7 +51,7 @@ class ConfSync(object):
|
|||
|
|
domain_name = args.domain_name
|
|||
|
|
host_id_list = args.host_id
|
|||
|
|
if not domain_name or not host_id_list:
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for query_real_conf!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
host_ids = []
|
|||
|
|
diff --git a/ragdoll/demo/demo_server.py b/ragdoll/demo/demo_server.py
|
|||
|
|
index 9ac6c08..c20a7d9 100644
|
|||
|
|
--- a/ragdoll/demo/demo_server.py
|
|||
|
|
+++ b/ragdoll/demo/demo_server.py
|
|||
|
|
@@ -1,6 +1,5 @@
|
|||
|
|
import connexion
|
|||
|
|
|
|||
|
|
-from ragdoll.models.conf_host import ConfHost
|
|||
|
|
from ragdoll.log.log import LOGGER
|
|||
|
|
|
|||
|
|
def _read_file(path):
|
|||
|
|
@@ -88,4 +87,4 @@ def sync_conf(body=None):
|
|||
|
|
rsp = {"code": 500,
|
|||
|
|
"msg": "Failed to synchronize the configuration. ERROR:{}".format(error_info),
|
|||
|
|
"status": False}
|
|||
|
|
- return rsp
|
|||
|
|
\ No newline at end of file
|
|||
|
|
+ return rsp
|
|||
|
|
diff --git a/ragdoll/demo/domain.py b/ragdoll/demo/domain.py
|
|||
|
|
index 6574fe2..5e65a72 100644
|
|||
|
|
--- a/ragdoll/demo/domain.py
|
|||
|
|
+++ b/ragdoll/demo/domain.py
|
|||
|
|
@@ -12,7 +12,7 @@ class DomainManage(object):
|
|||
|
|
domain_name_list = args.domain_name
|
|||
|
|
priority_list = args.priority
|
|||
|
|
if len(domain_name_list) != len(priority_list):
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for domain_create!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
data = []
|
|||
|
|
diff --git a/ragdoll/demo/host.py b/ragdoll/demo/host.py
|
|||
|
|
index 4f70980..9e4ab49 100644
|
|||
|
|
--- a/ragdoll/demo/host.py
|
|||
|
|
+++ b/ragdoll/demo/host.py
|
|||
|
|
@@ -2,7 +2,6 @@ import requests
|
|||
|
|
import json
|
|||
|
|
|
|||
|
|
from ragdoll.log.log import LOGGER
|
|||
|
|
-from ragdoll.models.domain import Domain
|
|||
|
|
from ragdoll.models.domain_name import DomainName
|
|||
|
|
from ragdoll.models.host import Host
|
|||
|
|
from ragdoll.models.host_infos import HostInfos
|
|||
|
|
@@ -17,7 +16,7 @@ class HostManage(object):
|
|||
|
|
ipv6_list = args.ipv6
|
|||
|
|
|
|||
|
|
if len(host_id_list) != len(ip_list):
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for adding host!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
host_infos = []
|
|||
|
|
@@ -35,7 +34,7 @@ class HostManage(object):
|
|||
|
|
def host_query(self, args):
|
|||
|
|
domain_name = args.domain_name
|
|||
|
|
if not domain_name:
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for qeurying host!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
data = DomainName(domain_name=domain_name)
|
|||
|
|
@@ -44,7 +43,7 @@ class HostManage(object):
|
|||
|
|
response = requests.post(url, data=json.dumps(data, cls=JSONEncoder), headers=headers)
|
|||
|
|
|
|||
|
|
if response.status_code != 200:
|
|||
|
|
- LOGGER.warning(json.loads(response.text).get("msg"))
|
|||
|
|
+ LOGGER.error(json.loads(response.text).get("msg"))
|
|||
|
|
else:
|
|||
|
|
LOGGER.debug("The following host are managed in domain_name:{}.".format(json.loads(response.text)))
|
|||
|
|
return
|
|||
|
|
@@ -56,7 +55,7 @@ class HostManage(object):
|
|||
|
|
ipv6_list = args.ipv6
|
|||
|
|
|
|||
|
|
if len(host_id_list) != len(ip_list):
|
|||
|
|
- LOGGER.error("ERROR: Input error!\n")
|
|||
|
|
+ LOGGER.error("ERROR: Input error for deleting host!\n")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
host_infos = []
|
|||
|
|
@@ -69,4 +68,4 @@ class HostManage(object):
|
|||
|
|
headers = {"Content-Type": "application/json"}
|
|||
|
|
response = requests.delete(url, data=json.dumps(data, cls=JSONEncoder), headers=headers)
|
|||
|
|
LOGGER.debug(json.loads(response.text).get("msg"))
|
|||
|
|
- return
|
|||
|
|
\ No newline at end of file
|
|||
|
|
+ return
|
|||
|
|
diff --git a/ragdoll/domain_conf_manage/__init__.py b/ragdoll/domain_conf_manage/__init__.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..0f38672
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/domain_conf_manage/__init__.py
|
|||
|
|
@@ -0,0 +1,18 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: __init__.py.py
|
|||
|
|
+@Time: 2024/3/8 11:39
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
diff --git a/ragdoll/domain_conf_manage/view.py b/ragdoll/domain_conf_manage/view.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..81015fb
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/domain_conf_manage/view.py
|
|||
|
|
@@ -0,0 +1,601 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: view.py
|
|||
|
|
+@Time: 2024/3/8 11:40
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+import io
|
|||
|
|
+import json
|
|||
|
|
+import os
|
|||
|
|
+
|
|||
|
|
+import connexion
|
|||
|
|
+import requests
|
|||
|
|
+from flask import request
|
|||
|
|
+from vulcanus.restful.resp.state import PARAM_ERROR, SUCCEED, PARTIAL_SUCCEED, SERVER_ERROR
|
|||
|
|
+from vulcanus.restful.response import BaseResponse
|
|||
|
|
+
|
|||
|
|
+from ragdoll.conf.constant import TARGETDIR
|
|||
|
|
+from ragdoll.const.conf_handler_const import DIRECTORY_FILE_PATH_LIST
|
|||
|
|
+from ragdoll.function.verify.domain_conf import AddManagementConfsSchema, \
|
|||
|
|
+ DeleteManagementConfsSchema, GetManagementConfsSchema, QueryChangelogSchema
|
|||
|
|
+from ragdoll.log.log import LOGGER
|
|||
|
|
+from ragdoll.models import ConfFiles, ExceptedConfInfo
|
|||
|
|
+from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
+from ragdoll.utils.format import Format
|
|||
|
|
+from ragdoll.utils.git_tools import GitTools
|
|||
|
|
+from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
+from ragdoll.utils.yang_module import YangModule
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class AddManagementConfsInDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=AddManagementConfsSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """add management configuration items and expected values in the domain
|
|||
|
|
+
|
|||
|
|
+ add management configuration items and expected values in the domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body: domain info
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: BaseResponse
|
|||
|
|
+ """
|
|||
|
|
+ global file_paths, reps
|
|||
|
|
+ accessToken = request.headers.get("access_token")
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ conf_files = params.get("confFiles")
|
|||
|
|
+
|
|||
|
|
+ # check the input domain
|
|||
|
|
+ checkRes = Format.domainCheck(domain)
|
|||
|
|
+ if not checkRes:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="Failed to verify the input parameter, please check the input "
|
|||
|
|
+ "parameters.")
|
|||
|
|
+
|
|||
|
|
+ # check whether the domain exists
|
|||
|
|
+ isExist = Format.isDomainExist(domain)
|
|||
|
|
+ if not isExist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The current domain does not exist, please create the domain "
|
|||
|
|
+ "first.")
|
|||
|
|
+
|
|||
|
|
+ # check whether the conf_files is null
|
|||
|
|
+ if len(conf_files) == 0:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The path of file can't be empty")
|
|||
|
|
+
|
|||
|
|
+ # Check all conf_files and check whether contents is empty. If so, the query actual configuration
|
|||
|
|
+ # interface is called. If not, the conversion is performed directly.
|
|||
|
|
+ # Content and host_id can be set to either content or host_id.
|
|||
|
|
+ # If they are both empty, invalid input is returned.
|
|||
|
|
+ contents_list_null = []
|
|||
|
|
+ contents_list_non_null = []
|
|||
|
|
+ for d_conf in conf_files:
|
|||
|
|
+ if "contents" in d_conf:
|
|||
|
|
+ contents_list_non_null.append(d_conf)
|
|||
|
|
+ elif d_conf["hostId"]:
|
|||
|
|
+ contents_list_null.append(d_conf)
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The input parameters are not compliant, please check the "
|
|||
|
|
+ "input parameters.")
|
|||
|
|
+
|
|||
|
|
+ successConf = []
|
|||
|
|
+ failedConf = []
|
|||
|
|
+ object_parse = ObjectParse()
|
|||
|
|
+ yang_module = YangModule()
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ # Content is not an empty scene and is directly analyed and parsed
|
|||
|
|
+ if len(contents_list_non_null) > 0:
|
|||
|
|
+ for d_conf in contents_list_non_null:
|
|||
|
|
+ if not d_conf["contents"].strip():
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The input parameters are not compliant, please check "
|
|||
|
|
+ "the input parameters.")
|
|||
|
|
+ content_string = object_parse.parse_conf_to_json(d_conf["filePath"], d_conf["contents"])
|
|||
|
|
+ if not content_string or not json.loads(content_string):
|
|||
|
|
+ failedConf.append(d_conf["filePath"])
|
|||
|
|
+ else:
|
|||
|
|
+ # create the file and expected value in domain
|
|||
|
|
+ feature_path = yang_module.get_feature_by_real_path(domain, d_conf["filePath"])
|
|||
|
|
+ result = conf_tools.wirteFileInPath(feature_path, content_string + '\n')
|
|||
|
|
+ if result:
|
|||
|
|
+ successConf.append(d_conf["filePath"])
|
|||
|
|
+ else:
|
|||
|
|
+ failedConf.append(d_conf["filePath"])
|
|||
|
|
+
|
|||
|
|
+ # content is empty
|
|||
|
|
+ if len(contents_list_null) > 0:
|
|||
|
|
+ # get the real conf in host
|
|||
|
|
+ get_real_conf_body = {}
|
|||
|
|
+ get_real_conf_body_info = []
|
|||
|
|
+ LOGGER.debug("contents_list_null is : {}".format(contents_list_null))
|
|||
|
|
+ exist_host = dict()
|
|||
|
|
+ for d_conf in contents_list_null:
|
|||
|
|
+ host_id = int(d_conf["hostId"])
|
|||
|
|
+ if host_id in exist_host:
|
|||
|
|
+ if d_conf["filePath"] not in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ exist_host[host_id].append(d_conf["filePath"])
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum, codeString, file_paths = object_parse.get_directory_files(d_conf, host_id, accessToken)
|
|||
|
|
+ if len(file_paths) == 0:
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+ else:
|
|||
|
|
+ for file_path in file_paths:
|
|||
|
|
+ exist_host[host_id].append(file_path)
|
|||
|
|
+ else:
|
|||
|
|
+ if d_conf["filePath"] not in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ conf_list = list()
|
|||
|
|
+ conf_list.append(d_conf["filePath"])
|
|||
|
|
+ exist_host[host_id] = conf_list
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum, codeString, file_paths = object_parse.get_directory_files(d_conf, host_id, accessToken)
|
|||
|
|
+ if len(file_paths) == 0:
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+ else:
|
|||
|
|
+ exist_host[host_id] = file_paths
|
|||
|
|
+
|
|||
|
|
+ for k, v in exist_host.items():
|
|||
|
|
+ confs = dict()
|
|||
|
|
+ confs["host_id"] = k
|
|||
|
|
+ confs["config_list"] = v
|
|||
|
|
+ get_real_conf_body_info.append(confs)
|
|||
|
|
+
|
|||
|
|
+ get_real_conf_body["infos"] = get_real_conf_body_info
|
|||
|
|
+
|
|||
|
|
+ url = conf_tools.load_url_by_conf().get("collect_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": accessToken}
|
|||
|
|
+ try:
|
|||
|
|
+ response = requests.post(url, data=json.dumps(get_real_conf_body), headers=headers) # post request
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "Failed to obtain the actual configuration, please check the interface of config/collect."
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+ response_code = json.loads(response.text).get("code")
|
|||
|
|
+ if response_code == None:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "Failed to obtain the actual configuration, please check the interface of conf/collect."
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+ if (response_code != "200") and (response_code != "206"):
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "Failed to obtain the actual configuration, please check the file exists."
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+ reps = json.loads(response.text).get("data")
|
|||
|
|
+ if not reps or len(reps) == 0:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "Failed to obtain the actual configuration, please check the host info for conf/collect."
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+ directory_d_file = []
|
|||
|
|
+ directory_d_files = {}
|
|||
|
|
+ for d_res in reps:
|
|||
|
|
+ failedlist = d_res.get("fail_files")
|
|||
|
|
+ if len(failedlist) > 0:
|
|||
|
|
+ for d_failed in failedlist:
|
|||
|
|
+ failedConf.append(d_failed)
|
|||
|
|
+ continue
|
|||
|
|
+ d_res_infos = d_res.get("infos")
|
|||
|
|
+ for d_file in d_res_infos:
|
|||
|
|
+ for dir_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ if str(d_file.get("path")).find(dir_path) == -1:
|
|||
|
|
+ file_path = d_file.get("path")
|
|||
|
|
+ content = d_file.get("content")
|
|||
|
|
+ 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):
|
|||
|
|
+ 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)
|
|||
|
|
+ else:
|
|||
|
|
+ directory_d_file.append(d_file)
|
|||
|
|
+ directory_d_files[dir_path] = directory_d_file
|
|||
|
|
+ if len(directory_d_files) > 0:
|
|||
|
|
+ for dir_path, directory_d_file in directory_d_files.items():
|
|||
|
|
+ content_string = object_parse.parse_dir_conf_to_json(dir_path, directory_d_file)
|
|||
|
|
+ if not content_string or not json.loads(content_string):
|
|||
|
|
+ failedConf.append(dir_path)
|
|||
|
|
+ else:
|
|||
|
|
+ feature_path = yang_module.get_feature_by_real_path(domain, dir_path)
|
|||
|
|
+ result = conf_tools.wirteFileInPath(feature_path, content_string + '\n')
|
|||
|
|
+ if result:
|
|||
|
|
+ successConf.append(dir_path)
|
|||
|
|
+ else:
|
|||
|
|
+ failedConf.append(dir_path)
|
|||
|
|
+ # git commit message
|
|||
|
|
+ if len(successConf) > 0:
|
|||
|
|
+ git_tools = GitTools()
|
|||
|
|
+ succ_conf = ""
|
|||
|
|
+ for d_conf in successConf:
|
|||
|
|
+ succ_conf = succ_conf + d_conf + " "
|
|||
|
|
+ commit_code = git_tools.gitCommit("Add the conf in {} domian, ".format(domain) +
|
|||
|
|
+ "the path including : {}".format(succ_conf))
|
|||
|
|
+
|
|||
|
|
+ # Joinin together the returned codenum and codeMessage
|
|||
|
|
+ LOGGER.debug("successConf is : {}".format(successConf))
|
|||
|
|
+ LOGGER.debug("failedConf is : {}".format(failedConf))
|
|||
|
|
+ if len(successConf) == 0:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "All configurations failed to be added."
|
|||
|
|
+ elif len(failedConf) > 0:
|
|||
|
|
+ codeNum = PARTIAL_SUCCEED
|
|||
|
|
+ codeString = Format.splicErrorString("confs", "add management conf", successConf, failedConf)
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ codeString = Format.spliceAllSuccString("confs", "add management conf", successConf)
|
|||
|
|
+
|
|||
|
|
+ # 根据agith_success_conf 更新agith的配置
|
|||
|
|
+ # 获取domain最新的有哪些配置 [1]
|
|||
|
|
+ conf_files_list = Format.get_conf_files_list(domain, accessToken)
|
|||
|
|
+ if len(conf_files_list) > 0:
|
|||
|
|
+ Format.update_agith(accessToken, conf_files_list, domain)
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class UploadManagementConfsInDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """upload management configuration items and expected values in the domain
|
|||
|
|
+
|
|||
|
|
+ upload management configuration items and expected values in the domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body: file info
|
|||
|
|
+ :type body: FileStorage
|
|||
|
|
+
|
|||
|
|
+ :rtype: BaseResponse
|
|||
|
|
+ """
|
|||
|
|
+ access_token = connexion.request.headers.get("access_token")
|
|||
|
|
+ file = connexion.request.files['file']
|
|||
|
|
+
|
|||
|
|
+ filePath = connexion.request.form.get("filePath")
|
|||
|
|
+ domainName = connexion.request.form.get("domainName")
|
|||
|
|
+ # check the input domainName
|
|||
|
|
+ checkRes = Format.domainCheck(domainName)
|
|||
|
|
+ if not checkRes:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="Failed to verify the input parameter, please check the input "
|
|||
|
|
+ "parameters.")
|
|||
|
|
+
|
|||
|
|
+ # check whether the domainName exists
|
|||
|
|
+ isExist = Format.isDomainExist(domainName)
|
|||
|
|
+ if not isExist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The current domain does not exist, please create the domain "
|
|||
|
|
+ "first.")
|
|||
|
|
+
|
|||
|
|
+ # check whether the file is null
|
|||
|
|
+ if file is None:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The file of conf can't be empty")
|
|||
|
|
+
|
|||
|
|
+ # check whether the conf is null
|
|||
|
|
+ if filePath is None:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The conf body of conf can't be empty")
|
|||
|
|
+
|
|||
|
|
+ successConf = []
|
|||
|
|
+ failedConf = []
|
|||
|
|
+ object_parse = ObjectParse()
|
|||
|
|
+ yang_module = YangModule()
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+
|
|||
|
|
+ # content is file
|
|||
|
|
+ if file:
|
|||
|
|
+ if not filePath.strip():
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The input parameters are not compliant, please check the "
|
|||
|
|
+ "input parameters.")
|
|||
|
|
+ try:
|
|||
|
|
+ file_bytes = file.read()
|
|||
|
|
+ if len(file_bytes) > 1024 * 1024:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The size of the uploaded file must be less than 1MB")
|
|||
|
|
+ byte_stream = io.BytesIO(file_bytes)
|
|||
|
|
+
|
|||
|
|
+ # Read the contents of the byte stream
|
|||
|
|
+ line_content = byte_stream.read().decode("UTF-8")
|
|||
|
|
+ except OSError as err:
|
|||
|
|
+ LOGGER.error("OS error: {}".format(err))
|
|||
|
|
+ codeNum = SERVER_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="OS error: {0}".format(err))
|
|||
|
|
+ except Exception as ex:
|
|||
|
|
+ LOGGER.error("Other error: {}".format(ex))
|
|||
|
|
+ codeNum = SERVER_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="read file error: {0}".format(ex))
|
|||
|
|
+
|
|||
|
|
+ content_string = object_parse.parse_conf_to_json(filePath, line_content)
|
|||
|
|
+ if not content_string or not json.loads(content_string):
|
|||
|
|
+ failedConf.append(filePath)
|
|||
|
|
+ else:
|
|||
|
|
+ # create the file and expected value in domain
|
|||
|
|
+ feature_path = yang_module.get_feature_by_real_path(domainName, filePath)
|
|||
|
|
+ result = conf_tools.wirteFileInPath(feature_path, content_string + '\n')
|
|||
|
|
+ if result:
|
|||
|
|
+ successConf.append(filePath)
|
|||
|
|
+ else:
|
|||
|
|
+ failedConf.append(filePath)
|
|||
|
|
+
|
|||
|
|
+ # git commit message
|
|||
|
|
+ if len(successConf) > 0:
|
|||
|
|
+ git_tools = GitTools()
|
|||
|
|
+ succ_conf = ""
|
|||
|
|
+ for d_conf in successConf:
|
|||
|
|
+ succ_conf = succ_conf + d_conf + " "
|
|||
|
|
+ commit_code = git_tools.gitCommit("Add the conf in {} domian, ".format(domainName) +
|
|||
|
|
+ "the path including : {}".format(succ_conf))
|
|||
|
|
+
|
|||
|
|
+ # Joinin together the returned codenum and codeMessage
|
|||
|
|
+ LOGGER.debug("successConf is : {}".format(successConf))
|
|||
|
|
+ LOGGER.debug("failedConf is : {}".format(failedConf))
|
|||
|
|
+ if len(successConf) == 0:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = "All configurations failed to be added."
|
|||
|
|
+ elif len(failedConf) > 0:
|
|||
|
|
+ codeNum = PARTIAL_SUCCEED
|
|||
|
|
+ codeString = Format.splicErrorString("confs", "add management conf", successConf, failedConf)
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ codeString = Format.spliceAllSuccString("confs", "add management conf", successConf)
|
|||
|
|
+
|
|||
|
|
+ # 获取domain最新的有哪些配置 [1]
|
|||
|
|
+ conf_files_list = Format.get_conf_files_list(domainName, access_token)
|
|||
|
|
+ if len(conf_files_list) > 0:
|
|||
|
|
+ Format.update_agith(access_token, conf_files_list, domainName)
|
|||
|
|
+
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DeleteManagementConfsInDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=DeleteManagementConfsSchema, token=True)
|
|||
|
|
+ def delete(self, **params):
|
|||
|
|
+ """delete management configuration items and expected values in the domain
|
|||
|
|
+
|
|||
|
|
+ delete management configuration items and expected values in the domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body: domain info
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: BaseResponse
|
|||
|
|
+ """
|
|||
|
|
+ access_token = connexion.request.headers.get("access_token")
|
|||
|
|
+ # check whether the domain exists
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+
|
|||
|
|
+ # check the input domain
|
|||
|
|
+ checkRes = Format.domainCheck(domain)
|
|||
|
|
+ if not checkRes:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="Failed to verify the input parameter, please check the input "
|
|||
|
|
+ "parameters.")
|
|||
|
|
+
|
|||
|
|
+ isExist = Format.isDomainExist(domain)
|
|||
|
|
+ if not isExist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The current domain does not exist")
|
|||
|
|
+
|
|||
|
|
+ # Check whether path is null in advance
|
|||
|
|
+ conf_files = params.get("confFiles")
|
|||
|
|
+ if len(conf_files) == 0:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The conf_files path can't be empty")
|
|||
|
|
+
|
|||
|
|
+ # Conf to record successes and failures
|
|||
|
|
+ successConf = []
|
|||
|
|
+ failedConf = []
|
|||
|
|
+
|
|||
|
|
+ # Check whether path exists in the domain. There are two possible paths :
|
|||
|
|
+ # (1)xpath path
|
|||
|
|
+ # (2) configuration item
|
|||
|
|
+ domain_path = os.path.join(TARGETDIR, domain)
|
|||
|
|
+ LOGGER.debug("conf_files is : {}".format(conf_files))
|
|||
|
|
+
|
|||
|
|
+ yang_modules = YangModule()
|
|||
|
|
+ module_lists = yang_modules.module_list
|
|||
|
|
+ if len(module_lists) == 0:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The yang module does not exist")
|
|||
|
|
+
|
|||
|
|
+ LOGGER.debug("module_lists is : {}".format(module_lists))
|
|||
|
|
+ for conf in conf_files:
|
|||
|
|
+ module = yang_modules.getModuleByFilePath(conf["filePath"])
|
|||
|
|
+ features = yang_modules.getFeatureInModule(module)
|
|||
|
|
+ features_path = os.path.join(domain_path, "/".join(features))
|
|||
|
|
+
|
|||
|
|
+ if os.path.isfile(features_path):
|
|||
|
|
+ LOGGER.info("It's a normal file : {}".format(features_path))
|
|||
|
|
+ try:
|
|||
|
|
+ os.remove(features_path)
|
|||
|
|
+ except OSError as ex:
|
|||
|
|
+ LOGGER.error("Failed to remove path, as OSError: {}".format(str(ex)))
|
|||
|
|
+ break
|
|||
|
|
+ successConf.append(conf["filePath"])
|
|||
|
|
+ else:
|
|||
|
|
+ LOGGER.error("It's a not normal file : {}".format(features_path))
|
|||
|
|
+ failedConf.append(conf["filePath"])
|
|||
|
|
+
|
|||
|
|
+ # git commit message
|
|||
|
|
+ if len(successConf) > 0:
|
|||
|
|
+ git_tools = GitTools()
|
|||
|
|
+ succ_conf = ""
|
|||
|
|
+ for d_conf in successConf:
|
|||
|
|
+ succ_conf = succ_conf + d_conf + " "
|
|||
|
|
+ commit_code = git_tools.gitCommit("delete the conf in {} domian, ".format(domain) +
|
|||
|
|
+ "the path including : {}".format(succ_conf))
|
|||
|
|
+
|
|||
|
|
+ # Joinin together the returned codenum and codeMessage
|
|||
|
|
+ if len(failedConf) == 0:
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ codeString = Format.spliceAllSuccString("confs", "delete management conf", successConf)
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = Format.splicErrorString("confs", "delete management conf", successConf, failedConf)
|
|||
|
|
+ codeString += "\n The reason for the failure is: these paths do not exist."
|
|||
|
|
+
|
|||
|
|
+ # 获取domain最新的有哪些配置 [1]
|
|||
|
|
+ conf_files_list = Format.get_conf_files_list(domain, access_token)
|
|||
|
|
+ Format.update_agith(access_token, conf_files_list, domain)
|
|||
|
|
+
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class GetManagementConfsInDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=GetManagementConfsSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """get management configuration items and expected values in the domain
|
|||
|
|
+
|
|||
|
|
+ get management configuration items and expected values in the domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body: domain info
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: ConfFiles
|
|||
|
|
+ """
|
|||
|
|
+ # Check whether the domain exists
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+
|
|||
|
|
+ # check the input domain
|
|||
|
|
+ checkRes = Format.domainCheck(domain)
|
|||
|
|
+ if not checkRes:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="Failed to verify the input parameter, please check the input "
|
|||
|
|
+ "parameters.")
|
|||
|
|
+
|
|||
|
|
+ isExist = Format.isDomainExist(domain)
|
|||
|
|
+ if not isExist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The current domain does not exist")
|
|||
|
|
+
|
|||
|
|
+ # The parameters of the initial return value assignment
|
|||
|
|
+ # expected_conf_lists = ConfFiles(domain_name=domain, conf_files=[])
|
|||
|
|
+ expected_conf_lists = {"domainName": domain, "confFiles": []}
|
|||
|
|
+
|
|||
|
|
+ # get the path in domain
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
+
|
|||
|
|
+ # When there is a file path is the path of judgment for the configuration items
|
|||
|
|
+ for root, dirs, files in os.walk(domainPath):
|
|||
|
|
+ if len(files) > 0 and len(root.split('/')) > 3:
|
|||
|
|
+ if "hostRecord.txt" in files:
|
|||
|
|
+ continue
|
|||
|
|
+ for d_file in files:
|
|||
|
|
+ d_file_path = os.path.join(root, d_file)
|
|||
|
|
+ contents = Format.get_file_content_by_read(d_file_path)
|
|||
|
|
+ feature = os.path.join(root.split('/')[-1], d_file)
|
|||
|
|
+ yang_modules = YangModule()
|
|||
|
|
+ d_module = yang_modules.getModuleByFeature(feature)
|
|||
|
|
+ file_lists = yang_modules.getFilePathInModdule(yang_modules.module_list)
|
|||
|
|
+ file_path = file_lists.get(d_module.name()).split(":")[-1]
|
|||
|
|
+
|
|||
|
|
+ # conf = ConfFile(file_path=file_path, contents=contents)
|
|||
|
|
+ conf = {"filePath": file_path, "contents": contents}
|
|||
|
|
+ # expected_conf_lists.conf_files.append(conf)
|
|||
|
|
+ expected_conf_lists.get("confFiles").append(conf)
|
|||
|
|
+ LOGGER.debug("expected_conf_lists is :{}".format(expected_conf_lists))
|
|||
|
|
+
|
|||
|
|
+ if len(expected_conf_lists.get("domainName")) > 0:
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ codeString = "Get management configuration items and expected values in the domain successfully"
|
|||
|
|
+ return self.response(code=codeNum, message=codeString, data=expected_conf_lists)
|
|||
|
|
+
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The file is Null in this domain")
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class QueryChangelogOfManagementConfsInDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=QueryChangelogSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """query the change log of management config in domain
|
|||
|
|
+
|
|||
|
|
+ query the change log of management config in domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body: domain info
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: ExceptedConfInfo
|
|||
|
|
+ """
|
|||
|
|
+ # check whether the domain exists
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ LOGGER.debug("Query changelog of conf body is : {}".format(params))
|
|||
|
|
+
|
|||
|
|
+ # check the input domain
|
|||
|
|
+ checkRes = Format.domainCheck(domain)
|
|||
|
|
+ if not checkRes:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="Failed to verify the input parameter, please check the input "
|
|||
|
|
+ "parameters.")
|
|||
|
|
+
|
|||
|
|
+ isExist = Format.isDomainExist(domain)
|
|||
|
|
+ if not isExist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The current domain does not exist")
|
|||
|
|
+
|
|||
|
|
+ # Check whether path is empty in advance. If path is empty, the configuration in the
|
|||
|
|
+ # entire domain is queried. Otherwise, the historical records of the specified file are queried.
|
|||
|
|
+ conf_files = params.get("confFiles")
|
|||
|
|
+ LOGGER.debug("conf_files is : {}".format(conf_files))
|
|||
|
|
+ conf_files_list = []
|
|||
|
|
+ if conf_files:
|
|||
|
|
+ for d_conf in conf_files:
|
|||
|
|
+ LOGGER.debug("d_conf is : {}".format(d_conf))
|
|||
|
|
+ conf_files_list.append(d_conf["filePath"])
|
|||
|
|
+ success_conf = []
|
|||
|
|
+ failed_conf = []
|
|||
|
|
+ domain_path = os.path.join(TARGETDIR, domain)
|
|||
|
|
+ # expected_conf_lists = ExceptedConfInfo(domain_name=domain, conf_base_infos=[])
|
|||
|
|
+ expected_conf_lists = {"domainName": domain, "confBaseInfos": []}
|
|||
|
|
+ yang_modules = YangModule()
|
|||
|
|
+ for root, dirs, files in os.walk(domain_path):
|
|||
|
|
+ if len(files) > 0 and len(root.split('/')) > 3:
|
|||
|
|
+ if "hostRecord.txt" in files:
|
|||
|
|
+ continue
|
|||
|
|
+ for d_file in files:
|
|||
|
|
+ feature = os.path.join(root.split('/')[-1], d_file)
|
|||
|
|
+ d_module = yang_modules.getModuleByFeature(feature)
|
|||
|
|
+ file_lists = yang_modules.getFilePathInModdule(yang_modules.module_list)
|
|||
|
|
+ file_path = file_lists.get(d_module.name()).split(":")[-1]
|
|||
|
|
+ if conf_files_list and (file_path not in conf_files_list):
|
|||
|
|
+ continue
|
|||
|
|
+ d_file_path = os.path.join(root, d_file)
|
|||
|
|
+ expectedValue = Format.get_file_content_by_read(d_file_path)
|
|||
|
|
+ git_tools = GitTools()
|
|||
|
|
+ gitMessage = git_tools.getLogMessageByPath(d_file_path)
|
|||
|
|
+ if gitMessage and expectedValue:
|
|||
|
|
+ success_conf.append(file_path)
|
|||
|
|
+ else:
|
|||
|
|
+ failed_conf.append(file_path)
|
|||
|
|
+
|
|||
|
|
+ conf_base_info = {"filePath": file_path, "expectedContents": expectedValue, "changeLog": gitMessage}
|
|||
|
|
+ expected_conf_lists.get("confBaseInfos").append(conf_base_info)
|
|||
|
|
+
|
|||
|
|
+ LOGGER.debug("expected_conf_lists is : {}".format(expected_conf_lists))
|
|||
|
|
+
|
|||
|
|
+ if len(success_conf) == 0:
|
|||
|
|
+ codeNum = SERVER_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="Failed to query the changelog of the configure in the domain.")
|
|||
|
|
+ if len(failed_conf) > 0:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+
|
|||
|
|
+ return self.response(code=codeNum, message="Succeed to query the changelog of the configure in the domain.",
|
|||
|
|
+ data=expected_conf_lists)
|
|||
|
|
diff --git a/ragdoll/domain_manage/__init__.py b/ragdoll/domain_manage/__init__.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..f0935bb
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/domain_manage/__init__.py
|
|||
|
|
@@ -0,0 +1,18 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: __init__.py.py
|
|||
|
|
+@Time: 2024/3/4 10:34
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
diff --git a/ragdoll/domain_manage/view.py b/ragdoll/domain_manage/view.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..dd507cb
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/domain_manage/view.py
|
|||
|
|
@@ -0,0 +1,135 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: view.py
|
|||
|
|
+@Time: 2024/3/4 10:34
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+import os
|
|||
|
|
+import shutil
|
|||
|
|
+
|
|||
|
|
+import connexion
|
|||
|
|
+from vulcanus.restful.resp.state import SUCCEED, SERVER_ERROR, PARAM_ERROR
|
|||
|
|
+from vulcanus.restful.response import BaseResponse
|
|||
|
|
+
|
|||
|
|
+from ragdoll.conf.constant import TARGETDIR
|
|||
|
|
+from ragdoll.function.verify.domain import CreateDomainSchema, DeleteDomainSchema
|
|||
|
|
+from ragdoll.utils.format import Format
|
|||
|
|
+from ragdoll.utils.git_tools import GitTools
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class CreateDomain(BaseResponse):
|
|||
|
|
+
|
|||
|
|
+ @BaseResponse.handle(schema=CreateDomainSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """
|
|||
|
|
+ create domain
|
|||
|
|
+
|
|||
|
|
+ Args:
|
|||
|
|
+ args (dict): e.g
|
|||
|
|
+ {
|
|||
|
|
+ "domainName":"xxx",
|
|||
|
|
+ "priority":""
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ Returns:
|
|||
|
|
+ dict: response body
|
|||
|
|
+ """
|
|||
|
|
+ successDomain = []
|
|||
|
|
+ failedDomain = []
|
|||
|
|
+ tempDomainName = params.get("domainName")
|
|||
|
|
+ checkRes = Format.domainCheck(tempDomainName)
|
|||
|
|
+ isExist = Format.isDomainExist(tempDomainName)
|
|||
|
|
+
|
|||
|
|
+ if isExist or not checkRes:
|
|||
|
|
+ failedDomain.append(tempDomainName)
|
|||
|
|
+ else:
|
|||
|
|
+ successDomain.append(tempDomainName)
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, tempDomainName)
|
|||
|
|
+ os.umask(0o077)
|
|||
|
|
+ os.mkdir(domainPath)
|
|||
|
|
+
|
|||
|
|
+ codeString = ""
|
|||
|
|
+ if len(failedDomain) == 0:
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ codeString = Format.spliceAllSuccString("domain", "created", successDomain)
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = SERVER_ERROR
|
|||
|
|
+ if params:
|
|||
|
|
+ if isExist:
|
|||
|
|
+ codeString = "domain {} create failed because it has been existed.".format(failedDomain[0])
|
|||
|
|
+ elif not checkRes:
|
|||
|
|
+ codeString = "domain {} create failed because format is incorrect.".format(failedDomain[0])
|
|||
|
|
+ else:
|
|||
|
|
+ codeString = Format.splicErrorString("domain", "created", successDomain, failedDomain)
|
|||
|
|
+
|
|||
|
|
+ # 对successDomain成功的domain添加文件监控开关、告警开关
|
|||
|
|
+ Format.add_domain_conf_trace_flag(params, successDomain, tempDomainName)
|
|||
|
|
+
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DeleteDomain(BaseResponse):
|
|||
|
|
+
|
|||
|
|
+ @BaseResponse.handle(schema=DeleteDomainSchema, token=True)
|
|||
|
|
+ def delete(self, **params):
|
|||
|
|
+ access_token = connexion.request.headers.get("access_token")
|
|||
|
|
+ domainName = params.get("domainName")
|
|||
|
|
+
|
|||
|
|
+ if not domainName:
|
|||
|
|
+ codeString = "The entered domain is empty"
|
|||
|
|
+ return self.response(code=PARAM_ERROR, message=codeString)
|
|||
|
|
+ # 1.清理agith
|
|||
|
|
+ # 获取domain下的host ids
|
|||
|
|
+ host_ids = Format.get_hostid_list_by_domain(domainName)
|
|||
|
|
+ if len(host_ids) > 0:
|
|||
|
|
+ Format.uninstall_trace(access_token, host_ids, domainName)
|
|||
|
|
+ successDomain = []
|
|||
|
|
+ failedDomain = []
|
|||
|
|
+
|
|||
|
|
+ checkRes = Format.domainCheck(domainName)
|
|||
|
|
+ isExist = Format.isDomainExist(domainName)
|
|||
|
|
+ if checkRes and isExist:
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, domainName)
|
|||
|
|
+ successDomain.append(domainName)
|
|||
|
|
+ shutil.rmtree(domainPath)
|
|||
|
|
+ else:
|
|||
|
|
+ failedDomain.append(domainName)
|
|||
|
|
+
|
|||
|
|
+ if len(failedDomain) == 0:
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ codeString = Format.spliceAllSuccString("domain", "delete", successDomain)
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = SERVER_ERROR
|
|||
|
|
+ codeString = Format.splicErrorString("domain", "delete", successDomain, failedDomain)
|
|||
|
|
+
|
|||
|
|
+ # 删除业务域,对successDomain成功的业务域进行redis的key值清理,以及domain下的主机进行agith的清理
|
|||
|
|
+ Format.clear_all_domain_data(access_token, domainName, successDomain, host_ids)
|
|||
|
|
+
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class QueryDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ domain_list = []
|
|||
|
|
+ cmd = "ls {}".format(TARGETDIR)
|
|||
|
|
+ gitTools = GitTools()
|
|||
|
|
+ ls_res = gitTools.run_shell_return_output(cmd).decode()
|
|||
|
|
+ ll_list = ls_res.split('\n')
|
|||
|
|
+ for d_ll in ll_list:
|
|||
|
|
+ if d_ll:
|
|||
|
|
+ domain = {"domainName": d_ll}
|
|||
|
|
+ domain_list.append(domain)
|
|||
|
|
+ return self.response(code=SUCCEED, data=domain_list)
|
|||
|
|
diff --git a/ragdoll/function/__init__.py b/ragdoll/function/__init__.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..1a316c9
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/function/__init__.py
|
|||
|
|
@@ -0,0 +1,18 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: __init__.py.py
|
|||
|
|
+@Time: 2024/3/4 10:48
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
diff --git a/ragdoll/function/verify/__init__.py b/ragdoll/function/verify/__init__.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..1a316c9
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/function/verify/__init__.py
|
|||
|
|
@@ -0,0 +1,18 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: __init__.py.py
|
|||
|
|
+@Time: 2024/3/4 10:48
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
diff --git a/ragdoll/function/verify/confs.py b/ragdoll/function/verify/confs.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..654ede4
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/function/verify/confs.py
|
|||
|
|
@@ -0,0 +1,82 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: confs.py
|
|||
|
|
+@Time: 2024/3/11 9:02
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+from marshmallow import Schema, fields
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DomainNameSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class HostIdSchema(Schema):
|
|||
|
|
+ hostId = fields.Integer(required=True, validate=lambda s: s >= 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class SyncHostConfsSchema(Schema):
|
|||
|
|
+ hostId = fields.Integer(required=True, validate=lambda s: s >= 0)
|
|||
|
|
+ syncConfigs = fields.List(fields.String(required=True, validate=lambda s: len(s) > 0), required=True,
|
|||
|
|
+ validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class ConfBaseSchema(Schema):
|
|||
|
|
+ filePath = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ expectedContents = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DomainConfBaseInfosSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ confBaseInfos = fields.List(fields.Nested(ConfBaseSchema(), required=True), required=True,
|
|||
|
|
+ validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class GetSyncStatusSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ ip = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class QueryExceptedConfsSchema(Schema):
|
|||
|
|
+ domainNames = fields.List(fields.Nested(DomainNameSchema(), required=True), required=True,
|
|||
|
|
+ validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class QueryRealConfsSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ hostIds = fields.List(fields.Nested(HostIdSchema(), required=True), required=True,
|
|||
|
|
+ validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class SyncConfToHostFromDomainSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ syncList = fields.List(fields.Nested(SyncHostConfsSchema(), required=True), required=True,
|
|||
|
|
+ validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class QuerySupportedConfsSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class CompareConfDiffSchema(Schema):
|
|||
|
|
+ expectedConfsResp = fields.List(fields.Nested(DomainConfBaseInfosSchema(), required=True), required=True,
|
|||
|
|
+ validate=lambda s: len(s) > 0)
|
|||
|
|
+ domainResult = fields.Dict(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class BatchSyncConfToHostFromDomainSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ hostIds = fields.List(fields.Integer(required=True, validate=lambda s: s >= 0), required=True,
|
|||
|
|
+ validate=lambda s: len(s) > 0)
|
|||
|
|
diff --git a/ragdoll/function/verify/domain.py b/ragdoll/function/verify/domain.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..c4c1323
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/function/verify/domain.py
|
|||
|
|
@@ -0,0 +1,36 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (C) 2023 isoftstone Technologies Co., Ltd. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: domain.py
|
|||
|
|
+@Time: 2024/3/4 10:48
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+from marshmallow import Schema, fields, validate
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class CreateDomainSchema(Schema):
|
|||
|
|
+ """
|
|||
|
|
+ validators for parameter of /domain/createDomain
|
|||
|
|
+ """
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ priority = fields.Integer(required=True, validate=lambda s: s >= 0)
|
|||
|
|
+ conf_change_flag = fields.Boolean(required=True, default=False, validate=validate.OneOf([True, False]))
|
|||
|
|
+ report_flag = fields.Boolean(required=True, default=False, validate=validate.OneOf([True, False]))
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DeleteDomainSchema(Schema):
|
|||
|
|
+ """
|
|||
|
|
+ validators for parameter of /domain/deleteDomain
|
|||
|
|
+ """
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
diff --git a/ragdoll/function/verify/domain_conf.py b/ragdoll/function/verify/domain_conf.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..698772b
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/function/verify/domain_conf.py
|
|||
|
|
@@ -0,0 +1,53 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: domain_conf.py
|
|||
|
|
+@Time: 2024/3/8 11:40
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+from marshmallow import Schema, fields
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class ConfSchema(Schema):
|
|||
|
|
+ filePath = fields.String(required=False, validate=lambda s: len(s) > 0)
|
|||
|
|
+ contents = fields.String(required=False, validate=lambda s: len(s) > 0)
|
|||
|
|
+ hostId = fields.Integer(required=False, validate=lambda s: s >= 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class ManageConfSchema(Schema):
|
|||
|
|
+ filePath = fields.String(required=False, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class AddManagementConfsSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ confFiles = fields.List(fields.Nested(ConfSchema(), required=True), required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class UploadManagementConfsSchema(Schema):
|
|||
|
|
+ filePath = fields.String(required=False, validate=lambda s: len(s) > 0)
|
|||
|
|
+ domainName = fields.String(required=False, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DeleteManagementConfsSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=False, validate=lambda s: len(s) > 0)
|
|||
|
|
+ confFiles = fields.List(fields.Nested(ManageConfSchema(), required=True), required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class GetManagementConfsSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=False, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class QueryChangelogSchema(Schema):
|
|||
|
|
+ domainName = fields.String(required=False, validate=lambda s: len(s) > 0)
|
|||
|
|
+ confFiles = fields.List(fields.Nested(ManageConfSchema(), required=True), required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
diff --git a/ragdoll/function/verify/host.py b/ragdoll/function/verify/host.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..1ea927c
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/function/verify/host.py
|
|||
|
|
@@ -0,0 +1,58 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: host.py
|
|||
|
|
+@Time: 2024/3/5 9:44
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+from marshmallow import Schema, fields
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class HostSchema(Schema):
|
|||
|
|
+ """
|
|||
|
|
+ validators for parameter of host
|
|||
|
|
+ """
|
|||
|
|
+ hostId = fields.Integer(required=True, validate=lambda s: s >= 0)
|
|||
|
|
+ ip = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ ipv6 = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class SingleDeleteHostSchema(Schema):
|
|||
|
|
+ """
|
|||
|
|
+ validators for parameter of /host/deleteHost
|
|||
|
|
+ """
|
|||
|
|
+ hostId = fields.Integer(required=True, validate=lambda s: s >= 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class AddHostSchema(Schema):
|
|||
|
|
+ """
|
|||
|
|
+ validators for parameter of /host/addHost
|
|||
|
|
+ """
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ hostInfos = fields.List(fields.Nested(HostSchema(), required=True), required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DeleteHostSchema(Schema):
|
|||
|
|
+ """
|
|||
|
|
+ validators for parameter of /host/deleteHost
|
|||
|
|
+ """
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+ hostInfos = fields.List(fields.Nested(SingleDeleteHostSchema(), required=True), required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class GetHostSchema(Schema):
|
|||
|
|
+ """
|
|||
|
|
+ validators for parameter of /host/getHost
|
|||
|
|
+ """
|
|||
|
|
+ domainName = fields.String(required=True, validate=lambda s: len(s) > 0)
|
|||
|
|
\ No newline at end of file
|
|||
|
|
diff --git a/ragdoll/host_manage/__init__.py b/ragdoll/host_manage/__init__.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..ec92ca5
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/host_manage/__init__.py
|
|||
|
|
@@ -0,0 +1,18 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: __init__.py.py
|
|||
|
|
+@Time: 2024/3/5 9:39
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
diff --git a/ragdoll/host_manage/view.py b/ragdoll/host_manage/view.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..ead42f7
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/host_manage/view.py
|
|||
|
|
@@ -0,0 +1,292 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: view.py
|
|||
|
|
+@Time: 2024/3/5 9:40
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+import ast
|
|||
|
|
+import json
|
|||
|
|
+import os
|
|||
|
|
+
|
|||
|
|
+import connexion
|
|||
|
|
+from vulcanus.restful.resp.state import PARAM_ERROR, SUCCEED, PARTIAL_SUCCEED, SERVER_ERROR
|
|||
|
|
+from vulcanus.restful.response import BaseResponse
|
|||
|
|
+
|
|||
|
|
+from ragdoll.conf.constant import TARGETDIR
|
|||
|
|
+from ragdoll.function.verify.host import AddHostSchema, DeleteHostSchema, GetHostSchema
|
|||
|
|
+from ragdoll.log.log import LOGGER
|
|||
|
|
+from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
+from ragdoll.utils.format import Format
|
|||
|
|
+from ragdoll.utils.git_tools import GitTools
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class AddHostInDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=AddHostSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """
|
|||
|
|
+ add host in the configuration domain
|
|||
|
|
+
|
|||
|
|
+ add host in the configuration domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body: domain info
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: BaseResponse
|
|||
|
|
+ """
|
|||
|
|
+ access_token = connexion.request.headers.get("access_token")
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ host_infos = params.get("hostInfos")
|
|||
|
|
+
|
|||
|
|
+ # check whether host_infos is empty
|
|||
|
|
+ if len(host_infos) == 0:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="Enter host info cannot be empty, please check the host info.")
|
|||
|
|
+
|
|||
|
|
+ checkRes = Format.domainCheck(domain)
|
|||
|
|
+ if not checkRes:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
+
|
|||
|
|
+ # check whether the domain exists
|
|||
|
|
+ isExist = Format.isDomainExist(domain)
|
|||
|
|
+ if not isExist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="The current domain does not exist, please create the domain first.")
|
|||
|
|
+
|
|||
|
|
+ successHost = []
|
|||
|
|
+ failedHost = []
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
+ # 将domainName 和host信息入库
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ Format.addHostSyncStatus(conf_tools, domain, host_infos)
|
|||
|
|
+
|
|||
|
|
+ # Check whether the current host exists in the domain.
|
|||
|
|
+ for host in host_infos:
|
|||
|
|
+ # 判断这个hostId是否在其他业务域中
|
|||
|
|
+ contained_flag = Format.isContainedHostIdInOtherDomain(host.get("hostId"))
|
|||
|
|
+ if contained_flag:
|
|||
|
|
+ failedHost.append(host.get("hostId"))
|
|||
|
|
+ else:
|
|||
|
|
+ hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
+ if os.path.isfile(hostPath):
|
|||
|
|
+ isContained = Format.isContainedHostIdInfile(hostPath, host.get("hostId"))
|
|||
|
|
+ if isContained:
|
|||
|
|
+ failedHost.append(host.get("hostId"))
|
|||
|
|
+ else:
|
|||
|
|
+ Format.addHostToFile(hostPath, host)
|
|||
|
|
+ successHost.append(host.get("hostId"))
|
|||
|
|
+ else:
|
|||
|
|
+ Format.addHostToFile(hostPath, host)
|
|||
|
|
+ successHost.append(host.get("hostId"))
|
|||
|
|
+
|
|||
|
|
+ if len(failedHost) == len(host_infos):
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="The all host already exists in the administrative scope of the domain.")
|
|||
|
|
+
|
|||
|
|
+ # Joining together the returned codenum codeMessage
|
|||
|
|
+ if len(failedHost) == 0:
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ codeString = Format.spliceAllSuccString("host", "add hosts", successHost)
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = PARTIAL_SUCCEED
|
|||
|
|
+ codeString = Format.splicErrorString("host", "add hosts", successHost, failedHost)
|
|||
|
|
+
|
|||
|
|
+ # git commit maessage
|
|||
|
|
+ if len(host_infos) > 0:
|
|||
|
|
+ git_tools = GitTools()
|
|||
|
|
+ commit_code = git_tools.gitCommit("Add the host in {} domian, ".format(domain) +
|
|||
|
|
+ "the host including : {}".format(successHost))
|
|||
|
|
+
|
|||
|
|
+ # 针对successHost 添加成功的host, 安装agith并启动agith,如果当前业务域有配置,配置agith,如果没有就不配置
|
|||
|
|
+ Format.install_update_agith(access_token, domain, successHost)
|
|||
|
|
+
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DeleteHostInDomain(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=DeleteHostSchema, token=True)
|
|||
|
|
+ def delete(self, **params):
|
|||
|
|
+ """delete host in the configuration domain
|
|||
|
|
+
|
|||
|
|
+ delete the host in the configuration domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body: domain info
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: BaseResponse
|
|||
|
|
+ """
|
|||
|
|
+ access_token = connexion.request.headers.get("access_token")
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ hostInfos = params.get("hostInfos")
|
|||
|
|
+
|
|||
|
|
+ # check the input domain
|
|||
|
|
+ checkRes = Format.domainCheck(domain)
|
|||
|
|
+ if not checkRes:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
+
|
|||
|
|
+ # check whether the domain exists
|
|||
|
|
+ isExist = Format.isDomainExist(domain)
|
|||
|
|
+ if not isExist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="The current domain does not exist, please create the domain first.")
|
|||
|
|
+
|
|||
|
|
+ # 将host sync status从库中删除
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ Format.deleteHostSyncStatus(conf_tools, domain, hostInfos)
|
|||
|
|
+
|
|||
|
|
+ # Whether the host information added within the current domain is empty while ain exists
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
+ hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
+ if not os.path.isfile(hostPath) or (os.path.isfile(hostPath) and os.stat(hostPath).st_size == 0):
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="The host information is not set in the current domain. Please add the host "
|
|||
|
|
+ "information first")
|
|||
|
|
+
|
|||
|
|
+ # If the input host information is empty, the host information of the whole domain is cleared
|
|||
|
|
+ if len(hostInfos) == 0:
|
|||
|
|
+ if os.path.isfile(hostPath):
|
|||
|
|
+ try:
|
|||
|
|
+ os.remove(hostPath)
|
|||
|
|
+ except OSError as ex:
|
|||
|
|
+ LOGGER.error("Failed to delete hostpath as OS error: {0}".format(ex))
|
|||
|
|
+ codeNum = SERVER_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="The host delete failed.")
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ return self.response(code=codeNum, message="All hosts are deleted in the current domain.")
|
|||
|
|
+
|
|||
|
|
+ # If the domain exists, check whether the current input parameter host belongs to the corresponding
|
|||
|
|
+ # domain. If the host is in the domain, the host is deleted. If the host is no longer in the domain,
|
|||
|
|
+ # the host is added to the failure range
|
|||
|
|
+ containedInHost = []
|
|||
|
|
+ notContainedInHost = []
|
|||
|
|
+ os.umask(0o077)
|
|||
|
|
+ for hostInfo in hostInfos:
|
|||
|
|
+ hostId = hostInfo.get("hostId")
|
|||
|
|
+ isContained = False
|
|||
|
|
+ try:
|
|||
|
|
+ with open(hostPath, 'r') as d_file:
|
|||
|
|
+ lines = d_file.readlines()
|
|||
|
|
+ with open(hostPath, 'w') as w_file:
|
|||
|
|
+ for line in lines:
|
|||
|
|
+ line_host_id = json.loads(str(ast.literal_eval(line)).replace("'", "\""))['host_id']
|
|||
|
|
+ if hostId != line_host_id:
|
|||
|
|
+ w_file.write(line)
|
|||
|
|
+ else:
|
|||
|
|
+ isContained = True
|
|||
|
|
+ except OSError as err:
|
|||
|
|
+ LOGGER.error("OS error: {0}".format(err))
|
|||
|
|
+ codeNum = SERVER_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="OS error: {0}".format(err))
|
|||
|
|
+
|
|||
|
|
+ if isContained:
|
|||
|
|
+ containedInHost.append(hostId)
|
|||
|
|
+ else:
|
|||
|
|
+ notContainedInHost.append(hostId)
|
|||
|
|
+
|
|||
|
|
+ # All hosts do not belong to the domain
|
|||
|
|
+ if len(notContainedInHost) == len(hostInfos):
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="All the host does not belong to the domain control, please enter the host "
|
|||
|
|
+ "again")
|
|||
|
|
+
|
|||
|
|
+ # Some hosts belong to domains, and some hosts do not belong to domains.
|
|||
|
|
+ if len(notContainedInHost) == 0:
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ codeString = Format.spliceAllSuccString("host", "delete", containedInHost)
|
|||
|
|
+ else:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ codeString = Format.splicErrorString("host", "delete", containedInHost, notContainedInHost)
|
|||
|
|
+
|
|||
|
|
+ # git commit message
|
|||
|
|
+ if len(containedInHost) > 0:
|
|||
|
|
+ git_tools = GitTools()
|
|||
|
|
+ commit_code = git_tools.gitCommit("Delete the host in {} domian, ".format(domain) +
|
|||
|
|
+ "the host including : {}".format(containedInHost))
|
|||
|
|
+ # # 根据containedInHost 停止agith服务,删除agith,删除redis key值
|
|||
|
|
+ Format.uninstall_hosts_agith(access_token, containedInHost, domain)
|
|||
|
|
+
|
|||
|
|
+ return self.response(code=codeNum, message=codeString)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class GetHostByDomainName(BaseResponse):
|
|||
|
|
+ @BaseResponse.handle(schema=GetHostSchema, token=True)
|
|||
|
|
+ def post(self, **params):
|
|||
|
|
+ """get host by domainName
|
|||
|
|
+
|
|||
|
|
+ get the host information of the configuration domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param body: domain info
|
|||
|
|
+ :type body: dict | bytes
|
|||
|
|
+
|
|||
|
|
+ :rtype: List[Host]
|
|||
|
|
+ """
|
|||
|
|
+ domain = params.get("domainName")
|
|||
|
|
+ # check the input domain
|
|||
|
|
+ checkRes = Format.domainCheck(domain)
|
|||
|
|
+ if not checkRes:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
+
|
|||
|
|
+ # check whether the domain exists
|
|||
|
|
+ isExist = Format.isDomainExist(domain)
|
|||
|
|
+ if not isExist:
|
|||
|
|
+ codeNum = PARAM_ERROR
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="The current domain does not exist, please create the domain first.")
|
|||
|
|
+
|
|||
|
|
+ # The domain exists, but the host information is empty
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
+ hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
+ if not os.path.isfile(hostPath) or (os.path.isfile(hostPath) and os.stat(hostPath).st_size == 0):
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ return self.response(code=codeNum,
|
|||
|
|
+ message="The host information is not set in the current domain. Please add the host "
|
|||
|
|
+ "information first.")
|
|||
|
|
+
|
|||
|
|
+ # The domain exists, and the host information exists and is not empty
|
|||
|
|
+ hostlist = []
|
|||
|
|
+ LOGGER.debug("hostPath is : {}".format(hostPath))
|
|||
|
|
+ try:
|
|||
|
|
+ with open(hostPath, 'r') as d_file:
|
|||
|
|
+ for line in d_file.readlines():
|
|||
|
|
+ json_str = json.loads(line)
|
|||
|
|
+ host_json = ast.literal_eval(json_str)
|
|||
|
|
+ hostId = host_json["host_id"]
|
|||
|
|
+ ip = host_json["ip"]
|
|||
|
|
+ ipv6 = host_json["ipv6"]
|
|||
|
|
+ host = {"hostId": hostId, "ip": ip, "ipv6": ipv6}
|
|||
|
|
+ hostlist.append(host)
|
|||
|
|
+ except OSError as err:
|
|||
|
|
+ LOGGER.error("OS error: {0}".format(err))
|
|||
|
|
+ codeNum = SERVER_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="OS error: {0}".format(err))
|
|||
|
|
+
|
|||
|
|
+ # Joining together the returned codeNum codeMessage
|
|||
|
|
+ if len(hostlist) == 0:
|
|||
|
|
+ codeNum = SERVER_ERROR
|
|||
|
|
+ return self.response(code=codeNum, message="Some unknown problems.")
|
|||
|
|
+ else:
|
|||
|
|
+ LOGGER.debug("hostlist is : {}".format(hostlist))
|
|||
|
|
+ codeNum = SUCCEED
|
|||
|
|
+ return self.response(code=codeNum, message="Get host info in the domain successfully", data=hostlist)
|
|||
|
|
diff --git a/ragdoll/manage.py b/ragdoll/manage.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..d646f77
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/manage.py
|
|||
|
|
@@ -0,0 +1,42 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+try:
|
|||
|
|
+ from gevent import monkey, pywsgi
|
|||
|
|
+
|
|||
|
|
+ monkey.patch_all(ssl=False)
|
|||
|
|
+except:
|
|||
|
|
+ pass
|
|||
|
|
+
|
|||
|
|
+from vulcanus.manage import init_application
|
|||
|
|
+from ragdoll.conf import configuration
|
|||
|
|
+from ragdoll.url import URLS
|
|||
|
|
+from ragdoll.utils.prepare import Prepare
|
|||
|
|
+from ragdoll.utils.yang_module import YangModule
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+def load_prepare():
|
|||
|
|
+ git_dir = configuration.git.get("GIT_DIR").replace("\"", "")
|
|||
|
|
+ git_user_name = configuration.git.get("USER_NAME").replace("\"", "")
|
|||
|
|
+ git_user_email = configuration.git.get("USER_EMAIL").replace("\"", "")
|
|||
|
|
+
|
|||
|
|
+ prepare = Prepare(git_dir)
|
|||
|
|
+ prepare.mkdir_git_warehose(git_user_name, git_user_email)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+def load_yang():
|
|||
|
|
+ yang_modules = YangModule()
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+def main():
|
|||
|
|
+ _app = init_application(name="ragdoll", settings=configuration, register_urls=URLS)
|
|||
|
|
+ # prepare to load config
|
|||
|
|
+ load_prepare()
|
|||
|
|
+ # load yang modules
|
|||
|
|
+ load_yang()
|
|||
|
|
+ print("configuration.ragdoll.get('ip') is ", configuration.ragdoll.get('IP'))
|
|||
|
|
+ return _app
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+app = main()
|
|||
|
|
+
|
|||
|
|
+if __name__ == "__main__":
|
|||
|
|
+ app.run(host=configuration.ragdoll.get('IP'), port=configuration.ragdoll.get("PORT"))
|
|||
|
|
diff --git a/ragdoll/models/batch_sync_req.py b/ragdoll/models/batch_sync_req.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..bca0e65
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/models/batch_sync_req.py
|
|||
|
|
@@ -0,0 +1,95 @@
|
|||
|
|
+# coding: utf-8
|
|||
|
|
+
|
|||
|
|
+from __future__ import absolute_import
|
|||
|
|
+from datetime import date, datetime # noqa: F401
|
|||
|
|
+
|
|||
|
|
+from typing import List, Dict # noqa: F401
|
|||
|
|
+
|
|||
|
|
+from ragdoll.models.base_model_ import Model
|
|||
|
|
+from ragdoll import util
|
|||
|
|
+from ragdoll.models.sync_host_confs import SyncHostConfs
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class BatchSyncReq(Model):
|
|||
|
|
+ """NOTE: This class is auto generated by the swagger code generator program.
|
|||
|
|
+
|
|||
|
|
+ Do not edit the class manually.
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ def __init__(self, domain_name: str = None, host_ids: List[int] = None): # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ """ConfHost - a model defined in Swagger
|
|||
|
|
+
|
|||
|
|
+ :param domain_name: The domain_name of this BatchSyncReq. # noqa: E501
|
|||
|
|
+ :type domain_name: str
|
|||
|
|
+ :param host_ids: The host_ids of this BatchSyncReq. # noqa: E501
|
|||
|
|
+ :type host_ids: List[int]
|
|||
|
|
+ """
|
|||
|
|
+ self.swagger_types = {
|
|||
|
|
+ 'domain_name': str,
|
|||
|
|
+ 'host_ids': List[int]
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self.attribute_map = {
|
|||
|
|
+ 'domain_name': 'domainName',
|
|||
|
|
+ 'host_ids': 'hostIds'
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self._domain_name = domain_name
|
|||
|
|
+ self._host_ids = host_ids
|
|||
|
|
+
|
|||
|
|
+ @classmethod
|
|||
|
|
+ def from_dict(cls, dikt) -> 'BatchSyncReq':
|
|||
|
|
+ """Returns the dict as a model
|
|||
|
|
+
|
|||
|
|
+ :param dikt: A dict.
|
|||
|
|
+ :type: dict
|
|||
|
|
+ :return: The BatchSyncReq of this BatchSyncReq. # noqa: E501
|
|||
|
|
+ :rtype: BatchSyncReq
|
|||
|
|
+ """
|
|||
|
|
+ return util.deserialize_model(dikt, cls)
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def domain_name(self) -> str:
|
|||
|
|
+ """Gets the domain_name of this BatchSyncReq.
|
|||
|
|
+
|
|||
|
|
+ domain name # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :return: The domain_name of this BatchSyncReq.
|
|||
|
|
+ :rtype: str
|
|||
|
|
+ """
|
|||
|
|
+ return self._domain_name
|
|||
|
|
+
|
|||
|
|
+ @domain_name.setter
|
|||
|
|
+ def domain_name(self, domain_name: str):
|
|||
|
|
+ """Sets the domain_name of this BatchSyncReq.
|
|||
|
|
+
|
|||
|
|
+ domain name # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param domain_name: The domain_name of this BatchSyncReq.
|
|||
|
|
+ :type domain_name: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._domain_name = domain_name
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def host_ids(self) -> List[int]:
|
|||
|
|
+ """Gets the host_ids of this BatchSyncReq.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :return: The host_ids of this BatchSyncReq.
|
|||
|
|
+ :rtype: List[int]
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ return self._host_ids
|
|||
|
|
+
|
|||
|
|
+ @host_ids.setter
|
|||
|
|
+ def host_ids(self, host_ids: List[int]):
|
|||
|
|
+ """Sets the sync_list of this BatchSyncReq.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :param host_ids: The host_ids of this BatchSyncReq.
|
|||
|
|
+ :type host_ids: List[int]
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._host_ids = host_ids
|
|||
|
|
diff --git a/ragdoll/models/compare_conf_diff.py b/ragdoll/models/compare_conf_diff.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..c68c8b7
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/models/compare_conf_diff.py
|
|||
|
|
@@ -0,0 +1,104 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: compare_conf_diff.py
|
|||
|
|
+@Time: 2024/1/25 10:05
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+from typing import List
|
|||
|
|
+from ragdoll import util
|
|||
|
|
+from ragdoll.models.base_model_ import Model
|
|||
|
|
+from ragdoll.models.domain_conf_base_infos import DomainConfBaseInfos
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class CompareConfDiff(Model):
|
|||
|
|
+ """NOTE: This class is auto generated by the swagger code generator program.
|
|||
|
|
+
|
|||
|
|
+ Do not edit the class manually.
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ def __init__(self, expected_confs_resp: List[DomainConfBaseInfos] = None, domain_result: object = None): # noqa: E501
|
|||
|
|
+ """CompareConfDiff - a model defined in Swagger
|
|||
|
|
+
|
|||
|
|
+ :param expected_confs_resp: The all domain conf. # noqa: E501
|
|||
|
|
+ :type expected_confs_resp: str
|
|||
|
|
+
|
|||
|
|
+ :param domain_result: The all domain host real conf. # noqa: E501
|
|||
|
|
+ :type domain_result: str
|
|||
|
|
+ """
|
|||
|
|
+ self.swagger_types = {
|
|||
|
|
+ 'expected_confs_resp': List[DomainConfBaseInfos],
|
|||
|
|
+ 'domain_result': object
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self.attribute_map = {
|
|||
|
|
+ 'expected_confs_resp': 'expectedConfsResp',
|
|||
|
|
+ 'domain_result': 'domainResult'
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self._expected_confs_resp = expected_confs_resp
|
|||
|
|
+ self._domain_result = domain_result
|
|||
|
|
+
|
|||
|
|
+ @classmethod
|
|||
|
|
+ def from_dict(cls, dikt) -> 'CompareConfDiff':
|
|||
|
|
+ """Returns the dict as a model
|
|||
|
|
+
|
|||
|
|
+ :param dikt: A dict.
|
|||
|
|
+ :type: dict
|
|||
|
|
+ :return: the CompareConfDiff of CompareConfDiff. # noqa: E501
|
|||
|
|
+ :rtype: CompareConfDiff
|
|||
|
|
+ """
|
|||
|
|
+ return util.deserialize_model(dikt, cls)
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def expected_confs_resp(self) -> List[DomainConfBaseInfos]:
|
|||
|
|
+ """Gets expected_confs_resp of this CompareConfDiff.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :return: The expected_confs_resp of this CompareConfDiff.
|
|||
|
|
+ :rtype: List[DomainConfBaseInfos]
|
|||
|
|
+ """
|
|||
|
|
+ return self._expected_confs_resp
|
|||
|
|
+
|
|||
|
|
+ @expected_confs_resp.setter
|
|||
|
|
+ def expected_confs_resp(self, expected_confs_resp: List[DomainConfBaseInfos]):
|
|||
|
|
+ """Sets expected_confs_resp of this CompareConfDiff.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :param expected_confs_resp: The expected_confs_resp of this CompareConfDiff.
|
|||
|
|
+ type expected_confs_resp: List[DomainConfBaseInfos]
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._expected_confs_resp = expected_confs_resp
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def domain_result(self) -> object:
|
|||
|
|
+ """Gets domain_result of this CompareConfDiff.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :return: The domain_result of this CompareConfDiff.
|
|||
|
|
+ :rtype: str
|
|||
|
|
+ """
|
|||
|
|
+ return self._domain_result
|
|||
|
|
+
|
|||
|
|
+ @domain_result.setter
|
|||
|
|
+ def domain_result(self, domain_result: object):
|
|||
|
|
+ """Sets domain_result of this CompareConfDiff.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :param domain_result: The domain_result of this CompareConfDiff.
|
|||
|
|
+ type domain_result: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._domain_result = domain_result
|
|||
|
|
diff --git a/ragdoll/models/conf_base.py b/ragdoll/models/conf_base.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..21d2ec6
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/models/conf_base.py
|
|||
|
|
@@ -0,0 +1,105 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: conf_base.py
|
|||
|
|
+@Time: 2024/1/25 15:09
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+from ragdoll import util
|
|||
|
|
+from ragdoll.models.base_model_ import Model
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class ConfBase(Model):
|
|||
|
|
+ """NOTE: This class is auto generated by the swagger code generator program.
|
|||
|
|
+
|
|||
|
|
+ Do not edit the class manually.
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ def __init__(self, file_path: str = None, expected_contents: str = None): # noqa: E501
|
|||
|
|
+ """ConfBaseInfo - a model defined in Swagger
|
|||
|
|
+
|
|||
|
|
+ :param file_path: The file_path of this ConfBase. # noqa: E501
|
|||
|
|
+ :type file_path: str
|
|||
|
|
+ :param expected_contents: The expected_contents of this ConfBase. # noqa: E501
|
|||
|
|
+ :type expected_contents: str
|
|||
|
|
+ """
|
|||
|
|
+ self.swagger_types = {
|
|||
|
|
+ 'file_path': str,
|
|||
|
|
+ 'expected_contents': str
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self.attribute_map = {
|
|||
|
|
+ 'file_path': 'filePath',
|
|||
|
|
+ 'expected_contents': 'expectedContents'
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self._file_path = file_path
|
|||
|
|
+ self._expected_contents = expected_contents
|
|||
|
|
+
|
|||
|
|
+ @classmethod
|
|||
|
|
+ def from_dict(cls, dikt) -> 'ConfBase':
|
|||
|
|
+ """Returns the dict as a model
|
|||
|
|
+
|
|||
|
|
+ :param dikt: A dict.
|
|||
|
|
+ :type: dict
|
|||
|
|
+ :return: The ConfBase of this ConfBase. # noqa: E501
|
|||
|
|
+ :rtype: ConfBase
|
|||
|
|
+ """
|
|||
|
|
+ return util.deserialize_model(dikt, cls)
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def file_path(self) -> str:
|
|||
|
|
+ """Gets the file_path of this ConfBase.
|
|||
|
|
+
|
|||
|
|
+ the path of a configuration file # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :return: The file_path of this ConfBase.
|
|||
|
|
+ :rtype: str
|
|||
|
|
+ """
|
|||
|
|
+ return self._file_path
|
|||
|
|
+
|
|||
|
|
+ @file_path.setter
|
|||
|
|
+ def file_path(self, file_path: str):
|
|||
|
|
+ """Sets the file_path of this ConfBase.
|
|||
|
|
+
|
|||
|
|
+ the path of a configuration file # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param file_path: The file_path of this ConfBase.
|
|||
|
|
+ :type file_path: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._file_path = file_path
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def expected_contents(self) -> str:
|
|||
|
|
+ """Gets the expected_contents of this ConfBase.
|
|||
|
|
+
|
|||
|
|
+ expected configuration value of configuration item # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :return: The expected_contents of this ConfBase.
|
|||
|
|
+ :rtype: str
|
|||
|
|
+ """
|
|||
|
|
+ return self._expected_contents
|
|||
|
|
+
|
|||
|
|
+ @expected_contents.setter
|
|||
|
|
+ def expected_contents(self, expected_contents: str):
|
|||
|
|
+ """Sets the expected_contents of this ConfBase.
|
|||
|
|
+
|
|||
|
|
+ expected configuration value of configuration item # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param expected_contents: The expected_contents of this ConfBase.
|
|||
|
|
+ :type expected_contents: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._expected_contents = expected_contents
|
|||
|
|
diff --git a/ragdoll/models/domain_conf_base_infos.py b/ragdoll/models/domain_conf_base_infos.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..e49e8d2
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/models/domain_conf_base_infos.py
|
|||
|
|
@@ -0,0 +1,105 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: domain_conf_base_infos.py
|
|||
|
|
+@Time: 2024/1/25 15:06
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+from typing import List
|
|||
|
|
+
|
|||
|
|
+from ragdoll.models.base_model_ import Model
|
|||
|
|
+from ragdoll.models.conf_base import ConfBase
|
|||
|
|
+from ragdoll import util
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DomainConfBaseInfos(Model):
|
|||
|
|
+ """NOTE: This class is auto generated by the swagger code generator program.
|
|||
|
|
+
|
|||
|
|
+ Do not edit the class manually.
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ def __init__(self, conf_base_infos: List[ConfBase] = None, domain_name: str = None): # noqa: E501
|
|||
|
|
+ """DomainConfBaseInfos - a model defined in Swagger
|
|||
|
|
+
|
|||
|
|
+ :param conf_base_infos: The all domain conf. # noqa: E501
|
|||
|
|
+ :type List[ConfBase]
|
|||
|
|
+
|
|||
|
|
+ :param domain_name: The domain_name of DomainConfBaseInfos. # noqa: E501
|
|||
|
|
+ :type domain_name: str
|
|||
|
|
+ """
|
|||
|
|
+ self.swagger_types = {
|
|||
|
|
+ 'conf_base_infos': List[ConfBase],
|
|||
|
|
+ 'domain_name': str
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self.attribute_map = {
|
|||
|
|
+ 'conf_base_infos': 'confBaseInfos',
|
|||
|
|
+ 'domain_name': 'domainName'
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self._conf_base_infos = conf_base_infos
|
|||
|
|
+ self._domain_name = domain_name
|
|||
|
|
+
|
|||
|
|
+ @classmethod
|
|||
|
|
+ def from_dict(cls, dikt) -> 'DomainConfBaseInfos':
|
|||
|
|
+ """Returns the dict as a model
|
|||
|
|
+
|
|||
|
|
+ :param dikt: A dict.
|
|||
|
|
+ :type: dict
|
|||
|
|
+ :return: the DomainConfBaseInfos of DomainConfBaseInfos. # noqa: E501
|
|||
|
|
+ :rtype: DomainConfBaseInfos
|
|||
|
|
+ """
|
|||
|
|
+ return util.deserialize_model(dikt, cls)
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def conf_base_infos(self) -> List[ConfBase]:
|
|||
|
|
+ """Gets conf_base_infos of this DomainConfBaseInfos.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :return: The conf_base_infos of this DomainConfBaseInfos.
|
|||
|
|
+ :rtype: List[ConfBase]
|
|||
|
|
+ """
|
|||
|
|
+ return self._conf_base_infos
|
|||
|
|
+
|
|||
|
|
+ @conf_base_infos.setter
|
|||
|
|
+ def conf_base_infos(self, conf_base_infos: List[ConfBase]):
|
|||
|
|
+ """Sets conf_base_infos of this DomainConfBaseInfos.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :param conf_base_infos: The conf_base_infos of this DomainConfBaseInfos.
|
|||
|
|
+ type expected_confs_resp: List[ConfBase]
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._conf_base_infos = conf_base_infos
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def domain_name(self) -> str:
|
|||
|
|
+ """Gets domain_name of this DomainConfBaseInfos.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :return: The domain_name of this DomainConfBaseInfos.
|
|||
|
|
+ :rtype: str
|
|||
|
|
+ """
|
|||
|
|
+ return self._domain_name
|
|||
|
|
+
|
|||
|
|
+ @domain_name.setter
|
|||
|
|
+ def domain_name(self, domain_name: str):
|
|||
|
|
+ """Sets domain_name of this DomainConfBaseInfos.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :param domain_name: The domain_result of this DomainConfBaseInfos.
|
|||
|
|
+ type domain_result: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._domain_name = domain_name
|
|||
|
|
diff --git a/ragdoll/models/domain_config_sync_result.py b/ragdoll/models/domain_config_sync_result.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..c7dc741
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/models/domain_config_sync_result.py
|
|||
|
|
@@ -0,0 +1,131 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: domain_config_sync_result.py
|
|||
|
|
+@Time: 2024/1/25 11:31
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+# coding: utf-8
|
|||
|
|
+
|
|||
|
|
+from __future__ import absolute_import
|
|||
|
|
+from datetime import date, datetime # noqa: F401
|
|||
|
|
+from typing import List, Dict # noqa: F401
|
|||
|
|
+from ragdoll.models.base_model_ import Model
|
|||
|
|
+from ragdoll import util
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DomainConfigSyncResult(Model):
|
|||
|
|
+ """NOTE: This class is auto generated by the swagger code generator program.
|
|||
|
|
+
|
|||
|
|
+ Do not edit the class manually.
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ def __init__(self, host_id: int = None, domain_name: str = None, sync_status: int = None): # noqa: E501
|
|||
|
|
+ """DomainConfigSyncResult - a model defined in Swagger
|
|||
|
|
+
|
|||
|
|
+ :param host_id: The host_id of this HostSyncResult. # noqa: E501
|
|||
|
|
+ :type host_id: int
|
|||
|
|
+ :param domain_name: The domain_name of this DomainConfigSyncResult. # noqa: E501
|
|||
|
|
+ :type domain_name: str
|
|||
|
|
+ :param sync_status: The sync_status of this DomainConfigSyncResult. # noqa: E501
|
|||
|
|
+ :type sync_status: int
|
|||
|
|
+ """
|
|||
|
|
+ self.swagger_types = {
|
|||
|
|
+ 'host_id': int,
|
|||
|
|
+ 'domain_name': str,
|
|||
|
|
+ 'sync_status': int
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self.attribute_map = {
|
|||
|
|
+ 'host_id': 'hostId',
|
|||
|
|
+ 'domain_name': 'domainName',
|
|||
|
|
+ 'sync_status': 'syncStatus'
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self._host_id = host_id
|
|||
|
|
+ self._domain_name = domain_name
|
|||
|
|
+ self._sync_status = sync_status
|
|||
|
|
+
|
|||
|
|
+ @classmethod
|
|||
|
|
+ def from_dict(cls, dikt) -> 'DomainConfigSyncResult':
|
|||
|
|
+ """Returns the dict as a model
|
|||
|
|
+
|
|||
|
|
+ :param dikt: A dict.
|
|||
|
|
+ :type: dict
|
|||
|
|
+ :return: The DomainConfigSyncResult of this DomainConfigSyncResult. # noqa: E501
|
|||
|
|
+ :rtype: DomainConfigSyncResult
|
|||
|
|
+ """
|
|||
|
|
+ return util.deserialize_model(dikt, cls)
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def host_id(self) -> int:
|
|||
|
|
+ """Gets the host_id of this DomainConfigSyncResult.
|
|||
|
|
+
|
|||
|
|
+ the id of host # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :return: The host_id of this DomainConfigSyncResult.
|
|||
|
|
+ :rtype: int
|
|||
|
|
+ """
|
|||
|
|
+ return self._host_id
|
|||
|
|
+
|
|||
|
|
+ @host_id.setter
|
|||
|
|
+ def host_id(self, host_id: int):
|
|||
|
|
+ """Sets the host_id of this DomainConfigSyncResult.
|
|||
|
|
+
|
|||
|
|
+ the id of host # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param host_id: The host_id of this DomainConfigSyncResult.
|
|||
|
|
+ :type host_id: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._host_id = host_id
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def domain_name(self) -> str:
|
|||
|
|
+ """Gets the domain_name of this DomainConfigSyncResult.
|
|||
|
|
+
|
|||
|
|
+ :return: The domain_name of this DomainConfigSyncResult.
|
|||
|
|
+ :rtype: str
|
|||
|
|
+ """
|
|||
|
|
+ return self._domain_name
|
|||
|
|
+
|
|||
|
|
+ @domain_name.setter
|
|||
|
|
+ def domain_name(self, domain_name: str):
|
|||
|
|
+ """Sets the domain_name of this DomainConfigSyncResult.
|
|||
|
|
+
|
|||
|
|
+ :param domain_name: The domain_name of this DomainConfigSyncResult.
|
|||
|
|
+ :type domain_name: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._domain_name = domain_name
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def sync_status(self) -> int:
|
|||
|
|
+ """Gets the sync_status of this DomainConfigSyncResult.
|
|||
|
|
+
|
|||
|
|
+ :return: The sync_status of this DomainConfigSyncResult.
|
|||
|
|
+ :rtype: int
|
|||
|
|
+ """
|
|||
|
|
+ return self._sync_status
|
|||
|
|
+
|
|||
|
|
+ @sync_status.setter
|
|||
|
|
+ def sync_status(self, sync_status: int):
|
|||
|
|
+ """Sets the sync_status of this DomainConfigSyncResult.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :param sync_status: The sync_status of this DomainConfigSyncResult.
|
|||
|
|
+ :type sync_status: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._sync_status = sync_status
|
|||
|
|
diff --git a/ragdoll/models/domain_ip.py b/ragdoll/models/domain_ip.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..932afec
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/models/domain_ip.py
|
|||
|
|
@@ -0,0 +1,92 @@
|
|||
|
|
+# coding: utf-8
|
|||
|
|
+
|
|||
|
|
+from __future__ import absolute_import
|
|||
|
|
+from datetime import date, datetime # noqa: F401
|
|||
|
|
+
|
|||
|
|
+from typing import List, Dict # noqa: F401
|
|||
|
|
+
|
|||
|
|
+from ragdoll.models.base_model_ import Model
|
|||
|
|
+from ragdoll import util
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DomainIp(Model):
|
|||
|
|
+ """NOTE: This class is auto generated by the swagger code generator program.
|
|||
|
|
+
|
|||
|
|
+ Do not edit the class manually.
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ def __init__(self, domain_name: str=None, ip: str=None): # noqa: E501
|
|||
|
|
+ """DomainName - a model defined in Swagger
|
|||
|
|
+
|
|||
|
|
+ :param domain_name: The domain_name of this DomainName. # noqa: E501
|
|||
|
|
+ :type domain_name: str
|
|||
|
|
+ """
|
|||
|
|
+ self.swagger_types = {
|
|||
|
|
+ 'domain_name': str,
|
|||
|
|
+ 'ip': str
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self.attribute_map = {
|
|||
|
|
+ 'domain_name': 'domainName',
|
|||
|
|
+ 'ip': 'ip'
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self._domain_name = domain_name
|
|||
|
|
+ self._ip = ip
|
|||
|
|
+
|
|||
|
|
+ @classmethod
|
|||
|
|
+ def from_dict(cls, dikt) -> 'DomainName':
|
|||
|
|
+ """Returns the dict as a model
|
|||
|
|
+
|
|||
|
|
+ :param dikt: A dict.
|
|||
|
|
+ :type: dict
|
|||
|
|
+ :return: The DomainName of this DomainName. # noqa: E501
|
|||
|
|
+ :rtype: DomainName
|
|||
|
|
+ """
|
|||
|
|
+ return util.deserialize_model(dikt, cls)
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def domain_name(self) -> str:
|
|||
|
|
+ """Gets the domain_name of this DomainName.
|
|||
|
|
+
|
|||
|
|
+ domain name # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :return: The domain_name of this DomainName.
|
|||
|
|
+ :rtype: str
|
|||
|
|
+ """
|
|||
|
|
+ return self._domain_name
|
|||
|
|
+
|
|||
|
|
+ @domain_name.setter
|
|||
|
|
+ def domain_name(self, domain_name: str):
|
|||
|
|
+ """Sets the domain_name of this DomainName.
|
|||
|
|
+
|
|||
|
|
+ domain name # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param domain_name: The domain_name of this DomainName.
|
|||
|
|
+ :type domain_name: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._domain_name = domain_name
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def ip(self) -> str:
|
|||
|
|
+ """Gets the ip in this domain.
|
|||
|
|
+
|
|||
|
|
+ the ipv4 address in this domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :return: The ip in this domain.
|
|||
|
|
+ :rtype: str
|
|||
|
|
+ """
|
|||
|
|
+ return self._ip
|
|||
|
|
+
|
|||
|
|
+ @ip.setter
|
|||
|
|
+ def ip(self, ip: str):
|
|||
|
|
+ """Sets the ip in this domain.
|
|||
|
|
+
|
|||
|
|
+ the ipv4 address in this domain # noqa: E501
|
|||
|
|
+
|
|||
|
|
+ :param ip: The ip in this domain.
|
|||
|
|
+ :type ip: str
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._ip = ip
|
|||
|
|
diff --git a/ragdoll/models/domain_names.py b/ragdoll/models/domain_names.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..096cd81
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/models/domain_names.py
|
|||
|
|
@@ -0,0 +1,65 @@
|
|||
|
|
+# coding: utf-8
|
|||
|
|
+
|
|||
|
|
+from __future__ import absolute_import
|
|||
|
|
+from datetime import date, datetime # noqa: F401
|
|||
|
|
+
|
|||
|
|
+from typing import List, Dict # noqa: F401
|
|||
|
|
+
|
|||
|
|
+from ragdoll.models import DomainName
|
|||
|
|
+from ragdoll.models.base_model_ import Model
|
|||
|
|
+from ragdoll import util
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class DomainNames(Model):
|
|||
|
|
+ """NOTE: This class is auto generated by the swagger code generator program.
|
|||
|
|
+
|
|||
|
|
+ Do not edit the class manually.
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ def __init__(self, domain_names: List[DomainName]=None): # noqa: E501
|
|||
|
|
+ """DomainName - a model defined in Swagger
|
|||
|
|
+
|
|||
|
|
+ :param domain_names: The domain_names of this DomainNames. # noqa: E501
|
|||
|
|
+ :type domain_names: List[DomainName]
|
|||
|
|
+ """
|
|||
|
|
+ self.swagger_types = {
|
|||
|
|
+ 'domain_names': List[DomainName]
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self.attribute_map = {
|
|||
|
|
+ 'domain_names': 'domainNames'
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ self._domain_names = domain_names
|
|||
|
|
+
|
|||
|
|
+ @classmethod
|
|||
|
|
+ def from_dict(cls, dikt) -> 'DomainNames':
|
|||
|
|
+ """Returns the dict as a model
|
|||
|
|
+
|
|||
|
|
+ :param dikt: A dict.
|
|||
|
|
+ :type: dict
|
|||
|
|
+ :return: The DomainNames of this DomainNames. # noqa: E501
|
|||
|
|
+ :rtype: DomainNames
|
|||
|
|
+ """
|
|||
|
|
+ return util.deserialize_model(dikt, cls)
|
|||
|
|
+
|
|||
|
|
+ @property
|
|||
|
|
+ def domain_names(self) -> List[DomainName]:
|
|||
|
|
+ """Gets the domain_names of this DomainNames.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :return: The domain_names of this DomainNames.
|
|||
|
|
+ :rtype: List[DomainName]
|
|||
|
|
+ """
|
|||
|
|
+ return self._domain_names
|
|||
|
|
+
|
|||
|
|
+ @domain_names.setter
|
|||
|
|
+ def domain_names(self, domain_names: List[DomainName]):
|
|||
|
|
+ """Sets the domain_names of this DomainNames.
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ :param domain_names: The domain_names of this DomainNames.
|
|||
|
|
+ type domain_names: List[DomainName]
|
|||
|
|
+ """
|
|||
|
|
+
|
|||
|
|
+ self._domain_names = domain_names
|
|||
|
|
diff --git a/ragdoll/models/realconf_base_info.py b/ragdoll/models/realconf_base_info.py
|
|||
|
|
index 8fe74f6..6139866 100644
|
|||
|
|
--- a/ragdoll/models/realconf_base_info.py
|
|||
|
|
+++ b/ragdoll/models/realconf_base_info.py
|
|||
|
|
@@ -15,7 +15,7 @@ class RealconfBaseInfo(Model):
|
|||
|
|
Do not edit the class manually.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
- def __init__(self, path: str=None, file_path: str=None, rpm_name: str=None, rpm_version: str=None, rpm_release: str=None, file_attr: str=None, file_owner: str=None, conf_type: str=None, spacer: str=None, conf_contens: str=None): # noqa: E501
|
|||
|
|
+ def __init__(self, path: str=None, file_path: str=None, rpm_name: str=None, rpm_version: str=None, rpm_release: str=None, file_attr: str=None, file_owner: str=None, conf_type: str=None, spacer: str=None, conf_contents: str=None): # noqa: E501
|
|||
|
|
"""RealconfBaseInfo - a model defined in Swagger
|
|||
|
|
|
|||
|
|
:param path: The path of this RealconfBaseInfo. # noqa: E501
|
|||
|
|
@@ -36,8 +36,8 @@ class RealconfBaseInfo(Model):
|
|||
|
|
:type conf_type: str
|
|||
|
|
:param spacer: The spacer of this RealconfBaseInfo. # noqa: E501
|
|||
|
|
:type spacer: str
|
|||
|
|
- :param conf_contens: The conf_contens of this RealconfBaseInfo. # noqa: E501
|
|||
|
|
- :type conf_contens: str
|
|||
|
|
+ :param conf_contents: The conf_contents of this RealconfBaseInfo. # noqa: E501
|
|||
|
|
+ :type conf_contents: str
|
|||
|
|
"""
|
|||
|
|
self.swagger_types = {
|
|||
|
|
'path': str,
|
|||
|
|
@@ -49,7 +49,7 @@ class RealconfBaseInfo(Model):
|
|||
|
|
'file_owner': str,
|
|||
|
|
'conf_type': str,
|
|||
|
|
'spacer': str,
|
|||
|
|
- 'conf_contens': str
|
|||
|
|
+ 'conf_contents': str
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
self.attribute_map = {
|
|||
|
|
@@ -62,7 +62,7 @@ class RealconfBaseInfo(Model):
|
|||
|
|
'file_owner': 'fileOwner',
|
|||
|
|
'conf_type': 'confType',
|
|||
|
|
'spacer': 'spacer',
|
|||
|
|
- 'conf_contens': 'confContents'
|
|||
|
|
+ 'conf_contents': 'confContents'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
self._path = path
|
|||
|
|
@@ -74,7 +74,7 @@ class RealconfBaseInfo(Model):
|
|||
|
|
self._file_owner = file_owner
|
|||
|
|
self._conf_type = conf_type
|
|||
|
|
self._spacer = spacer
|
|||
|
|
- self._conf_contens = conf_contens
|
|||
|
|
+ self._conf_contents = conf_contents
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def from_dict(cls, dikt) -> 'RealconfBaseInfo':
|
|||
|
|
@@ -293,24 +293,24 @@ class RealconfBaseInfo(Model):
|
|||
|
|
self._spacer = spacer
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
- def conf_contens(self) -> str:
|
|||
|
|
- """Gets the conf_contens of this RealconfBaseInfo.
|
|||
|
|
+ def conf_contents(self) -> str:
|
|||
|
|
+ """Gets the conf_contents of this RealconfBaseInfo.
|
|||
|
|
|
|||
|
|
the specific content of the configuration item # noqa: E501
|
|||
|
|
|
|||
|
|
- :return: The conf_contens of this RealconfBaseInfo.
|
|||
|
|
+ :return: The conf_contents of this RealconfBaseInfo.
|
|||
|
|
:rtype: str
|
|||
|
|
"""
|
|||
|
|
- return self._conf_contens
|
|||
|
|
+ return self._conf_contents
|
|||
|
|
|
|||
|
|
- @conf_contens.setter
|
|||
|
|
- def conf_contens(self, conf_contens: str):
|
|||
|
|
- """Sets the conf_contens of this RealconfBaseInfo.
|
|||
|
|
+ @conf_contents.setter
|
|||
|
|
+ def conf_contents(self, conf_contents: str):
|
|||
|
|
+ """Sets the conf_contents of this RealconfBaseInfo.
|
|||
|
|
|
|||
|
|
the specific content of the configuration item # noqa: E501
|
|||
|
|
|
|||
|
|
- :param conf_contens: The conf_contens of this RealconfBaseInfo.
|
|||
|
|
- :type conf_contens: str
|
|||
|
|
+ :param conf_contents: The conf_contents of this RealconfBaseInfo.
|
|||
|
|
+ :type conf_contents: str
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
- self._conf_contens = conf_contens
|
|||
|
|
+ self._conf_contents = conf_contents
|
|||
|
|
diff --git a/ragdoll/swagger/swagger.yaml b/ragdoll/swagger/swagger.yaml
|
|||
|
|
index 8fd5dcb..e90201f 100644
|
|||
|
|
--- a/ragdoll/swagger/swagger.yaml
|
|||
|
|
+++ b/ragdoll/swagger/swagger.yaml
|
|||
|
|
@@ -303,7 +303,12 @@ paths:
|
|||
|
|
summary: "query expected configuration value in the current hostId node"
|
|||
|
|
description: "queryExpectedConfs"
|
|||
|
|
operationId: "query_excepted_confs"
|
|||
|
|
- parameters: [ ]
|
|||
|
|
+ parameters:
|
|||
|
|
+ - in: "body"
|
|||
|
|
+ name: "body"
|
|||
|
|
+ required: false
|
|||
|
|
+ schema:
|
|||
|
|
+ $ref: "#/definitions/DomainNames"
|
|||
|
|
responses:
|
|||
|
|
"200":
|
|||
|
|
description: "query expected configuration value successfully"
|
|||
|
|
@@ -337,6 +342,50 @@ paths:
|
|||
|
|
items:
|
|||
|
|
$ref: "#/definitions/HostSyncResult"
|
|||
|
|
x-swagger-router-controller: "ragdoll.controllers.confs_controller"
|
|||
|
|
+ /confs/batch/syncConf:
|
|||
|
|
+ put:
|
|||
|
|
+ tags:
|
|||
|
|
+ - "confs"
|
|||
|
|
+ summary: "batch synchronize the configuration information of the configuration domain\
|
|||
|
|
+ \ to the host"
|
|||
|
|
+ description: "batch synchronize the configuration information of the configuration\
|
|||
|
|
+ \ domain to the host"
|
|||
|
|
+ operationId: "batch_sync_conf_to_host_from_domain"
|
|||
|
|
+ parameters:
|
|||
|
|
+ - in: "body"
|
|||
|
|
+ name: "body"
|
|||
|
|
+ required: false
|
|||
|
|
+ schema:
|
|||
|
|
+ $ref: "#/definitions/BatchSyncReq"
|
|||
|
|
+ responses:
|
|||
|
|
+ "200":
|
|||
|
|
+ description: "synchronize the configuration items successfully"
|
|||
|
|
+ schema:
|
|||
|
|
+ type: "array"
|
|||
|
|
+ items:
|
|||
|
|
+ $ref: "#/definitions/HostSyncResult"
|
|||
|
|
+ x-swagger-router-controller: "ragdoll.controllers.confs_controller"
|
|||
|
|
+ /confs/domain/diff:
|
|||
|
|
+ post:
|
|||
|
|
+ tags:
|
|||
|
|
+ - "confs"
|
|||
|
|
+ summary: "compare domain conf different"
|
|||
|
|
+ description: "compare domain conf different"
|
|||
|
|
+ operationId: "compare_conf_diff"
|
|||
|
|
+ parameters:
|
|||
|
|
+ - in: "body"
|
|||
|
|
+ name: "body"
|
|||
|
|
+ required: false
|
|||
|
|
+ schema:
|
|||
|
|
+ $ref: "#/definitions/CompareConfDiff"
|
|||
|
|
+ responses:
|
|||
|
|
+ "200":
|
|||
|
|
+ description: "compare domain conf different successfully"
|
|||
|
|
+ schema:
|
|||
|
|
+ type: "array"
|
|||
|
|
+ items:
|
|||
|
|
+ $ref: "#/definitions/DomainConfigSyncResult"
|
|||
|
|
+ x-swagger-router-controller: "ragdoll.controllers.confs_controller"
|
|||
|
|
/confs/getDomainStatus:
|
|||
|
|
post:
|
|||
|
|
tags:
|
|||
|
|
@@ -349,7 +398,7 @@ paths:
|
|||
|
|
name: "body"
|
|||
|
|
required: false
|
|||
|
|
schema:
|
|||
|
|
- $ref: "#/definitions/DomainName"
|
|||
|
|
+ $ref: "#/definitions/DomainIp"
|
|||
|
|
responses:
|
|||
|
|
"200":
|
|||
|
|
description: "get the status of the domain successfully"
|
|||
|
|
@@ -418,6 +467,22 @@ definitions:
|
|||
|
|
domainName:
|
|||
|
|
type: "string"
|
|||
|
|
description: "domain name"
|
|||
|
|
+ DomainNames:
|
|||
|
|
+ type: "object"
|
|||
|
|
+ properties:
|
|||
|
|
+ domainNames:
|
|||
|
|
+ type: "array"
|
|||
|
|
+ items:
|
|||
|
|
+ $ref: "#/definitions/DomainName"
|
|||
|
|
+ DomainIp:
|
|||
|
|
+ type: "object"
|
|||
|
|
+ properties:
|
|||
|
|
+ domainName:
|
|||
|
|
+ type: "string"
|
|||
|
|
+ description: "domain name"
|
|||
|
|
+ ip:
|
|||
|
|
+ type: "string"
|
|||
|
|
+ description: "ip"
|
|||
|
|
HostInfos:
|
|||
|
|
type: "object"
|
|||
|
|
properties:
|
|||
|
|
@@ -780,6 +845,18 @@ definitions:
|
|||
|
|
example:
|
|||
|
|
hostId: "hostId"
|
|||
|
|
syncStatus: "SUCCESS"
|
|||
|
|
+ DomainConfigSyncResult:
|
|||
|
|
+ type: "object"
|
|||
|
|
+ properties:
|
|||
|
|
+ hostId:
|
|||
|
|
+ type: "integer"
|
|||
|
|
+ description: "the id of host"
|
|||
|
|
+ domainName:
|
|||
|
|
+ type: "string"
|
|||
|
|
+ description: "the domainName of host"
|
|||
|
|
+ syncStatus:
|
|||
|
|
+ type: "integer"
|
|||
|
|
+ description: "the syncStatus of host"
|
|||
|
|
CollectInfo:
|
|||
|
|
type: "object"
|
|||
|
|
properties:
|
|||
|
|
@@ -820,6 +897,35 @@ definitions:
|
|||
|
|
type: "array"
|
|||
|
|
items:
|
|||
|
|
type: "string"
|
|||
|
|
+ CompareConfDiff:
|
|||
|
|
+ type: "object"
|
|||
|
|
+ properties:
|
|||
|
|
+ expectedConfsResp:
|
|||
|
|
+ type: "array"
|
|||
|
|
+ items:
|
|||
|
|
+ $ref: "#/definitions/DomainConfBaseInfos"
|
|||
|
|
+ domainResult:
|
|||
|
|
+ type: object
|
|||
|
|
+ description: "domain real config"
|
|||
|
|
+ DomainConfBaseInfos:
|
|||
|
|
+ type: "object"
|
|||
|
|
+ properties:
|
|||
|
|
+ confBaseInfos:
|
|||
|
|
+ type: array
|
|||
|
|
+ items:
|
|||
|
|
+ $ref: "#/definitions/ConfBase"
|
|||
|
|
+ domainName:
|
|||
|
|
+ type: "string"
|
|||
|
|
+ description: "domain name"
|
|||
|
|
+ ConfBase:
|
|||
|
|
+ type: "object"
|
|||
|
|
+ properties:
|
|||
|
|
+ filePath:
|
|||
|
|
+ type: "string"
|
|||
|
|
+ description: "file path"
|
|||
|
|
+ expectedContents:
|
|||
|
|
+ type: "string"
|
|||
|
|
+ description: "expected contents"
|
|||
|
|
SyncReq:
|
|||
|
|
type: "object"
|
|||
|
|
properties:
|
|||
|
|
@@ -830,6 +936,16 @@ definitions:
|
|||
|
|
type: "array"
|
|||
|
|
items:
|
|||
|
|
$ref: "#/definitions/SyncHostConfs"
|
|||
|
|
+ BatchSyncReq:
|
|||
|
|
+ type: "object"
|
|||
|
|
+ properties:
|
|||
|
|
+ domainName:
|
|||
|
|
+ type: "string"
|
|||
|
|
+ description: "domain name"
|
|||
|
|
+ hostIds:
|
|||
|
|
+ type: "array"
|
|||
|
|
+ items:
|
|||
|
|
+ type: "integer"
|
|||
|
|
SingleConf:
|
|||
|
|
type: object
|
|||
|
|
properties:
|
|||
|
|
diff --git a/ragdoll/test/test_conf_model.py b/ragdoll/test/test_conf_model.py
|
|||
|
|
index 35d6a0b..e29b2f8 100644
|
|||
|
|
--- a/ragdoll/test/test_conf_model.py
|
|||
|
|
+++ b/ragdoll/test/test_conf_model.py
|
|||
|
|
@@ -13,7 +13,7 @@ from ragdoll.log.log import LOGGER
|
|||
|
|
from ragdoll.test import BaseTestCase
|
|||
|
|
from ragdoll.utils.yang_module import YangModule
|
|||
|
|
from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
-from ragdoll.controllers.format import Format
|
|||
|
|
+from ragdoll.utils.format import Format
|
|||
|
|
|
|||
|
|
class TestConfModel():
|
|||
|
|
""" Test config_model """
|
|||
|
|
diff --git a/ragdoll/url.py b/ragdoll/url.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..4272bba
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/url.py
|
|||
|
|
@@ -0,0 +1,62 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+# ******************************************************************************
|
|||
|
|
+# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. 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.
|
|||
|
|
+# ******************************************************************************/
|
|||
|
|
+"""
|
|||
|
|
+@FileName: url.py
|
|||
|
|
+@Time: 2024/3/4 10:31
|
|||
|
|
+@Author: JiaoSiMao
|
|||
|
|
+Description:
|
|||
|
|
+"""
|
|||
|
|
+from ragdoll.conf.constant import (
|
|||
|
|
+ CREATE_DOMAIN, DELETE_DOMAIN, QUERY_DOMAIN, ADD_HOST_IN_DOMAIN, DELETE_HOST_IN_DOMAIN, GET_HOST_BY_DOMAIN,
|
|||
|
|
+ ADD_MANAGEMENT_CONFS_IN_DOMAIN, UPLOAD_MANAGEMENT_CONFS_IN_DOMAIN, DELETE_MANAGEMENT_CONFS_IN_DOMAIN,
|
|||
|
|
+ GET_MANAGEMENT_CONFS_IN_DOMAIN, QUERY_CHANGELOG_OF_MANAGEMENT_CONFS_IN_DOMAIN, GET_SYNC_STATUS,
|
|||
|
|
+ QUERY_EXCEPTED_CONFS, QUERY_REAL_CONFS, SYNC_CONF_TO_HOST_FROM_DOMAIN, QUERY_SUPPORTED_CONFS, COMPARE_CONF_DIFF,
|
|||
|
|
+ BATCH_SYNC_CONF_TO_HOST_FROM_DOMAIN
|
|||
|
|
+)
|
|||
|
|
+from ragdoll.domain_manage import view as domain_view
|
|||
|
|
+from ragdoll.host_manage import view as host_view
|
|||
|
|
+from ragdoll.domain_conf_manage import view as domain_conf_view
|
|||
|
|
+from ragdoll.confs_manage import view as confs_view
|
|||
|
|
+URLS = []
|
|||
|
|
+
|
|||
|
|
+SPECIFIC_URLS = {
|
|||
|
|
+ "DOMAIN_URLS": [
|
|||
|
|
+ (domain_view.CreateDomain, CREATE_DOMAIN),
|
|||
|
|
+ (domain_view.DeleteDomain, DELETE_DOMAIN),
|
|||
|
|
+ (domain_view.QueryDomain, QUERY_DOMAIN),
|
|||
|
|
+ ],
|
|||
|
|
+ "HOST_URLS": [
|
|||
|
|
+ (host_view.AddHostInDomain, ADD_HOST_IN_DOMAIN),
|
|||
|
|
+ (host_view.DeleteHostInDomain, DELETE_HOST_IN_DOMAIN),
|
|||
|
|
+ (host_view.GetHostByDomainName, GET_HOST_BY_DOMAIN),
|
|||
|
|
+ ],
|
|||
|
|
+ "MANAGEMENT_URLS": [
|
|||
|
|
+ (domain_conf_view.AddManagementConfsInDomain, ADD_MANAGEMENT_CONFS_IN_DOMAIN),
|
|||
|
|
+ (domain_conf_view.UploadManagementConfsInDomain, UPLOAD_MANAGEMENT_CONFS_IN_DOMAIN),
|
|||
|
|
+ (domain_conf_view.DeleteManagementConfsInDomain, DELETE_MANAGEMENT_CONFS_IN_DOMAIN),
|
|||
|
|
+ (domain_conf_view.GetManagementConfsInDomain, GET_MANAGEMENT_CONFS_IN_DOMAIN),
|
|||
|
|
+ (domain_conf_view.QueryChangelogOfManagementConfsInDomain, QUERY_CHANGELOG_OF_MANAGEMENT_CONFS_IN_DOMAIN),
|
|||
|
|
+ ],
|
|||
|
|
+ "CONFS_URLS": [
|
|||
|
|
+ (confs_view.GetTheSyncStatusOfDomain, GET_SYNC_STATUS),
|
|||
|
|
+ (confs_view.QueryExceptedConfs, QUERY_EXCEPTED_CONFS),
|
|||
|
|
+ (confs_view.QueryRealConfs, QUERY_REAL_CONFS),
|
|||
|
|
+ (confs_view.SyncConfToHostFromDomain, SYNC_CONF_TO_HOST_FROM_DOMAIN),
|
|||
|
|
+ (confs_view.QuerySupportedConfs, QUERY_SUPPORTED_CONFS),
|
|||
|
|
+ (confs_view.CompareConfDiff, COMPARE_CONF_DIFF),
|
|||
|
|
+ (confs_view.BatchSyncConfToHostFromDomain, BATCH_SYNC_CONF_TO_HOST_FROM_DOMAIN),
|
|||
|
|
+ ]
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+for _, value in SPECIFIC_URLS.items():
|
|||
|
|
+ URLS.extend(value)
|
|||
|
|
diff --git a/ragdoll/util.py b/ragdoll/util.py
|
|||
|
|
index edb2251..6d671c3 100644
|
|||
|
|
--- a/ragdoll/util.py
|
|||
|
|
+++ b/ragdoll/util.py
|
|||
|
|
@@ -1,7 +1,6 @@
|
|||
|
|
import datetime
|
|||
|
|
|
|||
|
|
import six
|
|||
|
|
-import typing
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _deserialize(data, klass):
|
|||
|
|
diff --git a/ragdoll/utils/conf_tools.py b/ragdoll/utils/conf_tools.py
|
|||
|
|
index 0ad1f8f..2fe0689 100644
|
|||
|
|
--- a/ragdoll/utils/conf_tools.py
|
|||
|
|
+++ b/ragdoll/utils/conf_tools.py
|
|||
|
|
@@ -6,7 +6,7 @@ from enum import Enum
|
|||
|
|
|
|||
|
|
from ragdoll.const.conf_handler_const import CONFIG
|
|||
|
|
from ragdoll.utils.git_tools import GitTools
|
|||
|
|
-from ragdoll.controllers.format import Format
|
|||
|
|
+from ragdoll.utils.format import Format
|
|||
|
|
from ragdoll.log.log import LOGGER
|
|||
|
|
from ragdoll.models.real_conf import RealConf
|
|||
|
|
from ragdoll.models.real_conf_path import RealConfPath
|
|||
|
|
@@ -80,10 +80,7 @@ class ConfTools(object):
|
|||
|
|
def listToDict(self, manaConfs):
|
|||
|
|
res = {}
|
|||
|
|
LOGGER.debug("manaConfs is : {}".format(manaConfs))
|
|||
|
|
- LOGGER.debug("the typr of manaConfs is : {}".format(type(manaConfs)))
|
|||
|
|
for d_conf in manaConfs:
|
|||
|
|
- LOGGER.debug("d_conf is : {}".format(d_conf))
|
|||
|
|
- LOGGER.debug("the type of d_conf is : {}".format(type(d_conf)))
|
|||
|
|
path = d_conf.get(PATH)
|
|||
|
|
value = d_conf.get(EXCEPTED_VALUE).strip()
|
|||
|
|
level = path.split("/")
|
|||
|
|
@@ -120,8 +117,6 @@ class ConfTools(object):
|
|||
|
|
"""
|
|||
|
|
realConfWithFeature = {}
|
|||
|
|
LOGGER.debug("featureList is : {}".format(featureList))
|
|||
|
|
- lenFeature = len(featureList)
|
|||
|
|
- tempRealConf = realConfDict
|
|||
|
|
d_real_file = {}
|
|||
|
|
d_real_file[featureList[1]] = realConfDict
|
|||
|
|
d_real_feature = {}
|
|||
|
|
@@ -173,11 +168,8 @@ class ConfTools(object):
|
|||
|
|
]
|
|||
|
|
"""
|
|||
|
|
res = []
|
|||
|
|
- conf_nums = len(realConfResText)
|
|||
|
|
LOGGER.debug("realConfResText is : {}".format(realConfResText))
|
|||
|
|
for d_conf in realConfResText:
|
|||
|
|
- LOGGER.debug("d_conf is : {}".format(d_conf))
|
|||
|
|
- LOGGER.debug("d_conf 's type is : {}".format(type(d_conf)))
|
|||
|
|
domainName = d_conf.get("domainName")
|
|||
|
|
hostId = d_conf.get("hostID")
|
|||
|
|
conf_base_infos = d_conf.get("confBaseInfos")
|
|||
|
|
@@ -187,7 +179,6 @@ class ConfTools(object):
|
|||
|
|
for d_conf_info in conf_base_infos:
|
|||
|
|
paths = d_conf_info.get("path").split(" ")
|
|||
|
|
confContents = json.loads(d_conf_info.get("confContents"))
|
|||
|
|
- LOGGER.debug("confContents is : {}".format(confContents))
|
|||
|
|
for d_path in paths:
|
|||
|
|
x_path = os.path.join(domainName, d_path)
|
|||
|
|
remove_feature_path = d_path.split("/")[2:]
|
|||
|
|
@@ -237,13 +228,11 @@ class ConfTools(object):
|
|||
|
|
dict1 = json.loads(real_conf)
|
|||
|
|
dict2 = json.loads(man_conf)
|
|||
|
|
|
|||
|
|
- res = ""
|
|||
|
|
+ res = SYNCHRONIZED
|
|||
|
|
for src_list, dst_list in zip(sorted(dict1), sorted(dict2)):
|
|||
|
|
if str(dict1[src_list]) != str(dict2[dst_list]):
|
|||
|
|
res = NOTSYNCHRONIZE
|
|||
|
|
- if not res:
|
|||
|
|
- res = SYNCHRONIZED
|
|||
|
|
-
|
|||
|
|
+ break
|
|||
|
|
return res
|
|||
|
|
|
|||
|
|
def getRpmInfo(self, path):
|
|||
|
|
@@ -253,15 +242,12 @@ class ConfTools(object):
|
|||
|
|
input: '/etc/yum.repos.d/openEuler.repo'
|
|||
|
|
output: openEuler-repos 1.0 3.0.oe1.aarch64
|
|||
|
|
"""
|
|||
|
|
- res = ""
|
|||
|
|
if not os.path.exists(path):
|
|||
|
|
return None
|
|||
|
|
cmd = "rpm -qf {}".format(path)
|
|||
|
|
gitTools = GitTools()
|
|||
|
|
package_string = gitTools.run_shell_return_output(cmd).decode()
|
|||
|
|
LOGGER.debug("package_string is : {}".format(package_string))
|
|||
|
|
- # lines = returnCode.rsplit(STRIKETHROUGH)
|
|||
|
|
- # res = lines[0]
|
|||
|
|
if 'not owned by any package' in package_string:
|
|||
|
|
return None, None, None
|
|||
|
|
pkg, arch = Format.rsplit(package_string, Format.arch_sep(package_string))
|
|||
|
|
@@ -270,7 +256,6 @@ class ConfTools(object):
|
|||
|
|
pkg, release = Format.rsplit(pkg, '-')
|
|||
|
|
name, version = Format.rsplit(pkg, '-')
|
|||
|
|
# If the value of epoch needs to be returned separately,
|
|||
|
|
- # epoch, version = version.split(':', 1) if ":" in version else ['0', version]
|
|||
|
|
return name, release, version
|
|||
|
|
|
|||
|
|
def getFileAttr(self, path):
|
|||
|
|
@@ -315,7 +300,6 @@ class ConfTools(object):
|
|||
|
|
d_lines = line.split(RightParen + TWOSPACE)
|
|||
|
|
for d_line in d_lines:
|
|||
|
|
d_line = d_line.lstrip()
|
|||
|
|
- # print("d_line is : {}".format(d_line))
|
|||
|
|
if d_line.startswith(ACCESS):
|
|||
|
|
fileAttr = d_line.split(FS)[0].split(LeftParen)[1]
|
|||
|
|
elif d_line.startswith(UID):
|
|||
|
|
@@ -355,15 +339,15 @@ class ConfTools(object):
|
|||
|
|
ll_res_list = ll_res.split(SPACE)
|
|||
|
|
|
|||
|
|
fileType = ll_res_list[0]
|
|||
|
|
- permssions = "0"
|
|||
|
|
+ permissions = "0"
|
|||
|
|
for perm in range(0, PERMISSION):
|
|||
|
|
items = fileType[1 + perm * PERMISSION: (perm + 1) * PERMISSION + 1]
|
|||
|
|
value = 0
|
|||
|
|
for d_item in items:
|
|||
|
|
d_item_value = self.switch_perm(d_item)
|
|||
|
|
value = value + d_item_value
|
|||
|
|
- permssions = permssions + str(value)
|
|||
|
|
- LOGGER.debug("the perssion is : {}".format(permssions))
|
|||
|
|
+ permissions = permissions + str(value)
|
|||
|
|
+ LOGGER.debug("the permission is : {}".format(permissions))
|
|||
|
|
|
|||
|
|
fileOwner = LeftParen + ll_res_list[2] + SPACE + ll_res_list[3] + RightParen
|
|||
|
|
LOGGER.debug("the fileOwner is : {}".format(fileOwner))
|
|||
|
|
@@ -446,9 +430,6 @@ class ConfTools(object):
|
|||
|
|
value = ""
|
|||
|
|
for count in range(0, len(real_conf)):
|
|||
|
|
d_real = real_conf[count]
|
|||
|
|
- # print("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&")
|
|||
|
|
- # print("d_real is : {}".format(d_real))
|
|||
|
|
- # print("path is : {}".format(path))
|
|||
|
|
if d_real.path == path:
|
|||
|
|
index = count
|
|||
|
|
value = d_real.real_value.strip()
|
|||
|
|
@@ -622,7 +603,43 @@ class ConfTools(object):
|
|||
|
|
object_file_url = "{address}:{port}{api}".format(address=object_file_address, api=object_file_api,
|
|||
|
|
port=object_file_port)
|
|||
|
|
|
|||
|
|
- url = {"collect_url": collect_url, "sync_url": sync_url, "object_file_url": object_file_url}
|
|||
|
|
+ batch_sync_address = ast.literal_eval(cf.get("sync", "batch_sync_address"))
|
|||
|
|
+ batch_sync_api = ast.literal_eval(cf.get("sync", "batch_sync_api"))
|
|||
|
|
+ batch_sync_port = str(cf.get("sync", "sync_port"))
|
|||
|
|
+ batch_sync_url = "{address}:{port}{api}".format(address=batch_sync_address, api=batch_sync_api,
|
|||
|
|
+ port=batch_sync_port)
|
|||
|
|
+
|
|||
|
|
+ host_sync_status_address = ast.literal_eval(cf.get("sync_status", "host_sync_status_address"))
|
|||
|
|
+ add_host_sync_status_api = ast.literal_eval(cf.get("sync_status", "add_host_sync_status_api"))
|
|||
|
|
+ delete_host_sync_status_api = ast.literal_eval(cf.get("sync_status", "delete_host_sync_status_api"))
|
|||
|
|
+ delete_all_host_sync_status_api = ast.literal_eval(cf.get("sync_status", "delete_all_host_sync_status_api"))
|
|||
|
|
+ host_sync_status_port = str(cf.get("sync_status", "host_sync_status_port"))
|
|||
|
|
+ add_host_sync_status_url = "{address}:{port}{api}".format(address=host_sync_status_address,
|
|||
|
|
+ api=add_host_sync_status_api,
|
|||
|
|
+ port=host_sync_status_port)
|
|||
|
|
+ delete_host_sync_status_url = "{address}:{port}{api}".format(address=host_sync_status_address,
|
|||
|
|
+ api=delete_host_sync_status_api,
|
|||
|
|
+ port=host_sync_status_port)
|
|||
|
|
+ delete_all_host_sync_status_url = "{address}:{port}{api}".format(address=host_sync_status_address,
|
|||
|
|
+ api=delete_all_host_sync_status_api,
|
|||
|
|
+ port=host_sync_status_port)
|
|||
|
|
+
|
|||
|
|
+ conf_trace_mgmt_address = ast.literal_eval(cf.get("conf_trace", "conf_trace_mgmt_address"))
|
|||
|
|
+ conf_trace_mgmt_api = ast.literal_eval(cf.get("conf_trace", "conf_trace_mgmt_api"))
|
|||
|
|
+ conf_trace_delete_api = ast.literal_eval(cf.get("conf_trace", "conf_trace_delete_api"))
|
|||
|
|
+ conf_trace_port = str(cf.get("conf_trace", "conf_trace_port"))
|
|||
|
|
+ conf_trace_mgmt_url = "{address}:{port}{api}".format(address=conf_trace_mgmt_address,
|
|||
|
|
+ api=conf_trace_mgmt_api,
|
|||
|
|
+ port=conf_trace_port)
|
|||
|
|
+ conf_trace_delete_url = "{address}:{port}{api}".format(address=conf_trace_mgmt_address,
|
|||
|
|
+ api=conf_trace_delete_api,
|
|||
|
|
+ port=conf_trace_port)
|
|||
|
|
+
|
|||
|
|
+ url = {"collect_url": collect_url, "sync_url": sync_url, "object_file_url": object_file_url,
|
|||
|
|
+ "batch_sync_url": batch_sync_url, "add_host_sync_status_url": add_host_sync_status_url,
|
|||
|
|
+ "delete_host_sync_status_url": delete_host_sync_status_url,
|
|||
|
|
+ "delete_all_host_sync_status_url": delete_all_host_sync_status_url,
|
|||
|
|
+ "conf_trace_mgmt_url": conf_trace_mgmt_url, "conf_trace_delete_url": conf_trace_delete_url}
|
|||
|
|
return url
|
|||
|
|
|
|||
|
|
def load_port_by_conf(self):
|
|||
|
|
diff --git a/ragdoll/utils/format.py b/ragdoll/utils/format.py
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..a674b44
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/ragdoll/utils/format.py
|
|||
|
|
@@ -0,0 +1,1138 @@
|
|||
|
|
+import os
|
|||
|
|
+import re
|
|||
|
|
+import json
|
|||
|
|
+import configparser
|
|||
|
|
+import ast
|
|||
|
|
+
|
|||
|
|
+import requests
|
|||
|
|
+
|
|||
|
|
+from ragdoll.log.log import LOGGER
|
|||
|
|
+
|
|||
|
|
+from ragdoll.const.conf_handler_const import NOT_SYNCHRONIZE, SYNCHRONIZED, CONFIG, \
|
|||
|
|
+ DIRECTORY_FILE_PATH_LIST
|
|||
|
|
+from ragdoll.models import ConfSyncedRes
|
|||
|
|
+from ragdoll.models.base_response import BaseResponse # noqa: E501
|
|||
|
|
+from ragdoll.models.conf_file import ConfFile
|
|||
|
|
+from ragdoll.models.conf_files import ConfFiles
|
|||
|
|
+from ragdoll.models.host import Host # noqa: E501
|
|||
|
|
+from ragdoll.utils.host_tools import HostTools
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+class Format(object):
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def domainCheck(domainName):
|
|||
|
|
+ res = True
|
|||
|
|
+ if not re.match(r"^[A-Za-z0-9_\.-]*$", domainName) or domainName == "" or len(domainName) > 255:
|
|||
|
|
+ res = False
|
|||
|
|
+ return res
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def isDomainExist(domainName):
|
|||
|
|
+ TARGETDIR = Format.get_git_dir()
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, domainName)
|
|||
|
|
+ if os.path.exists(domainPath):
|
|||
|
|
+ return True
|
|||
|
|
+
|
|||
|
|
+ return False
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def spliceAllSuccString(obj, operation, succDomain):
|
|||
|
|
+ """
|
|||
|
|
+ docstring
|
|||
|
|
+ """
|
|||
|
|
+ codeString = "All {obj} {oper} successfully, {succ} {obj} in total.".format( \
|
|||
|
|
+ obj=obj, oper=operation, succ=len(succDomain))
|
|||
|
|
+ return codeString
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def splicErrorString(obj, operation, succDomain, failDomain):
|
|||
|
|
+ """
|
|||
|
|
+ docstring
|
|||
|
|
+ """
|
|||
|
|
+ codeString = "{succ} {obj} {oper} successfully, {fail} {obj} {oper} failed.".format( \
|
|||
|
|
+ succ=len(succDomain), obj=obj, oper=operation, fail=len(failDomain))
|
|||
|
|
+
|
|||
|
|
+ succString = "\n"
|
|||
|
|
+ if len(succDomain) > 0:
|
|||
|
|
+ succString = "These are successful: "
|
|||
|
|
+ for succName in succDomain:
|
|||
|
|
+ succString += succName + " "
|
|||
|
|
+ succString += "."
|
|||
|
|
+
|
|||
|
|
+ if len(failDomain) > 0:
|
|||
|
|
+ failString = "These are failed: "
|
|||
|
|
+ for failName in failDomain:
|
|||
|
|
+ failString += failName + " "
|
|||
|
|
+ return codeString + succString + failString
|
|||
|
|
+
|
|||
|
|
+ return codeString + succString
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def two_abs_join(abs1, abs2):
|
|||
|
|
+ """
|
|||
|
|
+ Absolute path Joins two absolute paths together
|
|||
|
|
+ :param abs1: main path
|
|||
|
|
+ :param abs2: the spliced path
|
|||
|
|
+ :return: together the path
|
|||
|
|
+ """
|
|||
|
|
+ # 1. Format path (change \\ in path to \)
|
|||
|
|
+ abs2 = os.fspath(abs2)
|
|||
|
|
+
|
|||
|
|
+ # 2. Split the path file
|
|||
|
|
+ abs2 = os.path.splitdrive(abs2)[1]
|
|||
|
|
+ # 3. Remove the beginning '/'
|
|||
|
|
+ abs2 = abs2.strip('\\/') or abs2
|
|||
|
|
+ return os.path.abspath(os.path.join(abs1, abs2))
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def isContainedHostIdInfile(f_file, content):
|
|||
|
|
+ isContained = False
|
|||
|
|
+ with open(f_file, 'r') as d_file:
|
|||
|
|
+ for line in d_file.readlines():
|
|||
|
|
+ line_dict = json.loads(str(ast.literal_eval(line)).replace("'", "\""))
|
|||
|
|
+ if content == line_dict["host_id"]:
|
|||
|
|
+ isContained = True
|
|||
|
|
+ break
|
|||
|
|
+ return isContained
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def isContainedHostIdInOtherDomain(content):
|
|||
|
|
+ from ragdoll.conf.constant import TARGETDIR
|
|||
|
|
+ isContained = False
|
|||
|
|
+ contents = os.listdir(TARGETDIR)
|
|||
|
|
+ folders = [f for f in contents if os.path.isdir(os.path.join(TARGETDIR, f))]
|
|||
|
|
+ for folder in folders:
|
|||
|
|
+ hostPath = os.path.join(os.path.join(TARGETDIR, folder), "hostRecord.txt")
|
|||
|
|
+ if os.path.isfile(hostPath):
|
|||
|
|
+ with open(hostPath, 'r') as d_file:
|
|||
|
|
+ for line in d_file.readlines():
|
|||
|
|
+ line_dict = json.loads(str(ast.literal_eval(line)).replace("'", "\""))
|
|||
|
|
+ if content == line_dict["host_id"]:
|
|||
|
|
+ isContained = True
|
|||
|
|
+ break
|
|||
|
|
+ return isContained
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def addHostToFile(d_file, host):
|
|||
|
|
+ host = {'host_id': host["hostId"], 'ip': host["ip"], 'ipv6': host["ipv6"]}
|
|||
|
|
+ info_json = json.dumps(str(host), sort_keys=False, indent=4, separators=(',', ': '))
|
|||
|
|
+ os.umask(0o077)
|
|||
|
|
+ with open(d_file, 'a+') as host_file:
|
|||
|
|
+ host_file.write(info_json)
|
|||
|
|
+ host_file.write("\n")
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def getSubDirFiles(path):
|
|||
|
|
+ """
|
|||
|
|
+ desc: Subdirectory records and files need to be logged to the successConf
|
|||
|
|
+ """
|
|||
|
|
+ fileRealPathList = []
|
|||
|
|
+ fileXPathlist = []
|
|||
|
|
+ for root, dirs, files in os.walk(path):
|
|||
|
|
+ if len(files) > 0:
|
|||
|
|
+ preXpath = root.split('/', 3)[3]
|
|||
|
|
+ for d_file in files:
|
|||
|
|
+ xpath = os.path.join(preXpath, d_file)
|
|||
|
|
+ fileXPathlist.append(xpath)
|
|||
|
|
+ realPath = os.path.join(root, d_file)
|
|||
|
|
+ fileRealPathList.append(realPath)
|
|||
|
|
+
|
|||
|
|
+ return fileRealPathList, fileXPathlist
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def isHostInDomain(domainName):
|
|||
|
|
+ """
|
|||
|
|
+ desc: Query domain Whether host information is configured in the domain
|
|||
|
|
+ """
|
|||
|
|
+ isHostInDomain = False
|
|||
|
|
+ TARGETDIR = Format.get_git_dir()
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, domainName)
|
|||
|
|
+ hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
+ if os.path.isfile(hostPath):
|
|||
|
|
+ isHostInDomain = True
|
|||
|
|
+
|
|||
|
|
+ return isHostInDomain
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def isHostIdExist(hostPath, hostId):
|
|||
|
|
+ """
|
|||
|
|
+ desc: Query hostId exists within the current host domain management
|
|||
|
|
+ """
|
|||
|
|
+ isHostIdExist = False
|
|||
|
|
+ if os.path.isfile(hostPath) and os.stat(hostPath).st_size > 0:
|
|||
|
|
+ with open(hostPath) as h_file:
|
|||
|
|
+ for line in h_file.readlines():
|
|||
|
|
+ if hostId in line:
|
|||
|
|
+ isHostIdExist = True
|
|||
|
|
+ break
|
|||
|
|
+
|
|||
|
|
+ return isHostIdExist
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_file_content_by_readlines(d_file):
|
|||
|
|
+ """
|
|||
|
|
+ desc: remove empty lines and comments from d_file
|
|||
|
|
+ """
|
|||
|
|
+ res = []
|
|||
|
|
+ try:
|
|||
|
|
+ with open(d_file, 'r') as s_f:
|
|||
|
|
+ lines = s_f.readlines()
|
|||
|
|
+ for line in lines:
|
|||
|
|
+ tmp = line.strip()
|
|||
|
|
+ if not len(tmp) or tmp.startswith("#"):
|
|||
|
|
+ continue
|
|||
|
|
+ res.append(line)
|
|||
|
|
+ except FileNotFoundError:
|
|||
|
|
+ LOGGER.error(f"File not found: {d_file}")
|
|||
|
|
+ except IOError as e:
|
|||
|
|
+ LOGGER.error(f"IO error: {e}")
|
|||
|
|
+ except Exception as e:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {e}")
|
|||
|
|
+ return res
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_file_content_by_read(d_file):
|
|||
|
|
+ """
|
|||
|
|
+ desc: return a string after read the d_file
|
|||
|
|
+ """
|
|||
|
|
+ if not os.path.exists(d_file):
|
|||
|
|
+ return ""
|
|||
|
|
+ with open(d_file, 'r') as s_f:
|
|||
|
|
+ lines = s_f.read()
|
|||
|
|
+ return lines
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def rsplit(_str, seps):
|
|||
|
|
+ """
|
|||
|
|
+ Splits _str by the first sep in seps that is found from the right side.
|
|||
|
|
+ Returns a tuple without the separator.
|
|||
|
|
+ """
|
|||
|
|
+ for idx, ch in enumerate(reversed(_str)):
|
|||
|
|
+ if ch in seps:
|
|||
|
|
+ return _str[0:-idx - 1], _str[-idx:]
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def arch_sep(package_string):
|
|||
|
|
+ """
|
|||
|
|
+ Helper method for finding if arch separator is '.' or '-'
|
|||
|
|
+
|
|||
|
|
+ Args:
|
|||
|
|
+ package_string (str): dash separated package string such as 'bash-4.2.39-3.el7'.
|
|||
|
|
+
|
|||
|
|
+ Returns:
|
|||
|
|
+ str: arch separator
|
|||
|
|
+ """
|
|||
|
|
+ return '.' if package_string.rfind('.') > package_string.rfind('-') else '-'
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def set_file_content_by_path(content, path):
|
|||
|
|
+ res = 0
|
|||
|
|
+ if os.path.exists(path):
|
|||
|
|
+ with open(path, 'w+') as d_file:
|
|||
|
|
+ for d_cont in content:
|
|||
|
|
+ d_file.write(d_cont)
|
|||
|
|
+ d_file.write("\n")
|
|||
|
|
+ res = 1
|
|||
|
|
+ return res
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_git_dir():
|
|||
|
|
+ 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")
|
|||
|
|
+ git_dir = ast.literal_eval(cf.get("git", "git_dir"))
|
|||
|
|
+ return git_dir
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_hostinfo_by_domain(domainName):
|
|||
|
|
+ """
|
|||
|
|
+ desc: Query hostinfo by domainname
|
|||
|
|
+ """
|
|||
|
|
+ LOGGER.debug("Get hostinfo by domain : {}".format(domainName))
|
|||
|
|
+ TARGETDIR = Format.get_git_dir()
|
|||
|
|
+ hostlist = []
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, domainName)
|
|||
|
|
+ hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
+ if not os.path.exists(hostPath):
|
|||
|
|
+ return hostlist
|
|||
|
|
+ try:
|
|||
|
|
+ with open(hostPath, 'r') as d_file:
|
|||
|
|
+ for line in d_file.readlines():
|
|||
|
|
+ json_str = json.loads(line)
|
|||
|
|
+ host_json = ast.literal_eval(json_str)
|
|||
|
|
+ hostId = host_json["host_id"]
|
|||
|
|
+ ip = host_json["ip"]
|
|||
|
|
+ ipv6 = host_json["ipv6"]
|
|||
|
|
+ host = Host(host_id=hostId, ip=ip, ipv6=ipv6)
|
|||
|
|
+ hostlist.append(host.to_dict())
|
|||
|
|
+ except OSError as err:
|
|||
|
|
+ LOGGER.error("OS error: {0}".format(err))
|
|||
|
|
+ return hostlist
|
|||
|
|
+ if len(hostlist) == 0:
|
|||
|
|
+ LOGGER.debug("Hostlist is empty !")
|
|||
|
|
+ else:
|
|||
|
|
+ LOGGER.debug("Hostlist is : {}".format(hostlist))
|
|||
|
|
+ return hostlist
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_host_id_by_ip(ip, domainName):
|
|||
|
|
+ """
|
|||
|
|
+ desc: Query hostinfo by host ip
|
|||
|
|
+ """
|
|||
|
|
+ LOGGER.debug("Get hostinfo by ip : {}".format(ip))
|
|||
|
|
+ TARGET_DIR = Format.get_git_dir()
|
|||
|
|
+ hostlist = []
|
|||
|
|
+ domainPath = os.path.join(TARGET_DIR, domainName)
|
|||
|
|
+ hostPath = os.path.join(domainPath, "hostRecord.txt")
|
|||
|
|
+ if not os.path.isfile(hostPath) or os.stat(hostPath).st_size == 0:
|
|||
|
|
+ return hostlist
|
|||
|
|
+ try:
|
|||
|
|
+ with open(hostPath, 'r') as d_file:
|
|||
|
|
+ for line in d_file.readlines():
|
|||
|
|
+ json_str = json.loads(line)
|
|||
|
|
+ host_json = ast.literal_eval(json_str)
|
|||
|
|
+ if host_json["ip"] == ip:
|
|||
|
|
+ return host_json["host_id"]
|
|||
|
|
+ except OSError as err:
|
|||
|
|
+ LOGGER.error("OS error: {0}".format(err))
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_manageconf_by_domain(domain):
|
|||
|
|
+ LOGGER.debug("Get managerconf by domain : {}".format(domain))
|
|||
|
|
+ expected_conf_lists = ConfFiles(domain_name=domain, conf_files=[])
|
|||
|
|
+ TARGETDIR = Format.get_git_dir()
|
|||
|
|
+ domainPath = os.path.join(TARGETDIR, domain)
|
|||
|
|
+ from ragdoll.utils.yang_module import YangModule
|
|||
|
|
+ for root, dirs, files in os.walk(domainPath):
|
|||
|
|
+ if len(files) > 0 and len(root.split('/')) > 3:
|
|||
|
|
+ if "hostRecord.txt" in files:
|
|||
|
|
+ continue
|
|||
|
|
+ for d_file in files:
|
|||
|
|
+ d_file_path = os.path.join(root, d_file)
|
|||
|
|
+ contents = Format.get_file_content_by_read(d_file_path)
|
|||
|
|
+ feature = os.path.join(root.split('/')[-1], d_file)
|
|||
|
|
+ yang_modules = YangModule()
|
|||
|
|
+ d_module = yang_modules.getModuleByFeature(feature)
|
|||
|
|
+ file_lists = yang_modules.getFilePathInModdule(yang_modules.module_list)
|
|||
|
|
+ file_path = file_lists.get(d_module.name()).split(":")[-1]
|
|||
|
|
+
|
|||
|
|
+ conf = ConfFile(file_path=file_path, contents=contents)
|
|||
|
|
+ expected_conf_lists.conf_files.append(conf.to_dict())
|
|||
|
|
+
|
|||
|
|
+ LOGGER.debug("Expected_conf_lists is :{}".format(expected_conf_lists))
|
|||
|
|
+ return expected_conf_lists.to_dict()
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_realconf_by_domain_and_host(domain, exist_host, access_token):
|
|||
|
|
+ res = []
|
|||
|
|
+ conf_files = Format.get_manageconf_by_domain(domain)
|
|||
|
|
+ # get the real conf in host
|
|||
|
|
+ conf_list = []
|
|||
|
|
+ from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
+ from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ for d_conf in conf_files.get("conf_files"):
|
|||
|
|
+ file_path = d_conf.get("file_path").split(":")[-1]
|
|||
|
|
+ if file_path not in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ conf_list.append(file_path)
|
|||
|
|
+ else:
|
|||
|
|
+ d_conf_cs = d_conf.get("contents")
|
|||
|
|
+ d_conf_contents = json.loads(d_conf_cs)
|
|||
|
|
+ for d_conf_key, d_conf_value in d_conf_contents.items():
|
|||
|
|
+ conf_list.append(d_conf_key)
|
|||
|
|
+ get_real_conf_body = {}
|
|||
|
|
+ get_real_conf_body_info = []
|
|||
|
|
+ for d_host in exist_host:
|
|||
|
|
+ get_real_conf_body_infos = {}
|
|||
|
|
+ get_real_conf_body_infos["host_id"] = d_host
|
|||
|
|
+ get_real_conf_body_infos["config_list"] = conf_list
|
|||
|
|
+ get_real_conf_body_info.append(get_real_conf_body_infos)
|
|||
|
|
+ get_real_conf_body["infos"] = get_real_conf_body_info
|
|||
|
|
+ url = conf_tools.load_url_by_conf().get("collect_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": access_token}
|
|||
|
|
+ try:
|
|||
|
|
+ response = requests.post(url, data=json.dumps(get_real_conf_body), headers=headers) # post request
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ codeNum = 500
|
|||
|
|
+ codeString = "Failed to obtain the actual configuration, please check the interface of config/collect."
|
|||
|
|
+ base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
+ return base_rsp, codeNum
|
|||
|
|
+ resp = json.loads(response.text).get("data")
|
|||
|
|
+ resp_code = json.loads(response.text).get("code")
|
|||
|
|
+ if (resp_code != "200") and (resp_code != "206"):
|
|||
|
|
+ return res
|
|||
|
|
+
|
|||
|
|
+ if not resp or len(resp) == 0:
|
|||
|
|
+ return res
|
|||
|
|
+
|
|||
|
|
+ success_lists = {}
|
|||
|
|
+ failed_lists = {}
|
|||
|
|
+
|
|||
|
|
+ for d_res in resp:
|
|||
|
|
+ d_host_id = d_res.get("host_id")
|
|||
|
|
+ fail_files = d_res.get("fail_files")
|
|||
|
|
+ if len(fail_files) > 0:
|
|||
|
|
+ failed_lists["host_id"] = d_host_id
|
|||
|
|
+ failed_lists_conf = []
|
|||
|
|
+ for d_failed in fail_files:
|
|||
|
|
+ failed_lists_conf.append(d_failed)
|
|||
|
|
+ failed_lists["failed_conf"] = failed_lists_conf
|
|||
|
|
+ failed_lists["success_conf"] = []
|
|||
|
|
+ else:
|
|||
|
|
+ success_lists["host_id"] = d_host_id
|
|||
|
|
+ success_lists["success_conf"] = []
|
|||
|
|
+ success_lists["failed_conf"] = []
|
|||
|
|
+
|
|||
|
|
+ read_conf_info = {"domainName": domain, "hostID": d_host_id, "confBaseInfos": []}
|
|||
|
|
+ d_res_infos = d_res.get("infos")
|
|||
|
|
+
|
|||
|
|
+ real_directory_conf = {}
|
|||
|
|
+ real_directory_conf_list = {}
|
|||
|
|
+ object_parse = ObjectParse()
|
|||
|
|
+ for d_file in d_res_infos:
|
|||
|
|
+ content = d_file.get("content")
|
|||
|
|
+ file_path = d_file.get("path")
|
|||
|
|
+ file_atrr = d_file.get("file_attr").get("mode")
|
|||
|
|
+ file_owner = "({}, {})".format(d_file.get("file_attr").get("group"),
|
|||
|
|
+ d_file.get("file_attr").get("owner"))
|
|||
|
|
+ directory_flag = False
|
|||
|
|
+ for dir_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ if str(file_path).find(dir_path) != -1:
|
|||
|
|
+ if real_directory_conf.get(dir_path) is None:
|
|||
|
|
+ real_directory_conf_list[dir_path] = list()
|
|||
|
|
+ real_directory_conf[dir_path] = {"filePath": dir_path, "fileAttr": file_atrr,
|
|||
|
|
+ "fileOwner": file_owner, "confContents": ""}
|
|||
|
|
+ directory_conf = dict()
|
|||
|
|
+ directory_conf["path"] = file_path
|
|||
|
|
+ directory_conf["content"] = content
|
|||
|
|
+ real_directory_conf_list.get(dir_path).append(directory_conf)
|
|||
|
|
+ directory_flag = True
|
|||
|
|
+ break
|
|||
|
|
+ if not directory_flag:
|
|||
|
|
+ Format.deal_conf_list_content(content, d_file, file_path, object_parse, read_conf_info)
|
|||
|
|
+ if len(fail_files) > 0:
|
|||
|
|
+ failed_lists.get("success_conf").append(file_path)
|
|||
|
|
+ else:
|
|||
|
|
+ success_lists.get("success_conf").append(file_path)
|
|||
|
|
+
|
|||
|
|
+ for dir_path, dir_value in real_directory_conf_list.items():
|
|||
|
|
+ content_string = object_parse.parse_directory_single_conf_to_json(dir_value,
|
|||
|
|
+ real_directory_conf[
|
|||
|
|
+ dir_path]["filePath"])
|
|||
|
|
+ real_directory_conf[dir_path]["confContents"] = content_string
|
|||
|
|
+ real_conf_base_info = real_directory_conf.get(dir_path)
|
|||
|
|
+
|
|||
|
|
+ read_conf_info.get("confBaseInfos").append(real_conf_base_info)
|
|||
|
|
+ res.append(read_conf_info)
|
|||
|
|
+ return res
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def deal_conf_list_content(content, d_file, file_path, object_parse, read_conf_info):
|
|||
|
|
+ content_string = object_parse.parse_conf_to_json(file_path, content)
|
|||
|
|
+ file_atrr = d_file.get("file_attr").get("mode")
|
|||
|
|
+ file_owner = "({}, {})".format(d_file.get("file_attr").get("group"),
|
|||
|
|
+ d_file.get("file_attr").get("owner"))
|
|||
|
|
+ real_conf_base_info = {"path": file_path, "filePath": file_path, "fileAttr": file_atrr, "fileOwner": file_owner,
|
|||
|
|
+ "confContents": content_string}
|
|||
|
|
+ read_conf_info.get("confBaseInfos").append(real_conf_base_info)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def check_domain_param(domain):
|
|||
|
|
+ code_num = 200
|
|||
|
|
+ base_resp = None
|
|||
|
|
+ check_res = Format.domainCheck(domain)
|
|||
|
|
+ if not check_res:
|
|||
|
|
+ num = 400
|
|||
|
|
+ base_rsp = BaseResponse(num, "Failed to verify the input parameter, please check the input parameters.")
|
|||
|
|
+ return base_rsp, num
|
|||
|
|
+
|
|||
|
|
+ # check the domian is exist
|
|||
|
|
+ is_exist = Format.isDomainExist(domain)
|
|||
|
|
+ if not is_exist:
|
|||
|
|
+ code_num = 404
|
|||
|
|
+ base_rsp = BaseResponse(code_num, "The current domain does not exist, please create the domain first.")
|
|||
|
|
+ return base_rsp, code_num
|
|||
|
|
+
|
|||
|
|
+ # get the exist result of the host in domain
|
|||
|
|
+ is_host_list_exist = Format.isHostInDomain(domain)
|
|||
|
|
+ if not is_host_list_exist:
|
|||
|
|
+ code_num = 404
|
|||
|
|
+ base_resp = BaseResponse(code_num, "The host information is not set in the current domain." +
|
|||
|
|
+ "Please add the host information first")
|
|||
|
|
+ return base_resp, code_num
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_hostid_list_by_domain(domain):
|
|||
|
|
+ host_ids = []
|
|||
|
|
+ res_text = Format.get_hostinfo_by_domain(domain)
|
|||
|
|
+ if len(res_text) == 0:
|
|||
|
|
+ return host_ids
|
|||
|
|
+
|
|||
|
|
+ host_tools = HostTools()
|
|||
|
|
+ host_ids = host_tools.getHostList(res_text)
|
|||
|
|
+ return host_ids
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_domain_conf(domain):
|
|||
|
|
+ code_num = 200
|
|||
|
|
+ base_resp = None
|
|||
|
|
+ # get the host info in domain
|
|||
|
|
+ LOGGER.debug("Get the conf by domain: {}.".format(domain))
|
|||
|
|
+ code_string = "get domain confs succeed"
|
|||
|
|
+ host_ids = Format.get_hostid_list_by_domain(domain)
|
|||
|
|
+ if not host_ids:
|
|||
|
|
+ code_num = 404
|
|||
|
|
+ code_string = "The host currently controlled in the domain is empty. Please add host information to the " \
|
|||
|
|
+ "domain. "
|
|||
|
|
+ return code_num, code_string, list()
|
|||
|
|
+
|
|||
|
|
+ # get the managent conf in domain
|
|||
|
|
+ man_conf_res_text = Format.get_manageconf_by_domain(domain)
|
|||
|
|
+ manage_confs = man_conf_res_text.get("conf_files")
|
|||
|
|
+
|
|||
|
|
+ if len(manage_confs) == 0:
|
|||
|
|
+ code_num = 404
|
|||
|
|
+ code_string = "The configuration is not set in the current domain. Please add the configuration " \
|
|||
|
|
+ "information first. "
|
|||
|
|
+ return code_num, code_string, list()
|
|||
|
|
+ return code_num, code_string, manage_confs
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def diff_mangeconf_with_realconf(domain, real_conf_res_text, manage_confs):
|
|||
|
|
+ sync_status = {"domainName": domain, "hostStatus": []}
|
|||
|
|
+
|
|||
|
|
+ from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
+
|
|||
|
|
+ for d_real_conf in real_conf_res_text:
|
|||
|
|
+ host_id = d_real_conf["hostID"]
|
|||
|
|
+ host_sync_status = {"hostId": host_id, "syncStatus": []}
|
|||
|
|
+ d_real_conf_base = d_real_conf["confBaseInfos"]
|
|||
|
|
+ for d_conf in d_real_conf_base:
|
|||
|
|
+ directory_conf_is_synced = {"file_path": "", "isSynced": "", "singleConf": []}
|
|||
|
|
+ d_conf_path = d_conf["filePath"]
|
|||
|
|
+
|
|||
|
|
+ object_parse = ObjectParse()
|
|||
|
|
+ # get the conf type and model
|
|||
|
|
+ conf_type, conf_model = Format.get_conf_type_model(d_conf_path, object_parse)
|
|||
|
|
+
|
|||
|
|
+ Format.deal_conf_sync_status(conf_model, d_conf, d_conf_path, directory_conf_is_synced,
|
|||
|
|
+ host_sync_status, manage_confs)
|
|||
|
|
+
|
|||
|
|
+ if len(directory_conf_is_synced.get("singleConf")) > 0:
|
|||
|
|
+ synced_flag = SYNCHRONIZED
|
|||
|
|
+ for single_config in directory_conf_is_synced.get("singleConf"):
|
|||
|
|
+ if single_config.get("singleIsSynced") == SYNCHRONIZED:
|
|||
|
|
+ continue
|
|||
|
|
+ else:
|
|||
|
|
+ synced_flag = NOT_SYNCHRONIZE
|
|||
|
|
+ directory_conf_is_synced["isSynced"] = synced_flag
|
|||
|
|
+ host_sync_status.get("syncStatus").append(directory_conf_is_synced)
|
|||
|
|
+ sync_status.get("hostStatus").append(host_sync_status)
|
|||
|
|
+ return sync_status
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def deal_conf_sync_status(conf_model, d_conf, d_conf_path, directory_conf_is_synced, host_sync_status,
|
|||
|
|
+ manage_confs):
|
|||
|
|
+ comp_res = ""
|
|||
|
|
+ if d_conf_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ confContents = json.loads(d_conf["confContents"])
|
|||
|
|
+ directory_conf_contents = ""
|
|||
|
|
+ for d_man_conf in manage_confs:
|
|||
|
|
+ d_man_conf_path = d_man_conf.get("file_path")
|
|||
|
|
+ if d_man_conf_path != d_conf_path:
|
|||
|
|
+ continue
|
|||
|
|
+ else:
|
|||
|
|
+ directory_conf_is_synced["file_path"] = d_conf_path
|
|||
|
|
+ directory_conf_contents = d_man_conf.get("contents")
|
|||
|
|
+
|
|||
|
|
+ directory_conf_contents_dict = json.loads(directory_conf_contents)
|
|||
|
|
+
|
|||
|
|
+ for dir_conf_content_key, dir_conf_content_value in directory_conf_contents_dict.items():
|
|||
|
|
+ if dir_conf_content_key not in confContents.keys():
|
|||
|
|
+ single_conf = {"singleFilePath": dir_conf_content_key, "singleIsSynced": NOT_SYNCHRONIZE}
|
|||
|
|
+ directory_conf_is_synced.get("singleConf").append(single_conf)
|
|||
|
|
+ else:
|
|||
|
|
+ dst_conf = confContents.get(dir_conf_content_key)
|
|||
|
|
+ comp_res = conf_model.conf_compare(dir_conf_content_value, dst_conf)
|
|||
|
|
+ single_conf = {"singleFilePath": dir_conf_content_key, "singleIsSynced": comp_res}
|
|||
|
|
+ directory_conf_is_synced.get("singleConf").append(single_conf)
|
|||
|
|
+ else:
|
|||
|
|
+ for d_man_conf in manage_confs:
|
|||
|
|
+ if d_man_conf.get("file_path").split(":")[-1] != d_conf_path:
|
|||
|
|
+ continue
|
|||
|
|
+ comp_res = conf_model.conf_compare(d_man_conf.get("contents"), d_conf["confContents"])
|
|||
|
|
+ conf_is_synced = {"file_path": d_conf_path, "isSynced": comp_res}
|
|||
|
|
+ host_sync_status.get("syncStatus").append(conf_is_synced)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def convert_real_conf(conf_model, conf_type, conf_info, conf_path, parse):
|
|||
|
|
+ # load yang model info
|
|||
|
|
+ yang_info = parse._yang_modules.getModuleByFilePath(conf_path)
|
|||
|
|
+ conf_model.load_yang_model(yang_info)
|
|||
|
|
+
|
|||
|
|
+ # load conf info
|
|||
|
|
+ if conf_type == "kv":
|
|||
|
|
+ spacer_type = parse._yang_modules.getSpacerInModdule([yang_info])
|
|||
|
|
+ conf_model.read_conf(conf_info, spacer_type, yang_info)
|
|||
|
|
+ else:
|
|||
|
|
+ conf_model.read_conf(conf_info)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def deal_conf_sync_status_for_db(conf_model, d_conf, d_conf_path, directory_conf_is_synced, host_sync_status,
|
|||
|
|
+ manage_confs):
|
|||
|
|
+ comp_res = ""
|
|||
|
|
+ if d_conf_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ confContents = d_conf.get("conf_contents")
|
|||
|
|
+ directory_conf_contents = ""
|
|||
|
|
+ for d_man_conf in manage_confs:
|
|||
|
|
+ d_man_conf_path = d_man_conf.get("file_path")
|
|||
|
|
+ if d_man_conf_path != d_conf_path:
|
|||
|
|
+ continue
|
|||
|
|
+ else:
|
|||
|
|
+ directory_conf_is_synced["file_path"] = d_conf_path
|
|||
|
|
+ directory_conf_contents = d_man_conf.get("contents")
|
|||
|
|
+
|
|||
|
|
+ directory_conf_contents_dict = json.loads(directory_conf_contents)
|
|||
|
|
+
|
|||
|
|
+ for dir_conf_content_key, dir_conf_content_value in directory_conf_contents_dict.items():
|
|||
|
|
+ if dir_conf_content_key not in confContents.keys():
|
|||
|
|
+ single_conf = {"singleFilePath": dir_conf_content_key, "singleIsSynced": NOT_SYNCHRONIZE}
|
|||
|
|
+ directory_conf_is_synced["singleConf"].append(single_conf)
|
|||
|
|
+ else:
|
|||
|
|
+ dst_conf = confContents.get(dir_conf_content_key)
|
|||
|
|
+ comp_res = conf_model.conf_compare(dir_conf_content_value, dst_conf)
|
|||
|
|
+ single_conf = {"singleFilePath": dir_conf_content_key, "singleIsSynced": comp_res}
|
|||
|
|
+ directory_conf_is_synced["singleConf"].append(single_conf)
|
|||
|
|
+ else:
|
|||
|
|
+ for d_man_conf in manage_confs:
|
|||
|
|
+ if d_man_conf.get("file_path").split(":")[-1] != d_conf_path:
|
|||
|
|
+ continue
|
|||
|
|
+ contents = d_man_conf.get("contents")
|
|||
|
|
+ comp_res = conf_model.conf_compare(contents, json.dumps(d_conf.get("conf_contents")))
|
|||
|
|
+ conf_is_synced = {"file_path": d_conf_path, "isSynced": comp_res}
|
|||
|
|
+ host_sync_status["syncStatus"].append(conf_is_synced)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_conf_type_model(d_conf_path, object_parse):
|
|||
|
|
+ for dir_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ if str(d_conf_path).find(dir_path) != -1:
|
|||
|
|
+ conf_type = object_parse.get_conf_type_by_conf_path(dir_path)
|
|||
|
|
+ conf_model = object_parse.create_conf_model_by_type(conf_type)
|
|||
|
|
+ else:
|
|||
|
|
+ conf_type = object_parse.get_conf_type_by_conf_path(d_conf_path)
|
|||
|
|
+ conf_model = object_parse.create_conf_model_by_type(conf_type)
|
|||
|
|
+ return conf_type, conf_model
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def deal_sync_res(conf_tools, contents, file_path, host_id, host_sync_result, object_parse, access_token):
|
|||
|
|
+ sync_conf_url = conf_tools.load_url_by_conf().get("sync_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": access_token}
|
|||
|
|
+ if file_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ conf_sync_res_list = []
|
|||
|
|
+ for directory_file_path, directory_content in json.loads(contents).items():
|
|||
|
|
+ content = object_parse.parse_json_to_conf(directory_file_path, directory_content)
|
|||
|
|
+ # Configuration to the host
|
|||
|
|
+ data = {"host_id": host_id, "file_path": directory_file_path, "content": content}
|
|||
|
|
+ try:
|
|||
|
|
+ sync_response = requests.put(sync_conf_url, data=json.dumps(data), headers=headers)
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ codeNum = 500
|
|||
|
|
+ codeString = "Failed to sync configuration, please check the interface of config/sync."
|
|||
|
|
+ base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
+ return base_rsp, codeNum
|
|||
|
|
+ resp_code = json.loads(sync_response.text).get('code')
|
|||
|
|
+ resp = json.loads(sync_response.text).get('data').get('resp')
|
|||
|
|
+
|
|||
|
|
+ if resp_code == "200" and resp.get('sync_result') is True:
|
|||
|
|
+ conf_sync_res_list.append("SUCCESS")
|
|||
|
|
+ else:
|
|||
|
|
+ conf_sync_res_list.append("FAILED")
|
|||
|
|
+ if "FAILED" in conf_sync_res_list:
|
|||
|
|
+ conf_sync_res = ConfSyncedRes(file_path=file_path, result="FAILED")
|
|||
|
|
+ else:
|
|||
|
|
+ conf_sync_res = ConfSyncedRes(file_path=file_path, result="SUCCESS")
|
|||
|
|
+ host_sync_result.sync_result.append(conf_sync_res)
|
|||
|
|
+ else:
|
|||
|
|
+ content = object_parse.parse_json_to_conf(file_path, contents)
|
|||
|
|
+ # Configuration to the host
|
|||
|
|
+ data = {"host_id": host_id, "file_path": file_path, "content": content}
|
|||
|
|
+ sync_response = requests.put(sync_conf_url, data=json.dumps(data), headers=headers)
|
|||
|
|
+
|
|||
|
|
+ resp_code = json.loads(sync_response.text).get('code')
|
|||
|
|
+ resp = json.loads(sync_response.text).get('data').get('resp')
|
|||
|
|
+ conf_sync_res = ConfSyncedRes(file_path=file_path,
|
|||
|
|
+ result="")
|
|||
|
|
+ if resp_code == "200" and resp.get('sync_result') is True:
|
|||
|
|
+ conf_sync_res.result = "SUCCESS"
|
|||
|
|
+ else:
|
|||
|
|
+ conf_sync_res.result = "FAILED"
|
|||
|
|
+ host_sync_result.sync_result.append(conf_sync_res)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def deal_batch_sync_res(conf_tools, exist_host, file_path_infos, access_token):
|
|||
|
|
+ from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
+ sync_conf_url = conf_tools.load_url_by_conf().get("batch_sync_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": access_token}
|
|||
|
|
+ object_parse = ObjectParse()
|
|||
|
|
+ codeNum = 200
|
|||
|
|
+ codeString = "sync config succeed "
|
|||
|
|
+ # 组装参数
|
|||
|
|
+ sync_config_request = {"host_ids": exist_host, "file_path_infos": list()}
|
|||
|
|
+ for file_path, contents in file_path_infos.items():
|
|||
|
|
+ if file_path in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ for directory_file_path, directory_content in json.loads(contents).items():
|
|||
|
|
+ content = object_parse.parse_json_to_conf(directory_file_path, directory_content)
|
|||
|
|
+ single_file_path_info = {"file_path": directory_file_path, "content": content}
|
|||
|
|
+ sync_config_request["file_path_infos"].append(single_file_path_info)
|
|||
|
|
+ else:
|
|||
|
|
+ content = object_parse.parse_json_to_conf(file_path, contents)
|
|||
|
|
+ single_file_path_info = {"file_path": file_path, "content": content}
|
|||
|
|
+ sync_config_request["file_path_infos"].append(single_file_path_info)
|
|||
|
|
+ # 调用zeus接口
|
|||
|
|
+ try:
|
|||
|
|
+ sync_response = requests.put(sync_conf_url, data=json.dumps(sync_config_request), headers=headers)
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ codeNum = 500
|
|||
|
|
+ codeString = "Failed to sync configuration, please check the interface of config/sync."
|
|||
|
|
+ return codeNum, codeString, []
|
|||
|
|
+ # 处理响应
|
|||
|
|
+ resp_code = json.loads(sync_response.text).get('code')
|
|||
|
|
+ resp = json.loads(sync_response.text).get('data')
|
|||
|
|
+ if resp_code != "200":
|
|||
|
|
+ codeNum = 500
|
|||
|
|
+ codeString = f"Failed to sync configuration, reason is {json.loads(sync_response.text).get('message')}, " \
|
|||
|
|
+ f"please check the interface of config/sync. "
|
|||
|
|
+ return codeNum, codeString, []
|
|||
|
|
+
|
|||
|
|
+ # 重新构建返回值,目录文件返回值重新构造
|
|||
|
|
+ sync_res = []
|
|||
|
|
+ for host_result in resp:
|
|||
|
|
+ syncResult = []
|
|||
|
|
+ conf_sync_res_list = []
|
|||
|
|
+ sync_result_list = host_result.get("syncResult")
|
|||
|
|
+ dir_name = ""
|
|||
|
|
+ for single_result in sync_result_list:
|
|||
|
|
+ dir_name = os.path.dirname(single_result.get("filePath"))
|
|||
|
|
+ if dir_name in DIRECTORY_FILE_PATH_LIST and single_result.get("result") == "SUCCESS":
|
|||
|
|
+ conf_sync_res_list.append("SUCCESS")
|
|||
|
|
+ elif dir_name in DIRECTORY_FILE_PATH_LIST and single_result.get("result") == "FAIL":
|
|||
|
|
+ conf_sync_res_list.append("FAILED")
|
|||
|
|
+ else:
|
|||
|
|
+ syncResult.append(single_result)
|
|||
|
|
+ if conf_sync_res_list:
|
|||
|
|
+ if "FAILED" in conf_sync_res_list:
|
|||
|
|
+ directory_sync_result = {"filePath": dir_name, "result": "FAILED"}
|
|||
|
|
+ else:
|
|||
|
|
+ directory_sync_result = {"filePath": dir_name, "result": "SUCCESS"}
|
|||
|
|
+ syncResult.append(directory_sync_result)
|
|||
|
|
+ single_host_sync_result = {"host_id": host_result.get("host_id"), "syncResult": syncResult}
|
|||
|
|
+ sync_res.append(single_host_sync_result)
|
|||
|
|
+ return codeNum, codeString, sync_res
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def addHostSyncStatus(conf_tools, domain, host_infos):
|
|||
|
|
+ add_host_sync_status_url = conf_tools.load_url_by_conf().get("add_host_sync_status_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json"}
|
|||
|
|
+ # 数据入库
|
|||
|
|
+ try:
|
|||
|
|
+ for host in host_infos:
|
|||
|
|
+ contained_flag = Format.isContainedHostIdInOtherDomain(host.get("hostId"))
|
|||
|
|
+ if contained_flag:
|
|||
|
|
+ continue
|
|||
|
|
+ host_sync_status = {
|
|||
|
|
+ "host_id": host.get("hostId"),
|
|||
|
|
+ "host_ip": host.get("ip"),
|
|||
|
|
+ "domain_name": domain,
|
|||
|
|
+ "sync_status": 0
|
|||
|
|
+ }
|
|||
|
|
+ add_host_sync_status_response = requests.post(add_host_sync_status_url,
|
|||
|
|
+ data=json.dumps(host_sync_status), headers=headers)
|
|||
|
|
+ resp_code = json.loads(add_host_sync_status_response.text).get('code')
|
|||
|
|
+ if resp_code != "200":
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to add host sync status, please check the interface of /manage/host/sync/status/add.")
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ LOGGER.error("Failed to add host sync status, please check the interface of /manage/host/sync/status/add.")
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def deleteHostSyncStatus(conf_tools, domain, hostInfos):
|
|||
|
|
+ delete_host_sync_status_url = conf_tools.load_url_by_conf().get("delete_host_sync_status_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json"}
|
|||
|
|
+ # 数据入库
|
|||
|
|
+ try:
|
|||
|
|
+ for host in hostInfos:
|
|||
|
|
+ delete_host_sync_status = {
|
|||
|
|
+ "host_id": host.get("hostId"),
|
|||
|
|
+ "domain_name": domain
|
|||
|
|
+ }
|
|||
|
|
+ delete_host_sync_status_response = requests.post(delete_host_sync_status_url,
|
|||
|
|
+ data=json.dumps(delete_host_sync_status),
|
|||
|
|
+ headers=headers)
|
|||
|
|
+ resp_code = json.loads(delete_host_sync_status_response.text).get('code')
|
|||
|
|
+ if resp_code != "200":
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to delete host sync status, please check the interface of "
|
|||
|
|
+ "/manage/host/sync/status/delete.")
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to delete host sync status, please check the interface of "
|
|||
|
|
+ "/manage/host/sync/status/delete.")
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def diff_mangeconf_with_realconf_for_db(domain, real_conf_res_text, manage_confs):
|
|||
|
|
+ sync_status = {"domainName": domain, "hostStatus": []}
|
|||
|
|
+ from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
+
|
|||
|
|
+ for d_real_conf in real_conf_res_text:
|
|||
|
|
+ host_id = d_real_conf.get('host_id')
|
|||
|
|
+ host_sync_status = {"hostId": host_id, "syncStatus": []}
|
|||
|
|
+ d_real_conf_base = d_real_conf.get('conf_base_infos')
|
|||
|
|
+ for d_conf in d_real_conf_base:
|
|||
|
|
+ directory_conf_is_synced = {"file_path": "", "isSynced": "", "singleConf": []}
|
|||
|
|
+ d_conf_path = d_conf.get('file_path')
|
|||
|
|
+ object_parse = ObjectParse()
|
|||
|
|
+ # get the conf type and model
|
|||
|
|
+ conf_type, conf_model = Format.get_conf_type_model(d_conf_path, object_parse)
|
|||
|
|
+ Format.deal_conf_sync_status_for_db(conf_model, d_conf, d_conf_path, directory_conf_is_synced,
|
|||
|
|
+ host_sync_status, manage_confs)
|
|||
|
|
+
|
|||
|
|
+ if len(directory_conf_is_synced["singleConf"]) > 0:
|
|||
|
|
+ synced_flag = SYNCHRONIZED
|
|||
|
|
+ for single_config in directory_conf_is_synced["singleConf"]:
|
|||
|
|
+ if single_config["singleIsSynced"] == SYNCHRONIZED:
|
|||
|
|
+ continue
|
|||
|
|
+ else:
|
|||
|
|
+ synced_flag = NOT_SYNCHRONIZE
|
|||
|
|
+ directory_conf_is_synced["isSynced"] = synced_flag
|
|||
|
|
+ host_sync_status["syncStatus"].append(directory_conf_is_synced)
|
|||
|
|
+ sync_status.get("hostStatus").append(host_sync_status)
|
|||
|
|
+ return sync_status
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def deal_expected_confs_resp(expected_confs_resp_list):
|
|||
|
|
+ """"
|
|||
|
|
+ deal the excepted confs resp.
|
|||
|
|
+
|
|||
|
|
+ Args:
|
|||
|
|
+ expected_confs_resp_list (list): e.g
|
|||
|
|
+ [
|
|||
|
|
+ {
|
|||
|
|
+ "domainName": "xx"
|
|||
|
|
+ "confBaseInfos": [
|
|||
|
|
+ {
|
|||
|
|
+ "filePath": "xx",
|
|||
|
|
+ "expectedContents": "xxx"
|
|||
|
|
+ }
|
|||
|
|
+ ]
|
|||
|
|
+ }
|
|||
|
|
+ ]
|
|||
|
|
+ Returns:
|
|||
|
|
+ dict: e.g
|
|||
|
|
+ {
|
|||
|
|
+ "aops": [
|
|||
|
|
+ {
|
|||
|
|
+ "file_path": "xxx",
|
|||
|
|
+ "contents": "xxx"
|
|||
|
|
+ }
|
|||
|
|
+ ]
|
|||
|
|
+ }
|
|||
|
|
+ """
|
|||
|
|
+ # 处理expected_confs_resp,将其处理成[{"file_path": "xxx", "contents": "xxx"}], 每个domain都有一个manage_confs
|
|||
|
|
+ expected_confs_resp_dict = {}
|
|||
|
|
+ for expected_confs_resp in expected_confs_resp_list:
|
|||
|
|
+ manage_confs = []
|
|||
|
|
+ domain_name = expected_confs_resp["domainName"]
|
|||
|
|
+ confBaseInfos = expected_confs_resp["confBaseInfos"]
|
|||
|
|
+ for singleConfBaseInfo in confBaseInfos:
|
|||
|
|
+ file_path = singleConfBaseInfo["filePath"]
|
|||
|
|
+ contents = singleConfBaseInfo["expectedContents"]
|
|||
|
|
+ single_manage_conf = {"file_path": file_path, "contents": contents}
|
|||
|
|
+ manage_confs.append(single_manage_conf)
|
|||
|
|
+ expected_confs_resp_dict[domain_name] = manage_confs
|
|||
|
|
+ return expected_confs_resp_dict
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def deal_domain_result(domain_result):
|
|||
|
|
+ """"
|
|||
|
|
+ deal the domain result.
|
|||
|
|
+
|
|||
|
|
+ Args:
|
|||
|
|
+ domain_result (object): e.g
|
|||
|
|
+ {
|
|||
|
|
+ "aops": {
|
|||
|
|
+ "1": [
|
|||
|
|
+ {
|
|||
|
|
+ "filePath": "xxx",
|
|||
|
|
+ "contents": "xxxx"
|
|||
|
|
+ }
|
|||
|
|
+ ]
|
|||
|
|
+ }
|
|||
|
|
+ }
|
|||
|
|
+ Returns:
|
|||
|
|
+ dict: e.g
|
|||
|
|
+ {
|
|||
|
|
+ "aops": [
|
|||
|
|
+ {
|
|||
|
|
+ "domain_name": "xxx",
|
|||
|
|
+ "host_id": 1,
|
|||
|
|
+ "conf_base_infos": [
|
|||
|
|
+ {
|
|||
|
|
+ "file_path": "xxx",
|
|||
|
|
+ "conf_contents": {} or []
|
|||
|
|
+ }
|
|||
|
|
+ ]
|
|||
|
|
+ }
|
|||
|
|
+ ]
|
|||
|
|
+ }
|
|||
|
|
+ """
|
|||
|
|
+ # 处理domain_result,将其处理成[{"domain_name": "aops","host_id": 7, "conf_base_infos": [{"conf_contents": "xxx", "file_path": "xxx"}]}]
|
|||
|
|
+ from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
+ real_conf_res_text_dict = {}
|
|||
|
|
+ parse = ObjectParse()
|
|||
|
|
+ for domain, host_infos in domain_result.items():
|
|||
|
|
+ real_conf_res_text_list = []
|
|||
|
|
+ for host_id, confs in host_infos.items():
|
|||
|
|
+ signal_host_infos = {"domain_name": domain, "host_id": int(host_id), "conf_base_infos": []}
|
|||
|
|
+ for conf in confs:
|
|||
|
|
+ conf_path = conf["filePath"]
|
|||
|
|
+ conf_info = conf["contents"]
|
|||
|
|
+ conf_type = parse.get_conf_type_by_conf_path(conf_path)
|
|||
|
|
+ if not conf_type:
|
|||
|
|
+ return
|
|||
|
|
+ # create conf model
|
|||
|
|
+ conf_model = parse.create_conf_model_by_type(conf_type)
|
|||
|
|
+ # 转换解析
|
|||
|
|
+ if conf_path not in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ Format.convert_real_conf(conf_model, conf_type, conf_info, conf_path, parse)
|
|||
|
|
+ else:
|
|||
|
|
+ pam_res_infos = []
|
|||
|
|
+ for path, content in json.loads(conf_info).items():
|
|||
|
|
+ signal_res_info = {"path": path, "content": content}
|
|||
|
|
+ pam_res_infos.append(signal_res_info)
|
|||
|
|
+ Format.convert_real_conf(conf_model, conf_type, pam_res_infos, conf_path, parse)
|
|||
|
|
+ signal_conf = {"file_path": conf["filePath"], "conf_contents": conf_model.conf}
|
|||
|
|
+ signal_host_infos["conf_base_infos"].append(signal_conf)
|
|||
|
|
+ real_conf_res_text_list.append(signal_host_infos)
|
|||
|
|
+ real_conf_res_text_dict[domain] = real_conf_res_text_list
|
|||
|
|
+ return real_conf_res_text_dict
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def add_domain_conf_trace_flag(params, successDomain, tempDomainName):
|
|||
|
|
+ # 对successDomain成功的domain添加文件监控开关、告警开关
|
|||
|
|
+ if len(successDomain) > 0:
|
|||
|
|
+ from vulcanus.database.proxy import RedisProxy
|
|||
|
|
+ # 文件监控开关
|
|||
|
|
+ conf_change_flag = params.get("conf_change_flag")
|
|||
|
|
+ if conf_change_flag:
|
|||
|
|
+ RedisProxy.redis_connect.set(tempDomainName + "_conf_change", 1)
|
|||
|
|
+ else:
|
|||
|
|
+ RedisProxy.redis_connect.set(tempDomainName + "_conf_change", 0)
|
|||
|
|
+ # 告警开关
|
|||
|
|
+ report_flag = params.get("report_flag")
|
|||
|
|
+ if report_flag:
|
|||
|
|
+ RedisProxy.redis_connect.set(tempDomainName + "_report", 1)
|
|||
|
|
+ else:
|
|||
|
|
+ RedisProxy.redis_connect.set(tempDomainName + "_report", 0)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def uninstall_trace(access_token, host_ids, domain):
|
|||
|
|
+ from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ conf_trace_mgmt_url = conf_tools.load_url_by_conf().get("conf_trace_mgmt_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": access_token}
|
|||
|
|
+ # 数据入库
|
|||
|
|
+ try:
|
|||
|
|
+ conf_trace_mgmt_data = {
|
|||
|
|
+ "host_ids": host_ids,
|
|||
|
|
+ "action": "stop",
|
|||
|
|
+ "domain_name": domain
|
|||
|
|
+ }
|
|||
|
|
+ conf_trace_mgmt_response = requests.put(conf_trace_mgmt_url,
|
|||
|
|
+ data=json.dumps(conf_trace_mgmt_data), headers=headers)
|
|||
|
|
+ resp_code = json.loads(conf_trace_mgmt_response.text).get('code')
|
|||
|
|
+ if resp_code != "200":
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to conf trace mgmt, please check the interface of /conftrace/mgmt.")
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to conf trace mgmt, please check the interface of /conftrace/mgmt.")
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def clear_all_domain_data(access_token, domainName, successDomain, host_ids):
|
|||
|
|
+ # 删除业务域,对successDomain成功的业务域进行redis的key值清理,以及domain下的主机进行agith的清理
|
|||
|
|
+ if len(successDomain) > 0:
|
|||
|
|
+ from vulcanus.database.proxy import RedisProxy
|
|||
|
|
+ # 1.清理redis key值
|
|||
|
|
+ RedisProxy.redis_connect.delete(domainName + "_conf_change")
|
|||
|
|
+ RedisProxy.redis_connect.delete(domainName + "_report")
|
|||
|
|
+ # 2.清理数据库数据,以避免再次添加业务域的时候还能看到以往的记录
|
|||
|
|
+ Format.delete_conf_trace_infos(access_token, [], domainName)
|
|||
|
|
+ # 3.清理domain下面的host sync记录
|
|||
|
|
+ Format.delete_host_conf_sync_status(access_token, domainName, host_ids)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_conf_change_flag(domain):
|
|||
|
|
+ from vulcanus.database.proxy import RedisProxy
|
|||
|
|
+ domain_conf_change = RedisProxy.redis_connect.get(domain + "_conf_change")
|
|||
|
|
+ return domain_conf_change
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def install_update_agith(access_token, domain, host_ids):
|
|||
|
|
+ # 针对successHost 添加成功的host, 安装agith并启动agith,如果当前业务域有配置,配置agith,如果没有就不配置
|
|||
|
|
+ install_resp_code = "200"
|
|||
|
|
+ # 获取domain的文件监控开关
|
|||
|
|
+ domain_conf_change = Format.get_conf_change_flag(domain)
|
|||
|
|
+ conf_files_list = Format.get_conf_files_list(domain, access_token)
|
|||
|
|
+ if len(host_ids) > 0 and int(domain_conf_change) == 1 and len(conf_files_list) > 0:
|
|||
|
|
+ # 安装并启动agith
|
|||
|
|
+ from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ conf_trace_mgmt_url = conf_tools.load_url_by_conf().get("conf_trace_mgmt_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": access_token}
|
|||
|
|
+ try:
|
|||
|
|
+ conf_trace_mgmt_data = {
|
|||
|
|
+ "domain_name": domain,
|
|||
|
|
+ "host_ids": host_ids,
|
|||
|
|
+ "action": "start",
|
|||
|
|
+ "conf_files": conf_files_list
|
|||
|
|
+ }
|
|||
|
|
+ conf_trace_mgmt_response = requests.put(conf_trace_mgmt_url,
|
|||
|
|
+ data=json.dumps(conf_trace_mgmt_data), headers=headers)
|
|||
|
|
+ install_resp_code = json.loads(conf_trace_mgmt_response.text).get('code')
|
|||
|
|
+ LOGGER.info(f"install_resp_code is {install_resp_code}")
|
|||
|
|
+ if install_resp_code != "200":
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to conf trace mgmt, please check the interface of /conftrace/mgmt.")
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to conf trace mgmt, please check the interface of /conftrace/mgmt.")
|
|||
|
|
+
|
|||
|
|
+ # 根据业务有是否有配置,有配置并且agith启动成功情况下进行agith的配置
|
|||
|
|
+ conf_files_list = Format.get_conf_files_list(domain, access_token)
|
|||
|
|
+ if len(conf_files_list) > 0 and install_resp_code == "200":
|
|||
|
|
+ Format.update_agith_conf(conf_files_list, conf_trace_mgmt_url, headers, host_ids, domain)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def uninstall_hosts_agith(access_token, containedInHost, domain):
|
|||
|
|
+ # 根据containedInHost 停止agith服务,删除agith,删除redis key值
|
|||
|
|
+ if len(containedInHost) > 0:
|
|||
|
|
+ # 1.根据containedInHost 停止agith服务,删除agith
|
|||
|
|
+ from vulcanus.database.proxy import RedisProxy
|
|||
|
|
+ Format.uninstall_trace(access_token, containedInHost, domain)
|
|||
|
|
+ # 2.清理数据库数据,以避免再次添加业务域的时候还能看到以往的记录
|
|||
|
|
+ Format.delete_conf_trace_infos(access_token, containedInHost, domain)
|
|||
|
|
+ # 3.清理host sync记录
|
|||
|
|
+ Format.delete_host_conf_sync_status(access_token, domain, containedInHost)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def delete_conf_trace_infos(access_token, containedInHost, domain):
|
|||
|
|
+ from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ conf_trace_delete_url = conf_tools.load_url_by_conf().get("conf_trace_delete_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": access_token}
|
|||
|
|
+ # 数据入库
|
|||
|
|
+ try:
|
|||
|
|
+ conf_trace_delete_data = {
|
|||
|
|
+ "domain_name": domain,
|
|||
|
|
+ "host_ids": containedInHost
|
|||
|
|
+ }
|
|||
|
|
+ conf_trace_delete_response = requests.post(conf_trace_delete_url,
|
|||
|
|
+ data=json.dumps(conf_trace_delete_data), headers=headers)
|
|||
|
|
+ resp_code = json.loads(conf_trace_delete_response.text).get('code')
|
|||
|
|
+ if resp_code != "200":
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to delete trace info, please check the interface of /conftrace/delete.")
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to delete trace info, please check the interface of /conftrace/delete.")
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def get_conf_files_list(domain, access_token):
|
|||
|
|
+ conf_files_list = []
|
|||
|
|
+ conf_files = Format.get_manageconf_by_domain(domain).get("conf_files")
|
|||
|
|
+ for conf_file in conf_files:
|
|||
|
|
+ if conf_file.get("file_path") in DIRECTORY_FILE_PATH_LIST:
|
|||
|
|
+ # 获取文件夹下面的配置文件
|
|||
|
|
+ from ragdoll.utils.object_parse import ObjectParse
|
|||
|
|
+ object_parse = ObjectParse()
|
|||
|
|
+ d_conf = {"filePath": conf_file.get("file_path")}
|
|||
|
|
+ host_ids = Format.get_hostid_list_by_domain(domain)
|
|||
|
|
+ if len(host_ids):
|
|||
|
|
+ _, _, file_paths = object_parse.get_directory_files(d_conf, host_ids[0], access_token)
|
|||
|
|
+ if len(file_paths) > 0:
|
|||
|
|
+ conf_files_list.extend(file_paths)
|
|||
|
|
+ else:
|
|||
|
|
+ conf_files_list.append(conf_file.get("file_path"))
|
|||
|
|
+ return conf_files_list
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def update_agith(access_token, conf_files_list, domain):
|
|||
|
|
+ # 根据containedInHost,监控开关 停止agith服务,删除agith,删除redis key值
|
|||
|
|
+ domain_conf_change_flag = Format.get_conf_change_flag(domain)
|
|||
|
|
+ if int(domain_conf_change_flag) == 1:
|
|||
|
|
+ from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
+ # 根据containedInHost 停止agith服务,删除agith
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ conf_trace_mgmt_url = conf_tools.load_url_by_conf().get("conf_trace_mgmt_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": access_token}
|
|||
|
|
+ # 获取domain下的主机id
|
|||
|
|
+ host_ids = Format.get_hostid_list_by_domain(domain)
|
|||
|
|
+ # 数据入库
|
|||
|
|
+ if len(host_ids) > 0:
|
|||
|
|
+ Format.update_agith_conf(conf_files_list, conf_trace_mgmt_url, headers, host_ids, domain)
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def update_agith_conf(conf_files_list, conf_trace_mgmt_url, headers, host_ids, domain_name):
|
|||
|
|
+ try:
|
|||
|
|
+ conf_trace_mgmt_data = {
|
|||
|
|
+ "host_ids": host_ids,
|
|||
|
|
+ "action": "update",
|
|||
|
|
+ "conf_files": conf_files_list,
|
|||
|
|
+ "domain_name": domain_name
|
|||
|
|
+ }
|
|||
|
|
+ conf_trace_mgmt_response = requests.put(conf_trace_mgmt_url,
|
|||
|
|
+ data=json.dumps(conf_trace_mgmt_data), headers=headers)
|
|||
|
|
+ resp_code = json.loads(conf_trace_mgmt_response.text).get('code')
|
|||
|
|
+ if resp_code != "200":
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to conf trace mgmt, please check the interface of /conftrace/mgmt.")
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to conf trace mgmt, please check the interface of /conftrace/mgmt.")
|
|||
|
|
+
|
|||
|
|
+ @staticmethod
|
|||
|
|
+ def delete_host_conf_sync_status(access_token, domainName, hostIds):
|
|||
|
|
+ try:
|
|||
|
|
+ from ragdoll.utils.conf_tools import ConfTools
|
|||
|
|
+ conf_tools = ConfTools()
|
|||
|
|
+ delete_all_host_sync_status_url = conf_tools.load_url_by_conf().get("delete_all_host_sync_status_url")
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": access_token}
|
|||
|
|
+ delete_host_conf_sync_status_data = {
|
|||
|
|
+ "host_ids": hostIds,
|
|||
|
|
+ "domain_name": domainName
|
|||
|
|
+ }
|
|||
|
|
+ delete_sync_status_response = requests.post(delete_all_host_sync_status_url,
|
|||
|
|
+ data=json.dumps(delete_host_conf_sync_status_data),
|
|||
|
|
+ headers=headers)
|
|||
|
|
+ resp_code = json.loads(delete_sync_status_response.text).get('code')
|
|||
|
|
+ if resp_code != "200":
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to delete sync status, please check the interface of /manage/all/host/sync/status/delete.")
|
|||
|
|
+ except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
+ LOGGER.error(f"An error occurred: {connect_ex}")
|
|||
|
|
+ LOGGER.error(
|
|||
|
|
+ "Failed to delete sync status, please check the interface of /manage/all/host/sync/status/delete.")
|
|||
|
|
diff --git a/ragdoll/utils/git_tools.py b/ragdoll/utils/git_tools.py
|
|||
|
|
index 049d450..f6200af 100644
|
|||
|
|
--- a/ragdoll/utils/git_tools.py
|
|||
|
|
+++ b/ragdoll/utils/git_tools.py
|
|||
|
|
@@ -6,8 +6,7 @@ import ast
|
|||
|
|
|
|||
|
|
from ragdoll.const.conf_handler_const import CONFIG
|
|||
|
|
from ragdoll.log.log import LOGGER
|
|||
|
|
-from ragdoll.models.git_log_message import GitLogMessage
|
|||
|
|
-from ragdoll.controllers.format import Format
|
|||
|
|
+from ragdoll.utils.format import Format
|
|||
|
|
|
|||
|
|
|
|||
|
|
class GitTools(object):
|
|||
|
|
@@ -18,7 +17,6 @@ class GitTools(object):
|
|||
|
|
self._target_dir = self.load_git_dir()
|
|||
|
|
|
|||
|
|
def load_git_dir(self):
|
|||
|
|
- cf = configparser.ConfigParser()
|
|||
|
|
cf = configparser.ConfigParser()
|
|||
|
|
if os.path.exists(CONFIG):
|
|||
|
|
cf.read(CONFIG, encoding="utf-8")
|
|||
|
|
@@ -93,16 +91,14 @@ class GitTools(object):
|
|||
|
|
# Execute the shell command and return the execution node and output
|
|||
|
|
def run_shell_return_output(self, shell):
|
|||
|
|
cmd = subprocess.Popen(shell, stdout=subprocess.PIPE, shell=True)
|
|||
|
|
- LOGGER.debug("################# shell cmd ################")
|
|||
|
|
LOGGER.debug("subprocess.Popen({shell}, stdout=subprocess.PIPE, shell=True)".format(shell=shell))
|
|||
|
|
- LOGGER.debug("################# shell cmd end ################")
|
|||
|
|
output, err = cmd.communicate()
|
|||
|
|
return output
|
|||
|
|
|
|||
|
|
def makeGitMessage(self, path, logMessage):
|
|||
|
|
if len(logMessage) == 0:
|
|||
|
|
return "the logMessage is null"
|
|||
|
|
- LOGGER.debug("AAAA path is : {}".format(path))
|
|||
|
|
+ LOGGER.debug("path is : {}".format(path))
|
|||
|
|
cwdDir = os.getcwd()
|
|||
|
|
os.chdir(self._target_dir)
|
|||
|
|
LOGGER.debug(os.getcwd())
|
|||
|
|
@@ -113,26 +109,26 @@ class GitTools(object):
|
|||
|
|
count = logMessage.count("commit")
|
|||
|
|
lines = logMessage.split('\n')
|
|||
|
|
|
|||
|
|
+ LOGGER.debug("count is : {}".format(count))
|
|||
|
|
for index in range(0, count):
|
|||
|
|
- LOGGER.debug("AAAAAAAAAAAAAAA count is : {}".format(index))
|
|||
|
|
- gitMessage = GitLogMessage()
|
|||
|
|
+ gitMessage = {}
|
|||
|
|
for temp in range(0, singleLogLen):
|
|||
|
|
line = lines[index * singleLogLen + temp]
|
|||
|
|
value = line.split(" ", 1)[-1]
|
|||
|
|
if "commit" in line:
|
|||
|
|
- gitMessage.change_id = value
|
|||
|
|
+ gitMessage["changeId"] = value
|
|||
|
|
if "Author" in line:
|
|||
|
|
- gitMessage.author = value
|
|||
|
|
+ gitMessage["author"] = value
|
|||
|
|
if "Date" in line:
|
|||
|
|
- gitMessage._date = value[2:]
|
|||
|
|
- gitMessage.change_reason = lines[index * singleLogLen + 4]
|
|||
|
|
+ gitMessage["date"] = value[2:]
|
|||
|
|
+ gitMessage["changeReason"] = lines[index * singleLogLen + 4]
|
|||
|
|
LOGGER.debug("gitMessage is : {}".format(gitMessage))
|
|||
|
|
gitLogMessageList.append(gitMessage)
|
|||
|
|
|
|||
|
|
LOGGER.debug("################# gitMessage start ################")
|
|||
|
|
if count == 1:
|
|||
|
|
last_message = gitLogMessageList[0]
|
|||
|
|
- last_message.post_value = Format.get_file_content_by_read(path)
|
|||
|
|
+ last_message["postValue"] = Format.get_file_content_by_read(path)
|
|||
|
|
os.chdir(cwdDir)
|
|||
|
|
return gitLogMessageList
|
|||
|
|
|
|||
|
|
@@ -140,13 +136,13 @@ class GitTools(object):
|
|||
|
|
LOGGER.debug("index is : {}".format(index))
|
|||
|
|
message = gitLogMessageList[index]
|
|||
|
|
next_message = gitLogMessageList[index + 1]
|
|||
|
|
- message.post_value = Format.get_file_content_by_read(path)
|
|||
|
|
- shell = ['git checkout {}'.format(next_message.change_id)]
|
|||
|
|
+ message["postValue"] = Format.get_file_content_by_read(path)
|
|||
|
|
+ shell = ['git checkout {}'.format(next_message["changeId"])]
|
|||
|
|
output = self.run_shell_return_output(shell)
|
|||
|
|
- message.pre_value = Format.get_file_content_by_read(path)
|
|||
|
|
+ message["preValue"] = Format.get_file_content_by_read(path)
|
|||
|
|
# the last changlog
|
|||
|
|
first_message = gitLogMessageList[count - 1]
|
|||
|
|
- first_message.post_value = Format.get_file_content_by_read(path)
|
|||
|
|
+ first_message["postValue"] = Format.get_file_content_by_read(path)
|
|||
|
|
|
|||
|
|
LOGGER.debug("################# gitMessage end ################")
|
|||
|
|
os.chdir(cwdDir)
|
|||
|
|
diff --git a/ragdoll/utils/host_tools.py b/ragdoll/utils/host_tools.py
|
|||
|
|
index c467994..f561523 100644
|
|||
|
|
--- a/ragdoll/utils/host_tools.py
|
|||
|
|
+++ b/ragdoll/utils/host_tools.py
|
|||
|
|
@@ -75,11 +75,9 @@ class HostTools(object):
|
|||
|
|
}]
|
|||
|
|
"""
|
|||
|
|
res = []
|
|||
|
|
+ LOGGER.debug("The domainHost is : {}".format(domainHost))
|
|||
|
|
for d_host in domainHost:
|
|||
|
|
hostId = int(d_host.get('host_id'))
|
|||
|
|
- LOGGER.debug("the host Id is : {}".format(hostId))
|
|||
|
|
- d_host = {}
|
|||
|
|
- d_host["hostId"] = hostId
|
|||
|
|
res.append(hostId)
|
|||
|
|
|
|||
|
|
return res
|
|||
|
|
diff --git a/ragdoll/utils/object_parse.py b/ragdoll/utils/object_parse.py
|
|||
|
|
index 6cc4564..719dc68 100644
|
|||
|
|
--- a/ragdoll/utils/object_parse.py
|
|||
|
|
+++ b/ragdoll/utils/object_parse.py
|
|||
|
|
@@ -174,14 +174,14 @@ class ObjectParse(object):
|
|||
|
|
|
|||
|
|
return conf_info
|
|||
|
|
|
|||
|
|
- def get_directory_files(self, d_conf, host_id):
|
|||
|
|
+ def get_directory_files(self, d_conf, host_id, access_token):
|
|||
|
|
file_paths = list()
|
|||
|
|
conf_tools = ConfTools()
|
|||
|
|
file_directory = dict()
|
|||
|
|
- file_directory['file_directory'] = d_conf.file_path
|
|||
|
|
+ file_directory['file_directory'] = d_conf["filePath"]
|
|||
|
|
file_directory['host_id'] = host_id
|
|||
|
|
url = conf_tools.load_url_by_conf().get("object_file_url")
|
|||
|
|
- headers = {"Content-Type": "application/json"}
|
|||
|
|
+ headers = {"Content-Type": "application/json", "access_token": access_token}
|
|||
|
|
try:
|
|||
|
|
response = requests.post(url, data=json.dumps(file_directory), headers=headers)
|
|||
|
|
except requests.exceptions.RequestException as connect_ex:
|
|||
|
|
@@ -191,7 +191,7 @@ class ObjectParse(object):
|
|||
|
|
base_rsp = BaseResponse(codeNum, codeString)
|
|||
|
|
return base_rsp, codeNum
|
|||
|
|
response_code = json.loads(response.text).get("code")
|
|||
|
|
- if response_code == None:
|
|||
|
|
+ if response_code is None:
|
|||
|
|
codeNum = 500
|
|||
|
|
codeString = "Failed to obtain the actual configuration, please check the interface of conf/objectFile."
|
|||
|
|
return codeNum, codeString, file_paths
|
|||
|
|
@@ -207,5 +207,5 @@ class ObjectParse(object):
|
|||
|
|
return codeNum, codeString, file_paths
|
|||
|
|
codeNum = 200
|
|||
|
|
codeString = "Success get pam.d file paths."
|
|||
|
|
- file_paths = file_path_reps.get('resp').get('object_file_paths')
|
|||
|
|
+ file_paths = file_path_reps.get('object_file_paths')
|
|||
|
|
return codeNum, codeString, file_paths
|
|||
|
|
diff --git a/ragdoll/utils/prepare.py b/ragdoll/utils/prepare.py
|
|||
|
|
index 4e61489..132ea7b 100644
|
|||
|
|
--- a/ragdoll/utils/prepare.py
|
|||
|
|
+++ b/ragdoll/utils/prepare.py
|
|||
|
|
@@ -15,7 +15,7 @@ class Prepare(object):
|
|||
|
|
def target_dir(self, target_dir):
|
|||
|
|
self._target_dir = target_dir
|
|||
|
|
|
|||
|
|
- def mdkir_git_warehose(self, username, useremail):
|
|||
|
|
+ def mkdir_git_warehose(self, username, useremail):
|
|||
|
|
res = True
|
|||
|
|
LOGGER.debug("self._target_dir is : {}".format(self._target_dir))
|
|||
|
|
if os.path.exists(self._target_dir):
|
|||
|
|
@@ -26,7 +26,7 @@ class Prepare(object):
|
|||
|
|
git_tools = GitTools(self._target_dir)
|
|||
|
|
mkdir_code = git_tools.run_shell_return_code(cmd1)
|
|||
|
|
git_code = self.git_init(username, useremail)
|
|||
|
|
- if mkdir_code != 0:
|
|||
|
|
+ if mkdir_code != 0 or not git_code:
|
|||
|
|
res = False
|
|||
|
|
return res
|
|||
|
|
|
|||
|
|
diff --git a/ragdoll/utils/yang_module.py b/ragdoll/utils/yang_module.py
|
|||
|
|
index de0d9b5..f0f21ab 100644
|
|||
|
|
--- a/ragdoll/utils/yang_module.py
|
|||
|
|
+++ b/ragdoll/utils/yang_module.py
|
|||
|
|
@@ -1,7 +1,5 @@
|
|||
|
|
import libyang
|
|||
|
|
import os
|
|||
|
|
-import sys
|
|||
|
|
-import importlib
|
|||
|
|
import operator
|
|||
|
|
|
|||
|
|
from ragdoll.log.log import LOGGER
|
|||
|
|
@@ -95,10 +93,6 @@ class YangModule(object):
|
|||
|
|
if files_tail != "yang":
|
|||
|
|
continue
|
|||
|
|
modulePath = os.path.join(self._yang_dir, d_file)
|
|||
|
|
- # grammar_res = self.check_yang_grammar(modulePath)
|
|||
|
|
- # print("grammar_res is : {}".format(grammar_res))
|
|||
|
|
- # if not grammar_res:
|
|||
|
|
- # continue
|
|||
|
|
fo = open(modulePath, 'r+')
|
|||
|
|
module = self._ctx.parse_module_file(fo)
|
|||
|
|
module_list.append(module)
|
|||
|
|
@@ -183,7 +177,6 @@ class YangModule(object):
|
|||
|
|
continue
|
|||
|
|
xpath.append(path)
|
|||
|
|
|
|||
|
|
- # print("xpath is : {}".format(xpath))
|
|||
|
|
return xpath
|
|||
|
|
|
|||
|
|
def getFeatureInModule(self, modules):
|
|||
|
|
@@ -250,9 +243,8 @@ class YangModule(object):
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
res = {}
|
|||
|
|
+ LOGGER.debug("modules are : {}".format(modules))
|
|||
|
|
for d_mod in modules:
|
|||
|
|
- LOGGER.debug("d_mod is : {}".format(d_mod))
|
|||
|
|
- LOGGER.debug("d_mod's type is : {}".format(type(d_mod)))
|
|||
|
|
feature_list = self.getFeatureInModule(d_mod)
|
|||
|
|
module_name = d_mod.name()
|
|||
|
|
xpath = ""
|
|||
|
|
diff --git a/service/gala-ragdoll.service b/service/gala-ragdoll.service
|
|||
|
|
index 0fbaf2e..2dfc4f1 100644
|
|||
|
|
--- a/service/gala-ragdoll.service
|
|||
|
|
+++ b/service/gala-ragdoll.service
|
|||
|
|
@@ -3,8 +3,9 @@ Description=a-ops gala ragdoll service
|
|||
|
|
After=network.target
|
|||
|
|
|
|||
|
|
[Service]
|
|||
|
|
-Type=exec
|
|||
|
|
-ExecStart=/usr/bin/ragdoll
|
|||
|
|
+Type=forking
|
|||
|
|
+ExecStart=/usr/bin/ragdoll start
|
|||
|
|
+ExecStop=/usr/bin/ragdoll stop
|
|||
|
|
Restart=on-failure
|
|||
|
|
RestartSec=1
|
|||
|
|
RemainAfterExit=yes
|
|||
|
|
diff --git a/service/ragdoll b/service/ragdoll
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..92a5b7c
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/service/ragdoll
|
|||
|
|
@@ -0,0 +1,15 @@
|
|||
|
|
+#!/bin/bash
|
|||
|
|
+. /usr/bin/aops-vulcanus
|
|||
|
|
+
|
|||
|
|
+MANAGER_CONSTANT="ragdoll"
|
|||
|
|
+MANAGER_CONFIG_FILE=/etc/ragdoll/gala-ragdoll.conf
|
|||
|
|
+
|
|||
|
|
+function main() {
|
|||
|
|
+ if [ "${OPERATION}" = "start" ]; then
|
|||
|
|
+ create_config_file "${MANAGER_CONFIG_FILE}" "ragdoll"
|
|||
|
|
+ fi
|
|||
|
|
+ start_or_stop_service "${MANAGER_CONSTANT}"
|
|||
|
|
+ exit $?
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+main
|
|||
|
|
diff --git a/service/ragdoll-filetrace b/service/ragdoll-filetrace
|
|||
|
|
new file mode 100755
|
|||
|
|
index 0000000..cf1a0bd
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/service/ragdoll-filetrace
|
|||
|
|
@@ -0,0 +1,782 @@
|
|||
|
|
+#!/usr/bin/python3
|
|||
|
|
+from bpfcc import BPF
|
|||
|
|
+import json
|
|||
|
|
+import os
|
|||
|
|
+import requests
|
|||
|
|
+import threading
|
|||
|
|
+import psutil
|
|||
|
|
+import ctypes
|
|||
|
|
+
|
|||
|
|
+import logging
|
|||
|
|
+import logging.handlers
|
|||
|
|
+logger = logging.getLogger(__name__)
|
|||
|
|
+syslog_handler = logging.handlers.SysLogHandler(address='/dev/log')
|
|||
|
|
+syslog_handler.setLevel(logging.DEBUG)
|
|||
|
|
+syslog_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
|||
|
|
+logger.addHandler(syslog_handler)
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+bpf_text="""
|
|||
|
|
+//#define randomized_struct_fields_start struct {
|
|||
|
|
+//#define randomized_struct_fields_end };
|
|||
|
|
+
|
|||
|
|
+#include <uapi/linux/bpf.h>
|
|||
|
|
+#include <linux/dcache.h>
|
|||
|
|
+#include <linux/err.h>
|
|||
|
|
+#include <linux/fdtable.h>
|
|||
|
|
+#include <linux/fs.h>
|
|||
|
|
+#include <linux/fs_struct.h>
|
|||
|
|
+#include <linux/path.h>
|
|||
|
|
+#include <linux/sched.h>
|
|||
|
|
+#include <linux/slab.h>
|
|||
|
|
+
|
|||
|
|
+#define MAX_DEPTH 4
|
|||
|
|
+#define MAX_DIRNAME_LEN 16
|
|||
|
|
+#define MAX_FILENAME_LEN 32
|
|||
|
|
+#define MAX_CMD_LEN 32
|
|||
|
|
+#define MAX_TASK_COMM_LEN 32
|
|||
|
|
+
|
|||
|
|
+#define ARGSIZE 16
|
|||
|
|
+#define MAX_ARGS 4
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+struct pinfo_t {
|
|||
|
|
+ char comm[MAX_CMD_LEN];
|
|||
|
|
+ unsigned int ppid;
|
|||
|
|
+ char arg1[ARGSIZE];
|
|||
|
|
+ char arg2[ARGSIZE];
|
|||
|
|
+ char arg3[ARGSIZE];
|
|||
|
|
+ char arg4[ARGSIZE];
|
|||
|
|
+
|
|||
|
|
+};
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+struct event {
|
|||
|
|
+ unsigned int pid;
|
|||
|
|
+ unsigned int ppid;
|
|||
|
|
+ char cmd[16];
|
|||
|
|
+ char pcmd[16];
|
|||
|
|
+ unsigned long i_ino;
|
|||
|
|
+ char filename[MAX_FILENAME_LEN];
|
|||
|
|
+ char dir1[MAX_DIRNAME_LEN];
|
|||
|
|
+ char dir2[MAX_DIRNAME_LEN];
|
|||
|
|
+ char dir3[MAX_DIRNAME_LEN];
|
|||
|
|
+ char dir4[MAX_DIRNAME_LEN];
|
|||
|
|
+
|
|||
|
|
+ char oldfilename[MAX_FILENAME_LEN];
|
|||
|
|
+ char odir1[MAX_DIRNAME_LEN];
|
|||
|
|
+ char odir2[MAX_DIRNAME_LEN];
|
|||
|
|
+ char odir3[MAX_DIRNAME_LEN];
|
|||
|
|
+ char odir4[MAX_DIRNAME_LEN];
|
|||
|
|
+ int flag;
|
|||
|
|
+};
|
|||
|
|
+
|
|||
|
|
+BPF_HASH(exec_map, u32, struct pinfo_t);
|
|||
|
|
+BPF_PERF_OUTPUT(events);
|
|||
|
|
+
|
|||
|
|
+//for rm command
|
|||
|
|
+TRACEPOINT_PROBE(syscalls, sys_enter_unlinkat) {
|
|||
|
|
+ struct task_struct* t;
|
|||
|
|
+ struct task_struct* p;
|
|||
|
|
+
|
|||
|
|
+ struct event e = {};
|
|||
|
|
+ e.flag = 4;
|
|||
|
|
+
|
|||
|
|
+ t = (struct task_struct*)bpf_get_current_task();
|
|||
|
|
+ bpf_probe_read(&e.pid, sizeof(e.pid), &t->tgid);
|
|||
|
|
+ bpf_probe_read(&e.cmd, sizeof(e.cmd), &t->comm);
|
|||
|
|
+
|
|||
|
|
+ bpf_probe_read(&p, sizeof(p), &t->real_parent);
|
|||
|
|
+ bpf_probe_read(&e.ppid, sizeof(e.ppid), &p->tgid);
|
|||
|
|
+ bpf_probe_read(&e.pcmd, sizeof(e.pcmd), &p->comm);
|
|||
|
|
+
|
|||
|
|
+ bpf_probe_read_str((void*)&e.filename, sizeof(e.filename), (const void*)args->pathname);
|
|||
|
|
+ bpf_trace_printk("Process calling sys_enter_rename newfilename:%s \\n", e.filename);
|
|||
|
|
+ events.perf_submit((struct pt_regs *)args, &e, sizeof(e));
|
|||
|
|
+ return 0;
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+//for copy command
|
|||
|
|
+TRACEPOINT_PROBE(syscalls, sys_enter_copy_file_range) {
|
|||
|
|
+
|
|||
|
|
+ struct task_struct* t;
|
|||
|
|
+ struct task_struct* p;
|
|||
|
|
+ struct files_struct* f;
|
|||
|
|
+ struct fdtable* fdt;
|
|||
|
|
+ struct file** fdd;
|
|||
|
|
+ struct file* file;
|
|||
|
|
+ struct path path;
|
|||
|
|
+ struct dentry* dentry;
|
|||
|
|
+ struct inode* inode;
|
|||
|
|
+ struct qstr pathname;
|
|||
|
|
+ umode_t mode;
|
|||
|
|
+ unsigned long i_ino;
|
|||
|
|
+
|
|||
|
|
+ //char filename[128];
|
|||
|
|
+ struct event e = {};
|
|||
|
|
+ e.flag = 2;
|
|||
|
|
+
|
|||
|
|
+ int fd =args->fd_out;
|
|||
|
|
+ t = (struct task_struct*)bpf_get_current_task();
|
|||
|
|
+ if(t){
|
|||
|
|
+ bpf_probe_read(&f, sizeof(f), &(t->files));
|
|||
|
|
+ bpf_probe_read(&fdt, sizeof(fdt), (void*)&f->fdt);
|
|||
|
|
+ int ret = bpf_probe_read(&fdd, sizeof(fdd), (void*)&fdt->fd);
|
|||
|
|
+ if (ret) {
|
|||
|
|
+ //bpf_trace_printk("bpf_probe_read failed: %d\\n", ret);
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+ bpf_probe_read(&file, sizeof(file), (void*)&fdd[fd]);
|
|||
|
|
+
|
|||
|
|
+ //file file ppid pcmd
|
|||
|
|
+ bpf_probe_read(&p, sizeof(p), &t->real_parent);
|
|||
|
|
+ bpf_probe_read(&e.ppid, sizeof(e.ppid), &p->tgid);
|
|||
|
|
+ bpf_probe_read(&e.pcmd, sizeof(e.pcmd), &p->comm);
|
|||
|
|
+
|
|||
|
|
+ //fill file ino
|
|||
|
|
+ bpf_probe_read(&inode, sizeof(inode), &file->f_inode);
|
|||
|
|
+ bpf_probe_read(&e.i_ino, sizeof(i_ino), &inode->i_ino);
|
|||
|
|
+ bpf_probe_read(&mode, sizeof(mode), &inode->i_mode);
|
|||
|
|
+ if(!S_ISREG(mode)){
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ //file process info
|
|||
|
|
+ bpf_probe_read(&e.pid, sizeof(e.pid), &t->tgid);
|
|||
|
|
+ bpf_probe_read(&e.cmd, sizeof(e.cmd), &t->comm);
|
|||
|
|
+
|
|||
|
|
+ //get filename
|
|||
|
|
+ bpf_probe_read(&path, sizeof(path), (const void*)&file->f_path);
|
|||
|
|
+ bpf_probe_read(&dentry, sizeof(dentry), (const void*)&path.dentry);
|
|||
|
|
+ bpf_probe_read(&pathname, sizeof(pathname), (const void*)&dentry->d_name);
|
|||
|
|
+
|
|||
|
|
+ struct dentry* d_parent;
|
|||
|
|
+
|
|||
|
|
+ #pragma unroll
|
|||
|
|
+ for (int i = 0; i < MAX_DEPTH; i++) {
|
|||
|
|
+ bpf_probe_read(&d_parent, sizeof(d_parent), (const void*)&dentry->d_parent);
|
|||
|
|
+ if (d_parent == dentry) {
|
|||
|
|
+ break;
|
|||
|
|
+ }
|
|||
|
|
+ //fix me
|
|||
|
|
+ if(i == 0){
|
|||
|
|
+ bpf_probe_read(&e.dir1, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }else if(i == 1){
|
|||
|
|
+ bpf_probe_read(&e.dir2, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }else if(i == 2){
|
|||
|
|
+ bpf_probe_read(&e.dir3, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }else if(i == 3){
|
|||
|
|
+ bpf_probe_read(&e.dir4, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ dentry = d_parent;
|
|||
|
|
+ }
|
|||
|
|
+ bpf_probe_read_str((void*)&e.filename, sizeof(e.filename), (const void*)pathname.name);
|
|||
|
|
+ events.perf_submit((struct pt_regs *)args, &e, sizeof(e));
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+ return 0;
|
|||
|
|
+}
|
|||
|
|
+//for sed command
|
|||
|
|
+TRACEPOINT_PROBE(syscalls, sys_enter_rename) {
|
|||
|
|
+ struct task_struct* t;
|
|||
|
|
+ struct task_struct* p;
|
|||
|
|
+
|
|||
|
|
+ struct event e = {};
|
|||
|
|
+ e.flag = 1;
|
|||
|
|
+
|
|||
|
|
+ t = (struct task_struct*)bpf_get_current_task();
|
|||
|
|
+ bpf_probe_read(&e.pid, sizeof(e.pid), &t->tgid);
|
|||
|
|
+ bpf_probe_read(&e.cmd, sizeof(e.cmd), &t->comm);
|
|||
|
|
+
|
|||
|
|
+ bpf_probe_read(&p, sizeof(p), &t->real_parent);
|
|||
|
|
+ bpf_probe_read(&e.ppid, sizeof(e.ppid), &p->tgid);
|
|||
|
|
+ bpf_probe_read(&e.pcmd, sizeof(e.pcmd), &p->comm);
|
|||
|
|
+
|
|||
|
|
+ bpf_probe_read_str((void*)&e.filename, sizeof(e.filename), (const void*)args->newname);
|
|||
|
|
+ //bpf_trace_printk("Process calling sys_enter_rename newfilename:%s \\n", e.filename);
|
|||
|
|
+ events.perf_submit((struct pt_regs *)args, &e, sizeof(e));
|
|||
|
|
+ return 0;
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+TRACEPOINT_PROBE(syscalls, sys_enter_renameat) {
|
|||
|
|
+ char comm[TASK_COMM_LEN];
|
|||
|
|
+ bpf_get_current_comm(&comm, sizeof(comm));
|
|||
|
|
+ bpf_trace_printk("Process %s is calling renameat\\n", comm);
|
|||
|
|
+ return 0;
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+//for move command
|
|||
|
|
+TRACEPOINT_PROBE(syscalls, sys_enter_renameat2) {
|
|||
|
|
+ struct event e = {};
|
|||
|
|
+ e.flag = 3;
|
|||
|
|
+
|
|||
|
|
+ struct task_struct* t;
|
|||
|
|
+ struct task_struct* p;
|
|||
|
|
+
|
|||
|
|
+ t = (struct task_struct*)bpf_get_current_task();
|
|||
|
|
+ bpf_probe_read(&e.pid, sizeof(e.pid), &t->tgid);
|
|||
|
|
+ bpf_probe_read(&e.cmd, sizeof(e.cmd), &t->comm);
|
|||
|
|
+
|
|||
|
|
+ bpf_probe_read(&p, sizeof(p), &t->real_parent);
|
|||
|
|
+ bpf_probe_read(&e.ppid, sizeof(e.ppid), &p->tgid);
|
|||
|
|
+ bpf_probe_read(&e.pcmd, sizeof(e.pcmd), &p->comm);
|
|||
|
|
+
|
|||
|
|
+ bpf_probe_read_str((void*)&e.filename, sizeof(e.filename), (const void*)args->newname);
|
|||
|
|
+ bpf_probe_read_str((void*)&e.oldfilename, sizeof(e.oldfilename), (const void*)args->oldname);
|
|||
|
|
+
|
|||
|
|
+ struct fs_struct *fs;
|
|||
|
|
+ struct path pwd;
|
|||
|
|
+ bpf_probe_read(&fs, sizeof(fs), (const void*)&t->fs);
|
|||
|
|
+ bpf_probe_read(&pwd, sizeof(pwd), (const void*)&fs->pwd);
|
|||
|
|
+
|
|||
|
|
+ struct dentry* dentry;
|
|||
|
|
+ bpf_probe_read(&dentry, sizeof(dentry), (const void*)&pwd.dentry);
|
|||
|
|
+
|
|||
|
|
+ struct dentry* d_parent;
|
|||
|
|
+
|
|||
|
|
+ int olddfd = args->olddfd;
|
|||
|
|
+ int newdfd = args->newdfd;
|
|||
|
|
+ if (newdfd == AT_FDCWD) {
|
|||
|
|
+ #pragma unroll
|
|||
|
|
+ for (int i = 0; i < MAX_DEPTH; i++) {
|
|||
|
|
+ bpf_probe_read(&d_parent, sizeof(d_parent), (const void*)&dentry->d_parent);
|
|||
|
|
+ if (d_parent == dentry) {
|
|||
|
|
+ break;
|
|||
|
|
+ }
|
|||
|
|
+ //fix me
|
|||
|
|
+ if(i == 0){
|
|||
|
|
+ bpf_probe_read(&e.dir1, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }else if(i == 1){
|
|||
|
|
+ bpf_probe_read(&e.dir2, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }else if(i == 2){
|
|||
|
|
+ bpf_probe_read(&e.dir3, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }else if(i == 3){
|
|||
|
|
+ bpf_probe_read(&e.dir4, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }
|
|||
|
|
+ dentry = d_parent;
|
|||
|
|
+ }
|
|||
|
|
+ bpf_trace_printk("newfilename relative to CWD: %s\\n", e.filename);
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ if (olddfd == AT_FDCWD) {
|
|||
|
|
+ bpf_trace_printk("oldfilename relative to CWD: %s\\n", e.oldfilename);
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ events.perf_submit((struct pt_regs *)args, &e, sizeof(e));
|
|||
|
|
+
|
|||
|
|
+ return 0;
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+TRACEPOINT_PROBE(syscalls, sys_exit_execve) {
|
|||
|
|
+ u64 tgid_pid;
|
|||
|
|
+ u32 tgid, pid;
|
|||
|
|
+ tgid_pid = bpf_get_current_pid_tgid();
|
|||
|
|
+ tgid = tgid_pid >> 32;
|
|||
|
|
+ pid = (u32)tgid_pid;
|
|||
|
|
+ exec_map.delete(&pid);
|
|||
|
|
+ return 0;
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+TRACEPOINT_PROBE(syscalls, sys_enter_execve) {
|
|||
|
|
+ //args, args->filename, args->argv, args->envp
|
|||
|
|
+ struct pinfo_t p = {};
|
|||
|
|
+
|
|||
|
|
+ u64 tgid_pid;
|
|||
|
|
+ u32 tgid, pid;
|
|||
|
|
+ tgid_pid = bpf_get_current_pid_tgid();
|
|||
|
|
+ tgid = tgid_pid >> 32;
|
|||
|
|
+ pid = (u32)tgid_pid;
|
|||
|
|
+
|
|||
|
|
+ struct task_struct *t = (struct task_struct *)bpf_get_current_task();
|
|||
|
|
+ struct task_struct *pp;
|
|||
|
|
+ bpf_probe_read(&pp, sizeof(pp), &t->real_parent);
|
|||
|
|
+ bpf_probe_read(&p.ppid, sizeof(p.ppid), &pp->tgid);
|
|||
|
|
+
|
|||
|
|
+ bpf_get_current_comm(&p.comm, sizeof(p.comm));
|
|||
|
|
+
|
|||
|
|
+ //int i;
|
|||
|
|
+ //for (i = 1; i <= MAX_ARGS; i++) {
|
|||
|
|
+ // const char __user *argp;
|
|||
|
|
+ // bpf_probe_read_user(&argp, sizeof(argp), &args->argv[i]);
|
|||
|
|
+ // if (!argp) {
|
|||
|
|
+ // break;
|
|||
|
|
+ // }
|
|||
|
|
+ // if(i == 1){
|
|||
|
|
+ // bpf_probe_read_user_str(&p.arg1, sizeof(p.args1), argp);
|
|||
|
|
+ // }else if(i == 2){
|
|||
|
|
+ // bpf_probe_read_user_str(&p.arg2, sizeof(p.args2), argp);
|
|||
|
|
+ // }else if(i == 3){
|
|||
|
|
+ // bpf_probe_read_user_str(&p.arg3, sizeof(p.args3), argp);
|
|||
|
|
+ // }else if(i == 4){
|
|||
|
|
+ // bpf_probe_read_user_str(&p.arg4, sizeof(p.args4), argp);
|
|||
|
|
+ // }
|
|||
|
|
+ //}
|
|||
|
|
+
|
|||
|
|
+ const char *const * argv = args->argv;
|
|||
|
|
+ if(!argv[1]){
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+ bpf_probe_read_user(&p.arg1, sizeof(p.arg1), (void *)argv[1]);
|
|||
|
|
+
|
|||
|
|
+ if(!argv[2]){
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+ bpf_probe_read_user(&p.arg2, sizeof(p.arg2), (void *)argv[2]);
|
|||
|
|
+
|
|||
|
|
+ if(!argv[3]){
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+ bpf_probe_read_user(&p.arg3, sizeof(p.arg3), (void *)argv[3]);
|
|||
|
|
+
|
|||
|
|
+ if(!argv[4]){
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+ bpf_probe_read_user(&p.arg4, sizeof(p.arg4), (void *)argv[4]);
|
|||
|
|
+
|
|||
|
|
+ exec_map.update(&pid, &p);
|
|||
|
|
+ return 0;
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+//for vim echo ...
|
|||
|
|
+TRACEPOINT_PROBE(syscalls, sys_enter_write) {
|
|||
|
|
+ unsigned int fd;
|
|||
|
|
+ struct task_struct* t;
|
|||
|
|
+ struct task_struct* p;
|
|||
|
|
+ struct files_struct* f;
|
|||
|
|
+ struct fdtable* fdt;
|
|||
|
|
+ struct file** fdd;
|
|||
|
|
+ struct file* file;
|
|||
|
|
+ struct path path;
|
|||
|
|
+ struct dentry* dentry;
|
|||
|
|
+ struct inode* inode;
|
|||
|
|
+ struct qstr pathname;
|
|||
|
|
+ umode_t mode;
|
|||
|
|
+ unsigned long i_ino;
|
|||
|
|
+ char filename[128];
|
|||
|
|
+ struct event e = {};
|
|||
|
|
+ e.flag = 0;
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+ pid_t ppid;
|
|||
|
|
+ char pcomm[16];
|
|||
|
|
+
|
|||
|
|
+ fd =args->fd;
|
|||
|
|
+ t = (struct task_struct*)bpf_get_current_task();
|
|||
|
|
+ if(t){
|
|||
|
|
+ bpf_probe_read(&f, sizeof(f), &(t->files));
|
|||
|
|
+ bpf_probe_read(&fdt, sizeof(fdt), (void*)&f->fdt);
|
|||
|
|
+ int ret = bpf_probe_read(&fdd, sizeof(fdd), (void*)&fdt->fd);
|
|||
|
|
+ if (ret) {
|
|||
|
|
+ //bpf_trace_printk("bpf_probe_read failed: %d\\n", ret);
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+ bpf_probe_read(&file, sizeof(file), (void*)&fdd[fd]);
|
|||
|
|
+
|
|||
|
|
+ //file file ppid pcmd
|
|||
|
|
+ bpf_probe_read(&p, sizeof(p), &t->real_parent);
|
|||
|
|
+ bpf_probe_read(&e.ppid, sizeof(e.ppid), &p->tgid);
|
|||
|
|
+ bpf_probe_read(&e.pcmd, sizeof(e.pcmd), &p->comm);
|
|||
|
|
+
|
|||
|
|
+ //fill file ino
|
|||
|
|
+ bpf_probe_read(&inode, sizeof(inode), &file->f_inode);
|
|||
|
|
+ bpf_probe_read(&e.i_ino, sizeof(i_ino), &inode->i_ino);
|
|||
|
|
+ bpf_probe_read(&mode, sizeof(mode), &inode->i_mode);
|
|||
|
|
+ if(!S_ISREG(mode)){
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ //file process info
|
|||
|
|
+ bpf_probe_read(&e.pid, sizeof(e.pid), &t->tgid);
|
|||
|
|
+ bpf_probe_read(&e.cmd, sizeof(e.cmd), &t->comm);
|
|||
|
|
+ //bpf_probe_read(&e.pcmd, sizeof(e.pcmd), &p->comm);
|
|||
|
|
+
|
|||
|
|
+ //get filename
|
|||
|
|
+ bpf_probe_read(&path, sizeof(path), (const void*)&file->f_path);
|
|||
|
|
+ bpf_probe_read(&dentry, sizeof(dentry), (const void*)&path.dentry);
|
|||
|
|
+ bpf_probe_read(&pathname, sizeof(pathname), (const void*)&dentry->d_name);
|
|||
|
|
+ //fill name event
|
|||
|
|
+ //bpf_probe_read_str((void*)&e.filename, sizeof(e.filename), (const void*)pathname.name);
|
|||
|
|
+
|
|||
|
|
+ struct dentry* d_parent;
|
|||
|
|
+
|
|||
|
|
+ #pragma unroll
|
|||
|
|
+ for (int i = 0; i < MAX_DEPTH; i++) {
|
|||
|
|
+ bpf_probe_read(&d_parent, sizeof(d_parent), (const void*)&dentry->d_parent);
|
|||
|
|
+ if (d_parent == dentry) {
|
|||
|
|
+ break;
|
|||
|
|
+ }
|
|||
|
|
+ //fix me
|
|||
|
|
+ if(i == 0){
|
|||
|
|
+ bpf_probe_read(&e.dir1, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }else if(i == 1){
|
|||
|
|
+ bpf_probe_read(&e.dir2, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }else if(i == 2){
|
|||
|
|
+ bpf_probe_read(&e.dir3, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }else if(i == 3){
|
|||
|
|
+ bpf_probe_read(&e.dir4, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ dentry = d_parent;
|
|||
|
|
+ }
|
|||
|
|
+ bpf_probe_read_str((void*)&e.filename, sizeof(e.filename), (const void*)pathname.name);
|
|||
|
|
+ //bpf_trace_printk("filename parent e.filename: %s\\n", e.filename);
|
|||
|
|
+ events.perf_submit((struct pt_regs *)args, &e, sizeof(e));
|
|||
|
|
+
|
|||
|
|
+ return 0;
|
|||
|
|
+ }
|
|||
|
|
+ return 0;
|
|||
|
|
+}
|
|||
|
|
+"""
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+def get_conf():
|
|||
|
|
+ CONF = "/etc/agith/agith.config"
|
|||
|
|
+ conf_data = {}
|
|||
|
|
+ try:
|
|||
|
|
+ with open(CONF) as user_file:
|
|||
|
|
+ conf_data = json.load(user_file)
|
|||
|
|
+ except FileNotFoundError:
|
|||
|
|
+ print(f"[{CONF}] does not exist!")
|
|||
|
|
+ exit(1)
|
|||
|
|
+ return conf_data
|
|||
|
|
+ #print(json.dumps(conf_data, indent = 4))
|
|||
|
|
+
|
|||
|
|
+def get_ino(filename_list):
|
|||
|
|
+ global logger
|
|||
|
|
+ filenames = filename_list.split(",")
|
|||
|
|
+ ino_name_map = {}
|
|||
|
|
+ for f in filenames:
|
|||
|
|
+ try:
|
|||
|
|
+ stat_info = os.stat(f)
|
|||
|
|
+ i = stat_info.st_ino
|
|||
|
|
+ ino_name_map [str(i)] = f
|
|||
|
|
+ except FileNotFoundError:
|
|||
|
|
+ print(f"File not found: [{f}]")
|
|||
|
|
+ exit(1)
|
|||
|
|
+ except Exception as e:
|
|||
|
|
+ print(f"An error occurred with file {f}: {e}")
|
|||
|
|
+ exit(1)
|
|||
|
|
+ logger.warning("g_map:%s", ino_name_map)
|
|||
|
|
+ return ino_name_map
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+conf_data = get_conf()
|
|||
|
|
+g_map = get_ino(conf_data["Repository"]["conf_list"])
|
|||
|
|
+
|
|||
|
|
+def postdata(data=None):
|
|||
|
|
+ global conf_data
|
|||
|
|
+ global g_map
|
|||
|
|
+ global logger
|
|||
|
|
+ #logger.warning('post data: %s', data)
|
|||
|
|
+ g_map = get_ino(conf_data["Repository"]["conf_list"])
|
|||
|
|
+ try:
|
|||
|
|
+ aops_zeus = conf_data["Repository"]["aops_zeus"]
|
|||
|
|
+ response = requests.post(aops_zeus, json=data, timeout=5)
|
|||
|
|
+ response.raise_for_status()
|
|||
|
|
+ if response.status_code != 200:
|
|||
|
|
+ logger.info('POST request failed:', response.status_code)
|
|||
|
|
+ except requests.exceptions.HTTPError as http_err:
|
|||
|
|
+ logger.error(f"HTTP error occurred: {http_err}")
|
|||
|
|
+ except requests.exceptions.ConnectionError as conn_err:
|
|||
|
|
+ logger.error(f"Connection error occurred: {conn_err}")
|
|||
|
|
+ except requests.exceptions.Timeout as timeout_err:
|
|||
|
|
+ logger.error(f"Timeout error occurred: {timeout_err}")
|
|||
|
|
+ except requests.exceptions.RequestException as req_err:
|
|||
|
|
+ logger.error(f"An error occurred: {req_err}")
|
|||
|
|
+
|
|||
|
|
+def get_oldfile_full_path(e):
|
|||
|
|
+ #dir depth 4
|
|||
|
|
+ filename = ""
|
|||
|
|
+ if e.flag != 3:
|
|||
|
|
+ return filename
|
|||
|
|
+ dir1 = ""
|
|||
|
|
+ dir2 = ""
|
|||
|
|
+ dir3 = ""
|
|||
|
|
+ dir4 = ""
|
|||
|
|
+ try:
|
|||
|
|
+ dir1 = e.odir1.decode('utf-8')
|
|||
|
|
+ dir2 = e.odir2.decode('utf-8')
|
|||
|
|
+ dir3 = e.odir3.decode('utf-8')
|
|||
|
|
+ dir4 = e.odir4.decode('utf-8')
|
|||
|
|
+ except UnicodeDecodeError as ex:
|
|||
|
|
+ print(f"UnicodeDecodeError: {ex}")
|
|||
|
|
+
|
|||
|
|
+ filename = e.oldfilename.decode('utf-8')
|
|||
|
|
+ if not filename:
|
|||
|
|
+ return ""
|
|||
|
|
+
|
|||
|
|
+ if dir1 == "/":
|
|||
|
|
+ filename = dir1 + filename
|
|||
|
|
+ return filename
|
|||
|
|
+
|
|||
|
|
+ if dir2 == "/":
|
|||
|
|
+ filename = dir2 + dir1 + "/" + filename
|
|||
|
|
+ return filename
|
|||
|
|
+
|
|||
|
|
+ if dir3 == "/":
|
|||
|
|
+ filename = dir3 + dir2 + "/" + dir1 + "/" + filename
|
|||
|
|
+ return filename
|
|||
|
|
+
|
|||
|
|
+ if dir4 == "/":
|
|||
|
|
+ filename = dir4 + dir3 + "/" + dir2 + "/" + dir1 + "/" + filename
|
|||
|
|
+ return filename
|
|||
|
|
+ return filename
|
|||
|
|
+
|
|||
|
|
+def get_file_full_path(e):
|
|||
|
|
+ #dir depth 4
|
|||
|
|
+ dir1 = ""
|
|||
|
|
+ dir2 = ""
|
|||
|
|
+ dir3 = ""
|
|||
|
|
+ dir4 = ""
|
|||
|
|
+ try:
|
|||
|
|
+ dir1 = e.dir1.decode('utf-8')
|
|||
|
|
+ except UnicodeDecodeError as ex:
|
|||
|
|
+ print(f"UnicodeDecodeError: {ex}")
|
|||
|
|
+ try:
|
|||
|
|
+ dir2 = e.dir2.decode('utf-8')
|
|||
|
|
+ except UnicodeDecodeError as ex:
|
|||
|
|
+ print(f"UnicodeDecodeError: {ex}")
|
|||
|
|
+ try:
|
|||
|
|
+ dir3 = e.dir3.decode('utf-8')
|
|||
|
|
+ except UnicodeDecodeError as ex:
|
|||
|
|
+ print(f"UnicodeDecodeError: {ex}")
|
|||
|
|
+ try:
|
|||
|
|
+ dir4 = e.dir4.decode('utf-8')
|
|||
|
|
+ except UnicodeDecodeError as ex:
|
|||
|
|
+ print(f"UnicodeDecodeError: {ex}")
|
|||
|
|
+
|
|||
|
|
+ filename = e.filename.decode('utf-8')
|
|||
|
|
+ if not filename:
|
|||
|
|
+ return ""
|
|||
|
|
+ #filename is full path
|
|||
|
|
+ if os.path.exists(filename):
|
|||
|
|
+ return filename
|
|||
|
|
+
|
|||
|
|
+ if dir1 == "/":
|
|||
|
|
+ filename = dir1 + filename
|
|||
|
|
+ return filename
|
|||
|
|
+
|
|||
|
|
+ if dir2 == "/":
|
|||
|
|
+ filename = dir2 + dir1 + "/" + filename
|
|||
|
|
+ return filename
|
|||
|
|
+
|
|||
|
|
+ if dir3 == "/":
|
|||
|
|
+ filename = dir3 + dir2 + "/" + dir1 + "/" + filename
|
|||
|
|
+ return filename
|
|||
|
|
+
|
|||
|
|
+ if dir4 == "/":
|
|||
|
|
+ filename = dir4 + dir3 + "/" + dir2 + "/" + dir1 + "/" + filename
|
|||
|
|
+ return filename
|
|||
|
|
+ return filename
|
|||
|
|
+
|
|||
|
|
+class Data(ctypes.Structure):
|
|||
|
|
+ _fields_ = [
|
|||
|
|
+ ("comm", ctypes.c_char * 64),
|
|||
|
|
+ ("ppid", ctypes.c_uint32),
|
|||
|
|
+ ("arg1", ctypes.c_char * 16),
|
|||
|
|
+ ("arg2", ctypes.c_char * 16),
|
|||
|
|
+ ("arg3", ctypes.c_char * 16),
|
|||
|
|
+ ("arg4", ctypes.c_char * 16),
|
|||
|
|
+ ]
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
+def get_process_info_from_map(procid):
|
|||
|
|
+ global exec_map
|
|||
|
|
+ pid = procid
|
|||
|
|
+ try:
|
|||
|
|
+ pid = ctypes.c_int(pid)
|
|||
|
|
+ info = exec_map[pid]
|
|||
|
|
+ #info = ctypes.cast(ctypes.pointer(data), ctypes.POINTER(Data)).contents
|
|||
|
|
+ pid = int(pid.value)
|
|||
|
|
+ if info:
|
|||
|
|
+ #print(f"PID: {pid}, Command: {info.comm}, Args: {info.args.decode('utf-8', 'ignore')}")
|
|||
|
|
+ #cmd = info.comm.decode('utf-8') + " " + info.args.decode('utf-8', 'ignore')
|
|||
|
|
+ #cmd = info.args.decode('utf-8', 'ignore')
|
|||
|
|
+ str1 = info.arg1.decode('utf-8')
|
|||
|
|
+ str2 = info.arg2.decode('utf-8')
|
|||
|
|
+ str3 = info.arg3.decode('utf-8')
|
|||
|
|
+ str4 = info.arg4.decode('utf-8')
|
|||
|
|
+ cmd = str1 + " " + str2 + " " + str3 + " " + str4
|
|||
|
|
+ return {
|
|||
|
|
+ 'pid': pid,
|
|||
|
|
+ 'cmd': cmd,
|
|||
|
|
+ }
|
|||
|
|
+ else:
|
|||
|
|
+ #print(f"No information found for PID {pid}")
|
|||
|
|
+ return {
|
|||
|
|
+ 'pid': pid,
|
|||
|
|
+ 'error': 'error:No such process'
|
|||
|
|
+ }
|
|||
|
|
+ except KeyError:
|
|||
|
|
+ pid = int(pid.value)
|
|||
|
|
+ #print(f"key error for PID {pid}")
|
|||
|
|
+ return {
|
|||
|
|
+ 'pid': pid,
|
|||
|
|
+ 'error': 'error:keyerror.'
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+def get_ppid_by_pid(procid):
|
|||
|
|
+ global exec_map
|
|||
|
|
+ pid = procid
|
|||
|
|
+ try:
|
|||
|
|
+ pid = ctypes.c_int(pid)
|
|||
|
|
+ info = exec_map[pid]
|
|||
|
|
+ if info:
|
|||
|
|
+ return info.ppid
|
|||
|
|
+ else:
|
|||
|
|
+ #print("not found,get ppid form local")
|
|||
|
|
+ return get_parent_pid(pid)
|
|||
|
|
+ except KeyError:
|
|||
|
|
+ #print("keyerror ,get ppid form local")
|
|||
|
|
+ return get_parent_pid(pid)
|
|||
|
|
+ return 0
|
|||
|
|
+
|
|||
|
|
+def get_parent_pid(pid):
|
|||
|
|
+ if not isinstance(pid, int):
|
|||
|
|
+ pid = int(pid.value)
|
|||
|
|
+ try:
|
|||
|
|
+ with open(f"/proc/{pid}/status", "r") as f:
|
|||
|
|
+ for line in f:
|
|||
|
|
+ if line.startswith("PPid:"):
|
|||
|
|
+ parent_pid = int(line.split()[1])
|
|||
|
|
+ return parent_pid
|
|||
|
|
+ except FileNotFoundError:
|
|||
|
|
+ print(f"FileNotFoundError:/proc/{pid}/status")
|
|||
|
|
+ return 0
|
|||
|
|
+
|
|||
|
|
+def get_process_info_from_local(pid):
|
|||
|
|
+ try:
|
|||
|
|
+ process = psutil.Process(pid)
|
|||
|
|
+ name = process.name()
|
|||
|
|
+ cmdline = process.cmdline()
|
|||
|
|
+ return {
|
|||
|
|
+ 'pid': pid,
|
|||
|
|
+ 'cmd': name + " " + ''.join(map(str, cmdline)),
|
|||
|
|
+ }
|
|||
|
|
+ except psutil.NoSuchProcess:
|
|||
|
|
+ return {
|
|||
|
|
+ 'pid': pid,
|
|||
|
|
+ 'error': 'error:No such process'
|
|||
|
|
+ }
|
|||
|
|
+ except psutil.AccessDenied:
|
|||
|
|
+ return {
|
|||
|
|
+ 'pid': pid,
|
|||
|
|
+ 'error': 'error:Access denied'
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+def make_process_tree(procid):
|
|||
|
|
+ info = []
|
|||
|
|
+ tmp = {}
|
|||
|
|
+ flag = True
|
|||
|
|
+ pid = procid
|
|||
|
|
+ while flag:
|
|||
|
|
+ tmp = get_process_info_from_map(pid)
|
|||
|
|
+ if "error" in tmp:
|
|||
|
|
+ tmp = get_process_info_from_local(pid)
|
|||
|
|
+ if "error" in tmp:
|
|||
|
|
+ break;
|
|||
|
|
+ else:
|
|||
|
|
+ info.append(tmp)
|
|||
|
|
+ else:
|
|||
|
|
+ info.append(tmp)
|
|||
|
|
+
|
|||
|
|
+ ppid = get_ppid_by_pid(pid)
|
|||
|
|
+ if ppid == 0:
|
|||
|
|
+ break;
|
|||
|
|
+ else:
|
|||
|
|
+ pid = ppid
|
|||
|
|
+ return info
|
|||
|
|
+
|
|||
|
|
+def check_filename(newfile=None, oldfile=None):
|
|||
|
|
+ global conf_data
|
|||
|
|
+ conf_file = conf_data["Repository"]["conf_list"]
|
|||
|
|
+ if os.path.isdir(newfile):
|
|||
|
|
+ return False
|
|||
|
|
+
|
|||
|
|
+ newfile = remove_dot_slash(newfile)
|
|||
|
|
+ if newfile and newfile in conf_file:
|
|||
|
|
+ return True
|
|||
|
|
+
|
|||
|
|
+ if oldfile and oldfile in conf_file:
|
|||
|
|
+ return True
|
|||
|
|
+
|
|||
|
|
+ return False
|
|||
|
|
+
|
|||
|
|
+def remove_dot_slash(path):
|
|||
|
|
+ if path.startswith('./'):
|
|||
|
|
+ return path[2:]
|
|||
|
|
+ return path
|
|||
|
|
+
|
|||
|
|
+def get_filename(newfile=None, oldfile=None):
|
|||
|
|
+ global conf_data
|
|||
|
|
+ conf_file = conf_data["Repository"]["conf_list"]
|
|||
|
|
+ if not oldfile:
|
|||
|
|
+ return newfile
|
|||
|
|
+
|
|||
|
|
+ if oldfile in conf_file:
|
|||
|
|
+ return oldfile
|
|||
|
|
+ newfile = remove_dot_slash(newfile)
|
|||
|
|
+ if newfile in conf_file:
|
|||
|
|
+ return newfile
|
|||
|
|
+
|
|||
|
|
+def process_event(cpu, data, size):
|
|||
|
|
+ global b
|
|||
|
|
+ global conf_data
|
|||
|
|
+ global g_map
|
|||
|
|
+ global logger
|
|||
|
|
+ global executor
|
|||
|
|
+
|
|||
|
|
+ e = b["events"].event(data)
|
|||
|
|
+ fname = get_file_full_path(e)
|
|||
|
|
+ oldname = get_oldfile_full_path(e)
|
|||
|
|
+ filename = get_filename(fname, oldname)
|
|||
|
|
+ #print(f'post event filename:{fname} e.pid: {e.pid}')
|
|||
|
|
+ #fixme
|
|||
|
|
+ if check_filename(fname, oldname):
|
|||
|
|
+ #pid = ctypes.c_int(e.pid)
|
|||
|
|
+ #get_process_info_map(pid)
|
|||
|
|
+ aops_zeus = conf_data["Repository"]["aops_zeus"]
|
|||
|
|
+ d = {}
|
|||
|
|
+ d["host_id"] = int(conf_data["Repository"]["host_id"])
|
|||
|
|
+ d["domain_name"] = conf_data["Repository"]["domain_name"]
|
|||
|
|
+ #d["file"] = e.filename.decode('utf-8')
|
|||
|
|
+ d["flag"] = e.flag
|
|||
|
|
+ d["file"] = filename
|
|||
|
|
+ d["syscall"] = "write"
|
|||
|
|
+ d["pid"] = e.pid
|
|||
|
|
+ #d["dir1"] = e.dir1.decode('utf-8')
|
|||
|
|
+ #d["dir2"] = e.dir2.decode('utf-8')
|
|||
|
|
+ #d["dir3"] = e.dir3.decode('utf-8')
|
|||
|
|
+ #d["dir4"] = e.dir4.decode('utf-8')
|
|||
|
|
+ #d["inode"] = e.i_ino
|
|||
|
|
+ d["inode"] = 0
|
|||
|
|
+ d["cmd"] = e.cmd.decode('utf-8')
|
|||
|
|
+ d["ptrace"] = make_process_tree(e.ppid)
|
|||
|
|
+ #tmp = {"pid": e.ppid, "cmd": e.pcmd.decode('utf-8')}
|
|||
|
|
+ #d["ptrace"].append(tmp)
|
|||
|
|
+ print(d)
|
|||
|
|
+ #aops_zeus = conf_data["Repository"]["aops_zeus"]
|
|||
|
|
+ #response = requests.post(aops_zeus, json=d, timeout=5)
|
|||
|
|
+ #if response.status_code != 200:
|
|||
|
|
+ # print('POST request failed:', response.status_code)
|
|||
|
|
+ t = threading.Thread(target=postdata, args=(d,))
|
|||
|
|
+ t.deamon = True
|
|||
|
|
+ t.start()
|
|||
|
|
+
|
|||
|
|
+#load ebpf
|
|||
|
|
+b = BPF(text=bpf_text, cflags=["-Wno-macro-redefined"])
|
|||
|
|
+#exec_map = b["exec_map"]
|
|||
|
|
+exec_map = b.get_table("exec_map")
|
|||
|
|
+
|
|||
|
|
+if __name__ == "__main__":
|
|||
|
|
+
|
|||
|
|
+ #print(json.dumps(conf_data, indent = 4))
|
|||
|
|
+ aops_zeus = conf_data["Repository"]["aops_zeus"]
|
|||
|
|
+ conf_list = conf_data["Repository"]["conf_list"]
|
|||
|
|
+ host_id = conf_data["Repository"]["host_id"]
|
|||
|
|
+ domain_name = conf_data["Repository"]["domain_name"]
|
|||
|
|
+
|
|||
|
|
+ b["events"].open_perf_buffer(process_event)
|
|||
|
|
+ while True:
|
|||
|
|
+ b.perf_buffer_poll()
|
|||
|
|
diff --git a/service/ragdoll-filetrace.service b/service/ragdoll-filetrace.service
|
|||
|
|
new file mode 100644
|
|||
|
|
index 0000000..1ac3366
|
|||
|
|
--- /dev/null
|
|||
|
|
+++ b/service/ragdoll-filetrace.service
|
|||
|
|
@@ -0,0 +1,19 @@
|
|||
|
|
+[Unit]
|
|||
|
|
+Description=ragdoll-filetrace Service
|
|||
|
|
+After=network.target
|
|||
|
|
+StartLimitIntervalSec=30
|
|||
|
|
+
|
|||
|
|
+[Service]
|
|||
|
|
+Type=simple
|
|||
|
|
+ExecStartPre=/usr/bin/test -z "$(pgrep -f /usr/bin/ragdoll-filetrace)"
|
|||
|
|
+ExecStart=/usr/bin/python3 /usr/bin/ragdoll-filetrace
|
|||
|
|
+ExecStop=/bin/bash -c 'kill `pgrep -d " " -f ragdoll-filetrace>/dev/null`'
|
|||
|
|
+Restart=on-failure
|
|||
|
|
+RestartSec=5s
|
|||
|
|
+StartLimitBurst=3
|
|||
|
|
+
|
|||
|
|
+Environment=PYTHONUNBUFFERED=1
|
|||
|
|
+
|
|||
|
|
+[Install]
|
|||
|
|
+WantedBy=multi-user.target
|
|||
|
|
+
|
|||
|
|
diff --git a/setup.py b/setup.py
|
|||
|
|
index d21e2b6..1bd488a 100644
|
|||
|
|
--- a/setup.py
|
|||
|
|
+++ b/setup.py
|
|||
|
|
@@ -1,6 +1,5 @@
|
|||
|
|
# coding: utf-8
|
|||
|
|
|
|||
|
|
-import sys
|
|||
|
|
from setuptools import setup, find_packages
|
|||
|
|
|
|||
|
|
NAME = "ragdoll"
|
|||
|
|
@@ -20,16 +19,15 @@ setup(
|
|||
|
|
version=VERSION,
|
|||
|
|
description="Configuration traceability",
|
|||
|
|
author_email="",
|
|||
|
|
- url="",
|
|||
|
|
+ url="https://gitee.com/openeuler/gala-ragdoll",
|
|||
|
|
keywords=["Swagger", "Configuration traceability"],
|
|||
|
|
install_requires=REQUIRES,
|
|||
|
|
packages=find_packages(),
|
|||
|
|
package_data={'': ['swagger/swagger.yaml']},
|
|||
|
|
include_package_data=True,
|
|||
|
|
entry_points={
|
|||
|
|
- 'console_scripts': ['ragdoll=ragdoll.__main__:main']},
|
|||
|
|
+ 'console_scripts': ['ragdoll=ragdoll.manage:main']},
|
|||
|
|
long_description="""\
|
|||
|
|
A
|
|||
|
|
"""
|
|||
|
|
)
|
|||
|
|
-
|
|||
|
|
diff --git a/yang_modules/openEuler-hostname.yang b/yang_modules/openEuler-hostname.yang
|
|||
|
|
index 5b0f2ca..ca39557 100644
|
|||
|
|
--- a/yang_modules/openEuler-hostname.yang
|
|||
|
|
+++ b/yang_modules/openEuler-hostname.yang
|
|||
|
|
@@ -61,7 +61,7 @@ module openEuler-hostname {
|
|||
|
|
description "The file name is hostname";
|
|||
|
|
|
|||
|
|
hostname:path "openEuler:/etc/hostname";
|
|||
|
|
- hostname:type "text";
|
|||
|
|
+ hostname:type "hostname";
|
|||
|
|
hostname:spacer "";
|
|||
|
|
}
|
|||
|
|
}
|