diff --git a/apps/backend/components/collections/agent_new/install.py b/apps/backend/components/collections/agent_new/install.py index ae2040dce..458a1a339 100644 --- a/apps/backend/components/collections/agent_new/install.py +++ b/apps/backend/components/collections/agent_new/install.py @@ -298,6 +298,18 @@ def _execute(self, data, parent_data, common_data: base.AgentCommonData): host_id__sub_inst_id = { host_id: sub_inst_id for sub_inst_id, host_id in common_data.sub_inst_id__host_id_map.items() } + # 获取安装通道ID与name映射 + install_channel_id_name_map: Dict[str, str] = models.InstallChannel.install_channel_id_name_map(get_cache=True) + for sub_inst_id, sub_inst_obj in common_data.sub_inst_id__sub_inst_obj_map.items(): + install_channel_id: Optional[int] = sub_inst_obj.instance_info["host"].get("install_channel_id") + install_channel_name: str = install_channel_id_name_map.get( + str(install_channel_id), constants.DEFAULT_INSTALL_CHANNEL_NAME + ) + # 输出安装通道日志 + self.log_info( + sub_inst_ids=sub_inst_id, + log_content=_(f"选择的安装通道为:{install_channel_name}"), + ) is_uninstall = data.get_one_of_inputs("is_uninstall") host_id_obj_map = common_data.host_id_obj_map gse_version: str = data.get_one_of_inputs("meta", {}).get("GSE_VERSION") diff --git a/apps/node_man/constants.py b/apps/node_man/constants.py index 6e965680f..ff3aef0a1 100644 --- a/apps/node_man/constants.py +++ b/apps/node_man/constants.py @@ -77,6 +77,12 @@ class TimeUnit: DEFAULT_CLOUD_NAME = os.environ.get("DEFAULT_CLOUD_NAME", _("直连区域")) # 自动选择接入点ID DEFAULT_AP_ID = int(os.environ.get("DEFAULT_AP_ID", -1)) +# 自动选择安装通道ID +DEFAULT_INSTALL_CHANNEL_ID = int(os.environ.get("DEFAULT_AP_ID", -1)) +# 自动选择 +AUTOMATIC_CHOICE = os.environ.get("AUTOMATIC_CHOICE", _("自动选择")) +# 默认安装通道 +DEFAULT_INSTALL_CHANNEL_NAME = os.environ.get("DEFAULT_INSTALL_CHANNEL_NAME", _("默认通道")) # GSE命名空间 GSE_NAMESPACE = "nodeman" diff --git a/apps/node_man/handlers/install_channel.py b/apps/node_man/handlers/install_channel.py index 4a63b3c44..c0dba71e0 100644 --- a/apps/node_man/handlers/install_channel.py +++ b/apps/node_man/handlers/install_channel.py @@ -8,11 +8,13 @@ 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 ipaddress import IPv4Network, ip_address, ip_network from typing import Dict, List from django.forms import model_to_dict -from apps.node_man import models +from apps.core.concurrent.cache import FuncCacheDecorator +from apps.node_man import constants, models from apps.utils import APIModel @@ -54,3 +56,30 @@ def update( def destroy(self, install_channel_id: int): models.InstallChannel.objects.filter(bk_cloud_id=self.bk_cloud_id, id=install_channel_id).delete() + + @staticmethod + @FuncCacheDecorator(cache_time=20 * constants.TimeUnit.MINUTE) + def get_install_channel_id_network_segment(): + install_channel_id_network_segment: Dict[str, List[str]] = models.GlobalSettings.get_config( + key=models.GlobalSettings.KeyEnum.INSTALL_CHANNEL_ID_NETWORK_SEGMENT.value, default={} + ) + return install_channel_id_network_segment + + @classmethod + def judge_install_channel(cls, inner_ip: str): + """ + :param inner_ip: 内网IPv4地址 + :return: 安装通道ID + """ + install_channel_id_network_segment: Dict[str, List[str]] = cls.get_install_channel_id_network_segment( + get_cache=True + ) + network_obj__install_channel_id_map: Dict[IPv4Network, int] = { + ip_network(network_segment): int(install_channel_id) + for install_channel_id, network_segments in install_channel_id_network_segment.items() + for network_segment in network_segments + } + for network_obj, install_channel_id in network_obj__install_channel_id_map.items(): + if ip_address(inner_ip) in network_obj: + return install_channel_id + return None diff --git a/apps/node_man/handlers/job.py b/apps/node_man/handlers/job.py index 20f41c285..92b297603 100644 --- a/apps/node_man/handlers/job.py +++ b/apps/node_man/handlers/job.py @@ -31,6 +31,7 @@ from apps.node_man.handlers.cloud import CloudHandler from apps.node_man.handlers.cmdb import CmdbHandler from apps.node_man.handlers.host import HostHandler +from apps.node_man.handlers.install_channel import InstallChannelHandler from apps.node_man.tools import JobTools from apps.utils import APIModel from apps.utils.basic import filter_values, to_int_or_default @@ -286,6 +287,10 @@ def install( host["ticket"] = ticket if host.get("ap_id"): ap_ids.add(host["ap_id"]) + install_channel_id = host.get("install_channel_id") + if install_channel_id == constants.DEFAULT_INSTALL_CHANNEL_ID and host.get("inner_ip"): + install_channel_id = InstallChannelHandler.judge_install_channel(host["inner_ip"]) + host["install_channel_id"] = install_channel_id # 如果混合了【手动安装】,【自动安装】则不允许通过 # 此处暂不合入 job validator. diff --git a/apps/node_man/models.py b/apps/node_man/models.py index 91bf53f0e..fb57dfe19 100644 --- a/apps/node_man/models.py +++ b/apps/node_man/models.py @@ -164,6 +164,10 @@ class KeyEnum(Enum): IP_CHOOSER_ENABLE_SHOW_REALTIME_AGENT_STATE = "IP_CHOOSER_ENABLE_SHOW_REALTIME_AGENT_STATE" # IP选择器详情接口实时展示agent状态业务白名单 IP_CHOOSER_BIZ_WHITELIST = "IP_CHOOSER_BIZ_WHITELIST" + # 是否仅在直连区域开启自动选择安装通道 + 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" key = models.CharField(_("键"), max_length=255, db_index=True, primary_key=True) v_json = JSONField(_("值")) @@ -826,6 +830,14 @@ def install_channel_id__host_objs_map( return result + @classmethod + @FuncCacheDecorator(cache_time=20 * constants.TimeUnit.MINUTE) + def install_channel_id_name_map(cls) -> Dict[str, str]: + all_install_channel_map = { + str(install_channel["id"]): install_channel["name"] for install_channel in cls.objects.values("id", "name") + } + return all_install_channel_map + class Meta: verbose_name = _("安装通道(InstallChannel)") verbose_name_plural = _("安装通道(InstallChannel)") diff --git a/apps/node_man/tests/test_handlers/test_install_channel.py b/apps/node_man/tests/test_handlers/test_install_channel.py index 1f30b13fd..8906c430a 100644 --- a/apps/node_man/tests/test_handlers/test_install_channel.py +++ b/apps/node_man/tests/test_handlers/test_install_channel.py @@ -13,7 +13,7 @@ from apps.mock_data import common_unit, utils from apps.node_man import constants from apps.node_man.handlers.install_channel import InstallChannelHandler -from apps.node_man.models import InstallChannel +from apps.node_man.models import GlobalSettings, InstallChannel from apps.node_man.tests.utils import create_install_channel from apps.utils.unittest.testcase import CustomAPITestCase @@ -65,6 +65,17 @@ def test_install_channel_destroy(self, *args, **kwargs): ) self.assertEqual(len(InstallChannelHandler.list()), 0) + def test_judge_install_channel(self, *args, **kwargs): + # 构造安装通道与网段映射 + GlobalSettings.set_config( + key=GlobalSettings.KeyEnum.INSTALL_CHANNEL_ID_NETWORK_SEGMENT.value, value={"1": ["127.0.0.0/30"]} + ) + # 创建安装通道 + create_install_channel(1) + inner_ip = "127.0.0.1" + res = InstallChannelHandler.judge_install_channel(inner_ip=inner_ip) + self.assertEqual(res, 1) + class InstallChannelHiddenTestCase(CustomAPITestCase): def test_install_channel_hidden(self): diff --git a/apps/node_man/views/install_channel.py b/apps/node_man/views/install_channel.py index 5fbdc7fd1..6a721e632 100644 --- a/apps/node_man/views/install_channel.py +++ b/apps/node_man/views/install_channel.py @@ -12,6 +12,7 @@ from rest_framework.response import Response from apps.generic import ModelViewSet +from apps.node_man import constants from apps.node_man.handlers.install_channel import InstallChannelHandler from apps.node_man.handlers.permission import InstallChannelPermission from apps.node_man.models import InstallChannel @@ -37,6 +38,11 @@ def get_queryset(self): # 如果 hidden 为 False, 则返回所有未隐藏的安装通道 return InstallChannel.objects.filter(hidden=False) + def list(self, request, *args, **kwargs): + response = super().list(request, *args, **kwargs) + response.data.insert(0, {"id": constants.DEFAULT_INSTALL_CHANNEL_ID, "name": constants.AUTOMATIC_CHOICE}) + return response + @swagger_auto_schema( operation_summary="创建安装通道", tags=INSTALL_CHANNEL_VIEW_TAGS,