diff --git a/apps/node_man/constants.py b/apps/node_man/constants.py index 22c3ab6af..1836cb4b5 100644 --- a/apps/node_man/constants.py +++ b/apps/node_man/constants.py @@ -1244,7 +1244,7 @@ def _get_member__alias_map(cls) -> Dict[Enum, str]: cls.LOGIN_PORT: _("登录端口"), cls.LOGIN_ACCOUNT: _("登录账号"), cls.AUTH_TYPE: _("认证方式"), - cls.CREDENTIALS: _("凭证"), + cls.CREDENTIALS: _("密码/密钥"), cls.OUTER_IP: _("外网 IP"), cls.LOGIN_IP: _("登录 IP"), cls.BIZ: _("业务"), @@ -1255,49 +1255,65 @@ def _get_member__alias_map(cls) -> Dict[Enum, str]: cls.DATA_COMPRESSION: _("数据压缩"), } + @classmethod + def get_excel_optional_map(cls) -> Dict[str, str]: + excel_optional_map = ExcelOptionalType._get_member__alias_map() -EXCEL_REQUIRED = "必填" -EXCEL_OPTIONAL = "可选" -EXCEL_BOTH_NOT_EMPTY = "与「{}」不能同时为空" - -EXCEL_TITLE_OPTIONAL = { - ExcelField.INNER_IPV4.value: EXCEL_BOTH_NOT_EMPTY.format("内网 IPv6"), - ExcelField.INNER_IPV6.value: EXCEL_BOTH_NOT_EMPTY.format("内网 IPv4"), - ExcelField.OS_TYPE.value: EXCEL_REQUIRED, - ExcelField.INSTALL_CHANNEL.value: EXCEL_REQUIRED, - ExcelField.LOGIN_PORT.value: EXCEL_REQUIRED, - ExcelField.LOGIN_ACCOUNT.value: EXCEL_REQUIRED, - ExcelField.AUTH_TYPE.value: EXCEL_REQUIRED, - ExcelField.CREDENTIALS.value: EXCEL_REQUIRED, - ExcelField.OUTER_IP.value: EXCEL_OPTIONAL, - ExcelField.LOGIN_IP.value: EXCEL_OPTIONAL, - ExcelField.BIZ.value: EXCEL_OPTIONAL, - ExcelField.CLOUD.value: EXCEL_OPTIONAL, - ExcelField.AP.value: EXCEL_REQUIRED, - ExcelField.TRANSFER_SPEED_LIMIT.value: EXCEL_OPTIONAL, - ExcelField.ADDRESS_TYPE.value: EXCEL_REQUIRED, - ExcelField.DATA_COMPRESSION.value: EXCEL_OPTIONAL, -} + return { + cls.INNER_IPV4: excel_optional_map[ExcelOptionalType.BOTH_NOT_EMPTY].format( + cls._get_member__alias_map()[cls.INNER_IPV6] + ), + cls.INNER_IPV6: excel_optional_map[ExcelOptionalType.BOTH_NOT_EMPTY].format( + cls._get_member__alias_map()[cls.INNER_IPV4] + ), + cls.OS_TYPE: excel_optional_map[ExcelOptionalType.REQUIRED], + cls.INSTALL_CHANNEL: excel_optional_map[ExcelOptionalType.REQUIRED], + cls.LOGIN_PORT: excel_optional_map[ExcelOptionalType.REQUIRED], + cls.LOGIN_ACCOUNT: excel_optional_map[ExcelOptionalType.REQUIRED], + cls.AUTH_TYPE: excel_optional_map[ExcelOptionalType.REQUIRED], + cls.CREDENTIALS: excel_optional_map[ExcelOptionalType.REQUIRED], + cls.OUTER_IP: excel_optional_map[ExcelOptionalType.OPTIONAL], + cls.LOGIN_IP: excel_optional_map[ExcelOptionalType.OPTIONAL], + cls.BIZ: excel_optional_map[ExcelOptionalType.OPTIONAL], + cls.CLOUD: excel_optional_map[ExcelOptionalType.OPTIONAL], + cls.AP: excel_optional_map[ExcelOptionalType.REQUIRED], + cls.TRANSFER_SPEED_LIMIT: excel_optional_map[ExcelOptionalType.OPTIONAL], + cls.ADDRESS_TYPE: excel_optional_map[ExcelOptionalType.OPTIONAL], + cls.DATA_COMPRESSION: excel_optional_map[ExcelOptionalType.OPTIONAL], + } -EXCEL_TITLE_DESCRIBE = { - ExcelField.INNER_IPV4.value: "目标主机 IPv4 地址。", - ExcelField.INNER_IPV6.value: "目标主机 IPv6 地址。", - ExcelField.OS_TYPE.value: "目标主机操作系统类型。", - ExcelField.INSTALL_CHANNEL.value: "在特殊复杂网络下,目标主机无法与「管控区域」内主机直接连通,可通过指定「安装通道」进行 Agent 安装。默认使用「default」即可。", - ExcelField.LOGIN_PORT.value: "登录到目标主机上的sshd端口。", - ExcelField.LOGIN_ACCOUNT.value: "登录到目标主机上所使用的用户。", - ExcelField.AUTH_TYPE.value: "登录到目标主机上所使用的认证方式。", - ExcelField.CREDENTIALS.value: "登录到目标主机上所使用的凭证,根据认证方式提供密码或私钥,某些「认证方式」的选项可能会忽略这个字段。", - ExcelField.OUTER_IP.value: "会自动注册到 CMDB。", - ExcelField.LOGIN_IP.value: "目标主机的用于登录进行 Agent 安装的 IP 地址,区别于记录在 CMDB 中的 IP;支持 IPv4、IPv6。" - "若未填写,优先使用「内网IPv4」来登录目标机器,若「内网IPv4」未填写,使用「内网IPv6」。", - ExcelField.BIZ.value: "目标主机归属业务。默认使用「蓝鲸」业务", - ExcelField.CLOUD.value: "目标主机所在的管控区域。若是在某个云区域内,选择该云区域的名字。默认使用「直连区域」。", - ExcelField.AP.value: "一般情况下使用「自动选择」即可,若有特殊的接入点无法自动识别到,可以手动选择对应接入点。", - ExcelField.TRANSFER_SPEED_LIMIT.value: "Agent配置中对文件传输速率的硬限制,单位「Mbytes/s」,不填则使用Agent默认值100Mbytes/s。", - ExcelField.ADDRESS_TYPE.value: "记录到 CMDB 中的对应枚举字段。默认为「静态」。", - ExcelField.DATA_COMPRESSION.value: "开启数据压缩后,所有通过数据管道传输的日志采集数据的流量都将进行压缩,可一定程度上降低数据上报所带来的带宽压力。但会带来少量额外的CPU消耗。", -} + @classmethod + def get_excel_describe_map(cls) -> Dict[str, str]: + return { + cls.INNER_IPV4: _("目标主机 IPv4 地址"), + cls.INNER_IPV6: _("目标主机 IPv6 地址"), + cls.OS_TYPE: _("目标主机操作系统类型"), + cls.INSTALL_CHANNEL: _("在特殊复杂网络下,目标主机无法与「管控区域」内主机直接连通,可通过指定「安装通道」进行 Agent 安装。默认使用「default」即可"), + cls.LOGIN_PORT: _("登录到目标主机上的sshd端口"), + cls.LOGIN_ACCOUNT: _("登录到目标主机上所使用的用户"), + cls.AUTH_TYPE: _("登录到目标主机上所使用的认证方式"), + cls.CREDENTIALS: _("登录到目标主机上所使用的凭证,根据认证方式提供密码或私钥"), + cls.OUTER_IP: _("目标主机外网IP,会自动注册到 CMDB"), + cls.LOGIN_IP: _( + "用于登录目标主机执行安装的 IP 地址,区别于记录在 CMDB 中的 IP;支持 IPv4、IPv6。若未填写,优先使用「内网IPv4」来登录目标机器,若「内网IPv4」未填写,使用「内网IPv6」" + ), + cls.BIZ: _("目标主机归属业务。默认使用「蓝鲸」业务"), + cls.CLOUD: _("目标主机所在的管控区域。若是在某个云区域内,选择该云区域的名字。默认使用「直连区域」"), + cls.AP: _("一般情况下使用「自动选择」即可,若有特殊的接入点无法自动识别到,可以手动选择对应接入点"), + cls.TRANSFER_SPEED_LIMIT: _("Agent配置中对文件传输速率的硬限制,单位「Mbytes/s」,不填则使用Agent默认值100Mbytes/s"), + cls.ADDRESS_TYPE: _("记录到 CMDB 中的对应枚举字段。默认为「静态」"), + cls.DATA_COMPRESSION: _("开启数据压缩后,所有通过数据管道传输的日志采集数据的流量都将进行压缩,可一定程度上降低数据上报所带来的带宽压力。但会带来少量额外的CPU消耗"), + } + + +class ExcelOptionalType(EnhanceEnum): + REQUIRED = 0 + OPTIONAL = 1 + BOTH_NOT_EMPTY = 2 + + @classmethod + def _get_member__alias_map(cls) -> Dict[Enum, str]: + return {cls.REQUIRED: _("必填"), cls.OPTIONAL: _("可选"), cls.BOTH_NOT_EMPTY: _("与「{}」不能同时为空")} class ExcelAuthType(EnhanceEnum): diff --git a/apps/node_man/handlers/excel.py b/apps/node_man/handlers/excel.py index 469b8c409..637d82bae 100644 --- a/apps/node_man/handlers/excel.py +++ b/apps/node_man/handlers/excel.py @@ -9,16 +9,13 @@ specific language governing permissions and limitations under the License. """ import logging -import re -from typing import Any, Dict, List +from typing import Any, Dict -from django.core.files.uploadedfile import InMemoryUploadedFile -from openpyxl import Workbook, load_workbook +from openpyxl import Workbook from apps.node_man import constants, models from apps.node_man.handlers.cmdb import CmdbHandler from apps.node_man.tools.excel import ExcelTools -from apps.node_man.tools.host import HostTools logger = logging.getLogger("app") @@ -31,21 +28,15 @@ class ExcelHandler: @classmethod def generate_excel_template(cls): - # 整合数据转为下拉框所需列表, [id]name 格式 - all_install_channel = [ - f"[{item['id']}]{item['name']}" for item in list(models.InstallChannel.objects.all().values()) + # 整合数据转为下拉框所需列表 + all_install_channel = [constants.DEFAULT_INSTALL_CHANNEL_NAME] + list( + models.InstallChannel.install_channel_id_name_map().values() + ) + all_biz = [item["bk_biz_name"] for item in CmdbHandler().biz(param={"action": "agent_operate"})] + all_cloud = [constants.DEFAULT_CLOUD_NAME] + [ + cloud.bk_cloud_name for cloud in models.Cloud.objects.all().only("bk_cloud_name") ] - all_install_channel.insert(0, "[0]default") - all_biz = [ - f"[{item['bk_biz_id']}]{item['bk_biz_name']}" - for item in CmdbHandler().biz(param={"action": "agent_operate"}) - ] - all_cloud = [ - f"[{item['bk_cloud_id']}]{item['bk_cloud_name']}" for item in list(models.Cloud.objects.all().values()) - ] - all_cloud.insert(0, f"[{constants.DEFAULT_CLOUD}]{constants.DEFAULT_CLOUD_NAME}") - all_ap = [f"[{item['id']}]{item['name']}" for item in list(models.AccessPoint.objects.all().values())] - + all_ap = [constants.AUTOMATIC_CHOICE] + [ap.name for ap in models.AccessPoint.objects.all().only("name")] all_os = list(constants.OsType) all_auth_type = [str(type) for type in constants.ExcelAuthType.get_member_value__alias_map().values()] all_addressing = [str(type) for type in constants.CmdbAddressingType.get_member_value__alias_map().values()] @@ -56,136 +47,48 @@ def generate_excel_template(cls): excel_sheet = excel.active excel_sheet.title = MAIN_SHEET_NAME - excel_field: Dict[Any, str] = constants.ExcelField.get_member_value__alias_map() - excel_field_list = list(excel_field.keys()) - for col, key in enumerate(excel_field_list, start=1): + excel_field: Dict[Any, str] = constants.ExcelField._get_member__alias_map() + excel_optional = constants.ExcelOptionalType._get_member__alias_map() + excel_field_optional = constants.ExcelField.get_excel_optional_map() + excel_describe = constants.ExcelField.get_excel_describe_map() + for col, key in enumerate(constants.ExcelField, start=1): title_row_cell = excel_sheet.cell(row=1, column=col, value=str(excel_field[key])) ExcelTools.set_font_style(title_row_cell, font_size=16, color="538DD5", bold=True) - key_row_cell = excel_sheet.cell(row=2, column=col, value=str(key)) - ExcelTools.set_font_style(key_row_cell, font_size=12, color="538DD5", bold=True) - - optional_row_cell = excel_sheet.cell(row=3, column=col, value=constants.EXCEL_TITLE_OPTIONAL[key]) - if constants.EXCEL_TITLE_OPTIONAL[key] == constants.EXCEL_REQUIRED: + optional_row_cell = excel_sheet.cell(row=2, column=col, value=str(excel_field_optional[key])) + if excel_field_optional[key] == excel_optional[constants.ExcelOptionalType.REQUIRED]: ExcelTools.set_font_style(optional_row_cell, font_size=12, color="C0504D") else: ExcelTools.set_font_style(optional_row_cell, font_size=12, color="E26B0A") - describe_row_cell = excel_sheet.cell(row=4, column=col, value=constants.EXCEL_TITLE_DESCRIBE[key]) + describe_row_cell = excel_sheet.cell(row=3, column=col, value=str(excel_describe[key])) ExcelTools.set_font_style(describe_row_cell, font_size=12, color="000000") - if key == constants.ExcelField.OS_TYPE.value: - ExcelTools.create_dropdown(excel, 5, col, key, MAIN_SHEET_NAME, all_os) - elif key == constants.ExcelField.INSTALL_CHANNEL.value: - ExcelTools.create_dropdown(excel, 5, col, key, MAIN_SHEET_NAME, all_install_channel) - elif key == constants.ExcelField.AUTH_TYPE.value: - ExcelTools.create_dropdown(excel, 5, col, key, MAIN_SHEET_NAME, all_auth_type) - elif key == constants.ExcelField.BIZ.value: - ExcelTools.create_dropdown(excel, 5, col, key, MAIN_SHEET_NAME, all_biz) - elif key == constants.ExcelField.CLOUD.value: - ExcelTools.create_dropdown(excel, 5, col, key, MAIN_SHEET_NAME, all_cloud) - elif key == constants.ExcelField.AP.value: - ExcelTools.create_dropdown(excel, 5, col, key, MAIN_SHEET_NAME, all_ap) - elif key == constants.ExcelField.ADDRESS_TYPE.value: - ExcelTools.create_dropdown(excel, 5, col, key, MAIN_SHEET_NAME, all_addressing) - elif key == constants.ExcelField.DATA_COMPRESSION.value: - ExcelTools.create_dropdown(excel, 5, col, key, MAIN_SHEET_NAME, all_enable_compression) + if key == constants.ExcelField.OS_TYPE: + ExcelTools.create_dropdown(excel, 4, col, key.value, MAIN_SHEET_NAME, all_os) + elif key == constants.ExcelField.INSTALL_CHANNEL: + ExcelTools.create_dropdown(excel, 4, col, key.value, MAIN_SHEET_NAME, all_install_channel) + elif key == constants.ExcelField.AUTH_TYPE: + ExcelTools.create_dropdown(excel, 4, col, key.value, MAIN_SHEET_NAME, all_auth_type) + elif key == constants.ExcelField.BIZ: + ExcelTools.create_dropdown(excel, 4, col, key.value, MAIN_SHEET_NAME, all_biz) + elif key == constants.ExcelField.CLOUD: + ExcelTools.create_dropdown(excel, 4, col, key.value, MAIN_SHEET_NAME, all_cloud) + elif key == constants.ExcelField.AP: + ExcelTools.create_dropdown(excel, 4, col, key.value, MAIN_SHEET_NAME, all_ap) + elif key == constants.ExcelField.ADDRESS_TYPE: + ExcelTools.create_dropdown(excel, 4, col, key.value, MAIN_SHEET_NAME, all_addressing) + elif key == constants.ExcelField.DATA_COMPRESSION: + ExcelTools.create_dropdown(excel, 4, col, key.value, MAIN_SHEET_NAME, all_enable_compression) else: pass - ExcelTools.fill_color(excel_sheet, 1, 4, 1, len(excel_field_list), "D9D9D9") - ExcelTools.adjust_row_height(excel_sheet, 1, 3, 20) - ExcelTools.adjust_row_height(excel_sheet, 4, 4, 115) - ExcelTools.adjust_col_width(excel_sheet, 1, len(excel_field_list), 35) + ExcelTools.fill_color(excel_sheet, 1, 3, 1, len(excel_field), "D9D9D9") + # 调整首行高度 25 次行 35 描述行 175 宽度 35 + ExcelTools.adjust_row_height(excel_sheet, 1, 1, 30) + ExcelTools.adjust_row_height(excel_sheet, 2, 2, 35) + ExcelTools.adjust_row_height(excel_sheet, 3, 3, 175) + ExcelTools.adjust_col_width(excel_sheet, 1, len(excel_field), 35) ExcelTools.set_alignment(excel_sheet, "center", "left") return excel - - def analyze_excel(self, file: InMemoryUploadedFile) -> List[Dict]: - - # 解析excel - excel = load_workbook(filename=file) - excel_sheet = excel.active - keys = [cell.value for cell in excel_sheet[2]] - - # 正则匹配处理 [id]name 类型的下拉框内容 - pattern = r"\[(\d+)\]" - - # 获取加密cipher - cipher = HostTools.get_asymmetric_cipher() - - required_list = [ - key for key, value in constants.EXCEL_TITLE_OPTIONAL.items() if value == constants.EXCEL_REQUIRED - ] - - error_message: List[str] = [] - excel_data = [] - for index, row in enumerate(excel_sheet.iter_rows(min_row=5, values_only=True), start=5): - - row_data = {keys[i]: cell for i, cell in enumerate(row)} - - row_err_msg: List[str] = [] - - if ( - row_data[constants.ExcelField.INNER_IPV4.value] is None - and row_data[constants.ExcelField.INNER_IPV6.value] is None - ): - row_err_msg.append(ANALYZE_ERROR_MSG.format(index, "IP")) - - for key in required_list: - if row_data[key] is None: - row_err_msg.append(ANALYZE_ERROR_MSG.format(index, key)) - - if row_data[constants.ExcelField.INSTALL_CHANNEL.value] is not None: - install_channel = re.findall(pattern, row_data[constants.ExcelField.INSTALL_CHANNEL.value]) - if not install_channel: - row_err_msg.append(ANALYZE_ERROR_MSG.format(index, constants.ExcelField.INSTALL_CHANNEL.value)) - row_data[constants.ExcelField.INSTALL_CHANNEL.value] = int(install_channel[0]) - - if row_data[constants.ExcelField.BIZ.value] is not None: - biz = re.findall(pattern, row_data[constants.ExcelField.BIZ.value]) - if not biz: - row_err_msg.append(ANALYZE_ERROR_MSG.format(index, constants.ExcelField.BIZ.value)) - row_data[constants.ExcelField.BIZ.value] = int(biz[0]) - - if row_data[constants.ExcelField.CLOUD.value] is not None: - cloud = re.findall(pattern, row_data[constants.ExcelField.CLOUD.value]) - if not cloud: - row_err_msg.append(ANALYZE_ERROR_MSG.format(index, constants.ExcelField.CLOUD.value)) - row_data[constants.ExcelField.CLOUD.value] = int(cloud[0]) - - if row_data[constants.ExcelField.AP.value] is not None: - ap = re.findall(pattern, row_data[constants.ExcelField.AP.value]) - if not ap: - row_err_msg.append(ANALYZE_ERROR_MSG.format(index, constants.ExcelField.AP.value)) - row_data[constants.ExcelField.AP.value] = int(ap[0]) - - if len(row_err_msg) > 0: - error_message.extend(row_err_msg) - continue - - credentials: str = str(row_data[constants.ExcelField.CREDENTIALS.value]) - if ( - row_data[constants.ExcelField.AUTH_TYPE.value] - == constants.ExcelAuthType.get_member_value__alias_map()[constants.ExcelAuthType.PASSWORD.value] - ): - row_data[constants.ExcelField.AUTH_TYPE.value] = constants.ExcelAuthType.PASSWORD.value - row_data["password"] = HostTools.encrypt_with_friendly_exc_handle(cipher, credentials, ValueError) - else: - row_data[constants.ExcelField.AUTH_TYPE.value] = constants.ExcelAuthType.KEY.value - row_data["key"] = HostTools.encrypt_with_friendly_exc_handle(cipher, credentials, ValueError) - - del row_data[constants.ExcelField.CREDENTIALS.value] - - if ( - row_data[constants.ExcelField.ADDRESS_TYPE.value] - == constants.CmdbAddressingType.get_member_value__alias_map()[constants.CmdbAddressingType.STATIC.value] - ): - row_data[constants.ExcelField.ADDRESS_TYPE.value] = constants.CmdbAddressingType.STATIC.value - else: - row_data[constants.ExcelField.ADDRESS_TYPE.value] = constants.CmdbAddressingType.DYNAMIC.value - - excel_data.append(row_data) - - res = {"host": excel_data, "error_message": error_message} - return res diff --git a/apps/node_man/serializers/excel.py b/apps/node_man/serializers/excel.py index 2040bf0a6..808f67231 100644 --- a/apps/node_man/serializers/excel.py +++ b/apps/node_man/serializers/excel.py @@ -13,7 +13,3 @@ class ExcelDownloadSerializer(serializers.Serializer): pass - - -class ExcelUploadSerializer(serializers.Serializer): - file = serializers.FileField() diff --git a/apps/node_man/tools/excel.py b/apps/node_man/tools/excel.py index f5d107689..9552826bb 100644 --- a/apps/node_man/tools/excel.py +++ b/apps/node_man/tools/excel.py @@ -45,7 +45,7 @@ def create_dropdown( sheet = excel.create_sheet(title=src_sheet) main_sheet = excel[dst_sheet] for i, option in enumerate(options, start=1): - sheet[f"A{i}"] = option + sheet[f"A{i}"] = str(option) dv = DataValidation(type="list", formula1=f"={src_sheet}!$A$1:$A${len(options)}", allow_blank=True) diff --git a/apps/node_man/views/excel.py b/apps/node_man/views/excel.py index 1b9bea091..4d08a7458 100644 --- a/apps/node_man/views/excel.py +++ b/apps/node_man/views/excel.py @@ -14,14 +14,10 @@ from drf_yasg.utils import swagger_auto_schema from rest_framework import status from rest_framework.decorators import action -from rest_framework.response import Response from apps.generic import APIViewSet from apps.node_man.handlers.excel import ExcelHandler -from apps.node_man.serializers.excel import ( - ExcelDownloadSerializer, - ExcelUploadSerializer, -) +from apps.node_man.serializers.excel import ExcelDownloadSerializer EXCEL_VIEW_TAGS = ["excel"] @@ -56,30 +52,3 @@ def download(self, request): response.headers["Content-Type"] = "application/octet-stream" response.headers["Content-Disposition"] = 'attachment;filename="{}"'.format(filename) return response - - @swagger_auto_schema( - operation_summary="上传excel", - request_body=ExcelUploadSerializer(), - tags=EXCEL_VIEW_TAGS, - ) - @action(detail=False, methods=["POST"], serializer_class=ExcelUploadSerializer) - def upload(self, request): - """ - @api {POST} /excel/upload/ 上传excel - @apiName upload_excel - @apiGroup Excel - @apiParam {File} file excel文件 - @apiParamExample {Form} 请求例子: - { - "file": file - } - @apiSuccessExample {Json} 成功返回: - { - } - """ - ser = self.serializer_class(data=request.data) - ser.is_valid(raise_exception=True) - data = ser.validated_data - file = data["file"] - - return Response(ExcelHandler().analyze_excel(file)) diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo index 5f0987a6c..29c1320c4 100644 Binary files a/locale/en/LC_MESSAGES/django.mo and b/locale/en/LC_MESSAGES/django.mo differ diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 892d49019..43bc3c40f 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -6666,3 +6666,119 @@ msgstr "Variable variable" #~ msgid "日志详情获取成功" #~ msgstr "Log details fetched successfully" + +#: apps/node_man/constants.py: 1240 +msgid "内网 IPv4" +msgstr "LAN IPv4" + +#: apps/node_man/constants.py: 1241 +msgid "内网 IPv6" +msgstr "LAN IPv6" + +#: apps/node_man/constants.py: 1244 +msgid "登录端口" +msgstr "Login port" + +#: apps/node_man/constants.py: 1245 +msgid "登录账号" +msgstr "Login account" + +#: apps/node_man/constants.py: 1246 +msgid "认证方式" +msgstr "Authentication" + +#: apps/node_man/constants.py: 1247 +msgid "密码/密钥" +msgstr "Key/Password" + +#: apps/node_man/constants.py: 1248 +msgid "外网 IP" +msgstr "WAN IP" + +#: apps/node_man/constants.py: 1249 +msgid "登录 IP" +msgstr "Login IP" + +#: apps/node_man/constants.py: 1252 +msgid "接入点" +msgstr "Access point" + +#: apps/node_man/constants.py: 1255 +msgid "数据压缩" +msgstr "Data Compression" + +#: apps/node_man/constants.py: 1316 +msgid "必填" +msgstr "Required" + +#: apps/node_man/constants.py: 1316 +msgid "可选" +msgstr "Optional" + +#: apps/node_man/constants.py: 1316 +msgid "与「{}」不能同时为空" +msgstr "Cannot be empty at the same time as「{}」" + +#: apps/node_man/constants.py: 1288 +msgid "目标主机 IPv4 地址" +msgstr "Target host IPv4 address" + +#: apps/node_man/constants.py: 1289 +msgid "目标主机 IPv6 地址" +msgstr "Target host IPv6 address" + +#: apps/node_man/constants.py: 1290 +msgid "目标主机操作系统类型" +msgstr "Target host operating system type" + +#: apps/node_man/constants.py: 1291 +msgid "在特殊复杂网络下,目标主机无法与「管控区域」内主机直接连通,可通过指定「安装通道」进行 Agent 安装。默认使用「default」即可" +msgstr "In a special complex network, the target host cannot directly connect with the hosts in the 「BK-Net」, and can install the agent by specifying the 「Install channel」. Just use 「default」 by default" + +#: apps/node_man/constants.py: 1292 +msgid "登录到目标主机上的sshd端口" +msgstr "Log in to the sshd port on the target host" + +#: apps/node_man/constants.py: 1293 +msgid "登录到目标主机上所使用的用户" +msgstr "The user used to log in to the target host" + +#: apps/node_man/constants.py: 1294 +msgid "登录到目标主机上所使用的认证方式" +msgstr "The authentication method used to log in to the target host" + +#: apps/node_man/constants.py: 1295 +msgid "登录到目标主机上所使用的凭证,根据认证方式提供密码或私钥" +msgstr "The credentials used to log in to the target host, provide the password or private key according to the authentication method" + +#: apps/node_man/constants.py: 1296 +msgid "目标主机外网IP,会自动注册到 CMDB" +msgstr "The external IP address of the target host, it will be automatically registered into CMDB" + +#: apps/node_man/constants.py: 1298 +msgid "用于登录目标主机执行安装的 IP 地址,区别于记录在 CMDB 中的 IP;支持 IPv4、IPv6。若未填写,优先使用「内网IPv4」来登录目标机器,若「内网IPv4」未填写,使用「内网IPv6」" +msgstr "The IP address used to log in to the target host for installation is different from the IP recorded in CMDB; supports IPv4 and IPv6. If not filled in, priority is given to using 「LAN IPv4」 to log in to the target machine. If 「LAN IPv4」 is not filled in, use 「LAN IPv6」" + +#: apps/node_man/constants.py: 1300 +msgid "目标主机归属业务。默认使用「蓝鲸」业务" +msgstr "The target host belongs to the business. Default use of 「Blueking」 business" + +#: apps/node_man/constants.py: 1301 +msgid "目标主机所在的管控区域。若是在某个云区域内,选择该云区域的名字。默认使用「直连区域」" +msgstr "The control area where the target host is located. If within a certain cloud region, select the name of that cloud region. Default use of 「Direct Mode」" + +#: apps/node_man/constants.py: 1302 +msgid "一般情况下使用「自动选择」即可,若有特殊的接入点无法自动识别到,可以手动选择对应接入点" +msgstr "In general, 「Choose automatically」 is sufficient. If there are special access points that cannot be automatically recognized, the corresponding access points can be manually selected" + +#: apps/node_man/constants.py: 1303 +msgid "Agent配置中对文件传输速率的硬限制,单位「Mbytes/s」,不填则使用Agent默认值100Mbytes/s" +msgstr "Hard limit on file transfer rate in Agent configuration, in Mbytes/s. If left blank, the default value of 100Mbytes/s for Agent will be used" + +#: apps/node_man/constants.py: 1304 +msgid "记录到 CMDB 中的对应枚举字段。默认为「静态」" +msgstr "Record the corresponding enumeration fields in CMDB. Default to 「static」" + +#: apps/node_man/constants.py: 1305 +msgid "开启数据压缩后,所有通过数据管道传输的日志采集数据的流量都将进行压缩,可一定程度上降低数据上报所带来的带宽压力。但会带来少量额外的CPU消耗" +msgstr "After enabling data compression, all the traffic of log collection data transmitted through the data pipeline will be compressed, which can to some extent reduce the bandwidth pressure caused by data reporting. But it will bring a small amount of additional CPU consumption"