Skip to content

Commit

Permalink
feat: 蓝盾安装Agent获取安装策略 (closed TencentBlueKing#2349)
Browse files Browse the repository at this point in the history
  • Loading branch information
Huayeaaa committed Oct 29, 2024
1 parent 5730fa1 commit 1197ee1
Show file tree
Hide file tree
Showing 8 changed files with 506 additions and 0 deletions.
53 changes: 53 additions & 0 deletions apps/node_man/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,8 @@ def _get_member__alias_map(cls) -> Dict[Enum, str]:
)
WINDOWS_PORT = 445
WINDOWS_ACCOUNT = "Administrator"
SSH_PORT = 22
LINUX_PORT = 36000
LINUX_ACCOUNT = "root"
APPLY_RESOURCE_WATCH_EVENT_LENS = 2000

Expand Down Expand Up @@ -1209,3 +1211,54 @@ def _get_member__alias_map(cls) -> Dict[Enum, str]:
@classmethod
def cpu_type__os_bit_map(cls):
return {CpuType.x86: cls.BIT32.value, CpuType.x86_64: cls.BIT64.value, CpuType.aarch64: cls.ARM.value}


class HttpPortType(EnhanceEnum):
HTTP_PORT = 80
HTTPS_PORT = 443

@classmethod
def _get_member__alias_map(cls) -> Dict[Enum, str]:
return {cls.HTTP_PORT: _("HTTP端口"), cls.HTTPS_PORT: _("HTTPS端口")}


class NginxPortType(EnhanceEnum):
NGINX_DOWNLOAD_PORT = 17980
NGINX_PROXY_PASS_PORT = 17981

@classmethod
def _get_member__alias_map(cls) -> Dict[Enum, str]:
return {cls.NGINX_DOWNLOAD_PORT: _("Nginx下载端口"), cls.NGINX_PROXY_PASS_PORT: _("Nginx代理端口")}


class PortProtocolType(EnhanceEnum):
TCP = "TCP"
UDP = "UDP"

@classmethod
def _get_member__alias_map(cls) -> Dict[Enum, str]:
return {cls.TCP: _("TCP协议"), cls.UDP: _("UDP协议")}


class UseType(EnhanceEnum):
SSH_CONNECT_PORT = "ssh_connect_port"
NGINX_DOWNLOAD = "nginx_download"
NGINX_PROXY_PASS = "nginx_proxy_pass"
REPORT_LOG = "report_log"
GET_CONFIG = "get_config"
TASK_SERVE_PORT = "task_serve_port"
DATA_SERVE_PORT = "data_serve_port"
FILE_TRANSMIT_PORT = "file_transmit_port"

@classmethod
def _get_member__alias_map(cls) -> Dict[Enum, str]:
return {
cls.SSH_CONNECT_PORT: _("ssh连接端口"),
cls.NGINX_DOWNLOAD: _("nginx下载"),
cls.NGINX_PROXY_PASS: _("nginx代理"),
cls.REPORT_LOG: _("上报日志"),
cls.GET_CONFIG: _("获取配置"),
cls.TASK_SERVE_PORT: _("任务服务端口"),
cls.DATA_SERVE_PORT: _("数据服务端口"),
cls.FILE_TRANSMIT_PORT: _("文件传输端口"),
}
6 changes: 6 additions & 0 deletions apps/node_man/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,9 @@ class YunTiPolicyConfigNotExistsError(NodeManBaseException):
MESSAGE = _("云梯策略配置不存在")
MESSAGE_TPL = _("云梯策略配置不存在")
ERROR_CODE = 43


class DefaultCloudNotExistsNetworkStrategy(NodeManBaseException):
MESSAGE = _("直连区域无需配置网络策略")
MESSAGE_TPL = _("直连区域无需配置网络策略")
ERROR_CODE = 44
272 changes: 272 additions & 0 deletions apps/node_man/handlers/network_strategy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-节点管理(BlueKing-BK-NODEMAN) available.
Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at https://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from collections import defaultdict
from typing import Any, Dict, List, Tuple, Union

from django.db.models import QuerySet
from django.utils.translation import ugettext as _

from apps.node_man import constants, exceptions, models


class NetworkStrategyHandler:
@staticmethod
def aggregate_host_info(host_info: List[Dict[str, Union[int, str]]]) -> List[Dict[str, Any]]:
"""
:param host_info: 主机信息
:return: 聚合同云区域后的主机信息
"""
ips_gby_bk_cloud_id: Dict[Tuple, Any] = defaultdict(list)
for item in host_info:
key = (item["bk_cloud_id"], item["is_install_proxy_strategy"])
ips_gby_bk_cloud_id[key].append(item["host_ip"])

