forked from TencentBlueKing/bk-nodeman
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 蓝盾安装Agent获取安装策略 (closed TencentBlueKing#2349)
- Loading branch information
Showing
8 changed files
with
506 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
Oops, something went wrong.