result: List[Dict[str, Any]] = [
{"bk_cloud_id": key[0], "host_ips": value, "is_install_proxy_strategy": key[1]}
for key, value in ips_gby_bk_cloud_id.items()
]
return result

@staticmethod
def get_ap_common_config(ap_obj: models.AccessPoint):
"""
获取接入点公共配置
:param ap_obj: 接入点对象
:return: 接入点名称,端口配置,任务服务器IP,数据服务器IP,文件服务器IP
"""
ap_name: str = ap_obj.name
port_config: Dict[str, int] = ap_obj.port_config
task_server: List[Dict[str, Any]] = ap_obj.taskserver
data_server: List[Dict[str, Any]] = ap_obj.dataserver
file_server: List[Dict[str, Any]] = ap_obj.btfileserver
gse_servers = [task_server, data_server, file_server]
if all(isinstance(server, list) for server in gse_servers):
task_server_ips: str = ",".join([task_server_info["inner_ip"] for task_server_info in task_server])
data_server_ips: str = ",".join([data_server_info["inner_ip"] for data_server_info in data_server])
file_server_ips: str = ",".join([file_server_info["inner_ip"] for file_server_info in file_server])
else:
# 兼容接入点改造后的server信息
task_server_ips = ",".join([outer_ip["ip"] for outer_ip in task_server["outer_ip_infos"]])
data_server_ips = ",".join([outer_ip["ip"] for outer_ip in data_server["outer_ip_infos"]])
file_server_ips = ",".join([outer_ip["ip"] for outer_ip in file_server["outer_ip_infos"]])
return ap_name, port_config, task_server_ips, data_server_ips, file_server_ips

@staticmethod
def get_gse_common_port(port_config: Dict[str, int]):
"""
获取Agent与Proxy所需共同端口
:param port_config: 端口配置字典
:return: Agent与Proxy所需共同端口
"""
io_port: int = port_config["io_port"]
data_port: int = port_config["data_port"]
bt_port: int = port_config["bt_port"]
tracker_port: int = port_config["tracker_port"]
return io_port, data_port, bt_port, tracker_port

@staticmethod
def structure_strategy_data(
source_address: str, target_address: str, port: Union[int, str], protocol: str, use: str
):
"""
构造策略数据字典
:param source_address: 源地址
:param target_address: 目标地址
:param port: 端口
:param protocol: 通信协议
:param use: 用途
:return: 策略数据
"""
return {
"source_address": source_address,
"target_address": target_address,
"port": str(port),
"protocol": protocol,
"use": use,
}

@staticmethod
def port_use():
"""
:return: 各个端口的用途
"""
# 构造用途映射
use_map: Dict[str, str] = {
key: str(value) for key, value in constants.UseType.get_member_value__alias_map().items()
}
# P-agent的上报日志和Nginx的代理转发用途
nginx_download_proxy_pass = (
f"{use_map[constants.UseType.NGINX_DOWNLOAD.value]}/{use_map[constants.UseType.NGINX_PROXY_PASS.value]}"
)
task_serve: str = f"{use_map[constants.UseType.TASK_SERVE_PORT.value]}"
data_serve: str = f"{use_map[constants.UseType.DATA_SERVE_PORT.value]}"
file_transmit: str = f"{use_map[constants.UseType.FILE_TRANSMIT_PORT.value]}"

get_config: str = f"{use_map[constants.UseType.GET_CONFIG.value]}"
report_log: str = f"{use_map[constants.UseType.REPORT_LOG.value]}"
ssh_connect: str = f"{use_map[constants.UseType.SSH_CONNECT_PORT.value]}"
return nginx_download_proxy_pass, task_serve, data_serve, file_transmit, get_config, report_log, ssh_connect

def install_strategy(self, host_info: List[Dict[str, Union[int, str]]]) -> List[Dict[str, Any]]:
"""
安装策略
:param host_info: 主机信息
:return: 策略数据
"""
result = []
# 聚合相同云区域的主机IP
host_info: List[Dict[str, Any]] = self.aggregate_host_info(host_info=host_info)
cloud_id_name_map, cloud_ap_id_map, ap_id_obj_map = self.get_cloud_info_and_ap_map()
# 各个端口的用途
(
nginx_download_proxy_pass,
task_serve,
data_serve,
file_transmit,
get_config,
report_log,
ssh_connect,
) = self.port_use()

# 获取proxy所需端口
ssh_connect_port = f"{constants.SSH_PORT} or {constants.LINUX_PORT}"
http_port = constants.HttpPortType.HTTP_PORT.value
https_port = constants.HttpPortType.HTTPS_PORT.value
report_log_port = f"{http_port},{https_port}"
# Agent上报日志及下载文件所需端口
nginx_download_proxy_pass_port: str = (
f"{constants.NginxPortType.NGINX_DOWNLOAD_PORT.value},"
f"{constants.NginxPortType.NGINX_PROXY_PASS_PORT.value}"
)
# 获取后台配置的公网IP信息
nodeman_outer_ip, nginx_server_ip, blueking_external_saas_ip = self.get_network_strategy_config()
# 获取通信协议
tcp_value, udp_value, tcp_udp_values = self.list_communication_protocol()

for host in host_info:
bk_cloud_id: int = host["bk_cloud_id"]
if bk_cloud_id == constants.DEFAULT_CLOUD and not host["is_install_proxy_strategy"]:
raise exceptions.DefaultCloudNotExistsNetworkStrategy(_("直连Agent端:与蓝鲸服务端双向全通,若存在策略限制,可新增管控区域来管理."))
if bk_cloud_id == constants.DEFAULT_CLOUD and host["is_install_proxy_strategy"]:
raise exceptions.ProxyNotAvaliableError(_("直连区域不可安装Proxy"))
bk_cloud_name: str = cloud_id_name_map.get(str(bk_cloud_id))
if not bk_cloud_name:
raise exceptions.CloudNotExistError(_(f"不存在ID为: {bk_cloud_id} 的管控区域"))
ap_id: int = cloud_ap_id_map.get(bk_cloud_id)
ap_obj: models.AccessPoint = ap_id_obj_map.get(ap_id)
if not ap_obj or ap_id == constants.DEFAULT_AP_ID:
raise exceptions.ApIDNotExistsError(_("该云区域未选择接入点或原接入点不存在数据库中"))

# 获取接入点名称,端口配置,任务服务器IP,数据服务器IP,文件服务器IP
ap_name, port_config, task_server_ips, data_server_ips, file_server_ips = self.get_ap_common_config(
ap_obj=ap_obj
)
# 获取Agent与Proxy所需共同端口
io_port, data_port, bt_port, tracker_port = self.get_gse_common_port(port_config=port_config)
if not host["is_install_proxy_strategy"]:
bt_transfer_port_scope = f"{bt_port}-{tracker_port}"
bt_port_start = port_config["bt_port_start"]
bt_port_end = port_config["bt_port_end"]
bt_port_scope = f"{bt_port_start}-{bt_port_end}"
file_svr_port = port_config["file_svr_port"]
host_queryset: QuerySet = models.Host.objects.filter(
bk_cloud_id=bk_cloud_id, node_type=constants.NodeType.PROXY
).values("inner_ip")
proxies_ips: str = ",".join([host["inner_ip"] for host in host_queryset])
host_ips = ",".join(host["host_ips"])
strategy_data = [
self.structure_strategy_data(
host_ips, proxies_ips, nginx_download_proxy_pass_port, tcp_value, nginx_download_proxy_pass
),
self.structure_strategy_data(host_ips, proxies_ips, io_port, tcp_value, task_serve),
self.structure_strategy_data(host_ips, proxies_ips, data_port, tcp_value, data_serve),
self.structure_strategy_data(host_ips, proxies_ips, file_svr_port, tcp_value, file_transmit),
self.structure_strategy_data(host_ips, proxies_ips, bt_port, tcp_udp_values, file_transmit),
self.structure_strategy_data(host_ips, proxies_ips, tracker_port, udp_value, file_transmit),
self.structure_strategy_data(
proxies_ips, host_ips, bt_transfer_port_scope, tcp_udp_values, file_transmit
),
self.structure_strategy_data(proxies_ips, host_ips, bt_port_scope, tcp_udp_values, file_transmit),
self.structure_strategy_data(
host_ips, host_ips, bt_transfer_port_scope, tcp_udp_values, file_transmit
),
self.structure_strategy_data(host_ips, host_ips, bt_port_scope, tcp_udp_values, file_transmit),
]
strategy_name = f"{bk_cloud_name}-{ap_name}"
result.append({"agent_strategy_name": strategy_name, "agent_strategy_data": strategy_data})
else:
file_topology_bind_port = port_config["file_topology_bind_port"]
proxies_ips = ",".join(host["host_ips"])
strategy_data = [
self.structure_strategy_data(
nodeman_outer_ip, proxies_ips, ssh_connect_port, tcp_value, ssh_connect
),
self.structure_strategy_data(proxies_ips, data_server_ips, io_port, tcp_value, task_serve),
self.structure_strategy_data(proxies_ips, task_server_ips, data_port, tcp_value, data_serve),
self.structure_strategy_data(
proxies_ips, file_server_ips, file_topology_bind_port, tcp_value, file_transmit
),
self.structure_strategy_data(proxies_ips, nginx_server_ip, http_port, tcp_value, get_config),
self.structure_strategy_data(
proxies_ips, blueking_external_saas_ip, report_log_port, tcp_value, report_log
),
self.structure_strategy_data(proxies_ips, proxies_ips, bt_port, tcp_udp_values, file_transmit),
self.structure_strategy_data(proxies_ips, proxies_ips, tracker_port, udp_value, file_transmit),
self.structure_strategy_data(
proxies_ips, proxies_ips, file_topology_bind_port, tcp_value, file_transmit
),
]
strategy_name = f"{bk_cloud_name}-{ap_name}"
result.append({"proxy_strategy_name": strategy_name, "proxy_strategy_data": strategy_data})

return result

@staticmethod
def get_network_strategy_config():
"""
获取全局配置中的各项策略IP,可配置多个
:return: 节点管理出口IP、nginx服务IP、蓝鲸外部版SAAS_IP
{
nodeman_outer_ip: "127.0.0.1",
nginx_server_ip: "127.0.0.2",
blueking_external_saas_ip: ["127.0.0.3", "127.0.0.4"]
}
"""
network_strategy_config: Dict[str, Any] = models.GlobalSettings.get_config(
key=models.GlobalSettings.KeyEnum.NETWORK_STRATEGY_CONFIG.value, default={}
)
network_strategy_config: Dict[str, str] = {
key: ",".join(value) for key, value in network_strategy_config.items()
}
nodeman_outer_ip: str = network_strategy_config.get("nodeman_outer_ip", "")
nginx_server_ip: str = network_strategy_config.get("nginx_server_ip", "")
blueking_external_saas_ip: str = network_strategy_config.get("blueking_external_saas_ip", "")
return nodeman_outer_ip, nginx_server_ip, blueking_external_saas_ip

@staticmethod
def list_communication_protocol():
"""
列举通信协议
"""
tcp_value = constants.PortProtocolType.TCP.value
udp_value = constants.PortProtocolType.UDP.value
tcp_udp_values = f"{tcp_value},{udp_value}"
return tcp_value, udp_value, tcp_udp_values

@staticmethod
def get_cloud_info_and_ap_map():
"""
:return: 云区域ID与云区域名称映射、云区域ID与接入点ID映射、接入点ID与接入点对象映射
"""
cloud_id_name_map: Dict[str, str] = models.Cloud.cloud_id_name_map(get_cache=True)
cloud_ap_id_map: Dict[int, int] = models.Cloud.cloud_ap_id_map()
ap_id_obj_map: Dict[int, models.AccessPoint] = models.AccessPoint.ap_id_obj_map()
return cloud_id_name_map, cloud_ap_id_map, ap_id_obj_map
2 changes: 2 additions & 0 deletions apps/node_man/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ class KeyEnum(Enum):
AUTO_SELECT_INSTALL_CHANNEL_ONLY_DIRECT_AREA = "AUTO_SELECT_INSTALL_CHANNEL_ONLY_DIRECT_AREA"
# 安装通道ID与网段列表映射
INSTALL_CHANNEL_ID_NETWORK_SEGMENT = "INSTALL_CHANNEL_ID_NETWORK_SEGMENT"
# 网络策略相关配置
NETWORK_STRATEGY_CONFIG = "NETWORK_STRATEGY_CONFIG"

key = models.CharField(_("键"), max_length=255, db_index=True, primary_key=True)
v_json = JSONField(_("值"))
Expand Down
22 changes: 22 additions & 0 deletions apps/node_man/serializers/network_strategy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-节点管理(BlueKing-BK-NODEMAN) available.
Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at https://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers


class CloudHostInfoSerializer(serializers.Serializer):
bk_cloud_id = serializers.IntegerField(label=_("管控区域ID"))
host_ip = serializers.IPAddressField(label=_("主机IP"), protocol="ipv4")
is_install_proxy_strategy = serializers.BooleanField(label=_("是否为安装Proxy策略"), required=False, default=False)


class InstallStrategySerializer(serializers.Serializer):
host_info = serializers.ListField(label=_("主机信息"), child=CloudHostInfoSerializer())
Loading

0 comments on commit 1197ee1

Please sign in to comment.