Skip to content

Commit

Permalink
节点管理-评审会: 主机「复制」按钮支持导出「管控区域 ID:IP」 (closed TencentBlueKing#1963)
Browse files Browse the repository at this point in the history
  • Loading branch information
Huayeaaa committed Jan 19, 2024
1 parent 8e36cae commit 802cecd
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 7 deletions.
12 changes: 12 additions & 0 deletions apps/core/ipchooser/handlers/host_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def check(
bk_host_id_set: typing.Set[int] = set()
bk_host_name_set: typing.Set[str] = set()
cloud_inner_ip_set: typing.Set[str] = set()
cloud_inner_ipv6_set: typing.Set[str] = set()

for ip_or_cloud_ip in ip_list:
# 按分隔符切割,获取切割后长度
Expand All @@ -74,7 +75,17 @@ def check(
if block_num == 1:
inner_ip_set.add(ip_or_cloud_ip)
else:
if "[" and "]" in ip_or_cloud_ip:
ip_or_cloud_ip = ip_or_cloud_ip.replace("[", "").replace("]", "")
cloud_inner_ip_set.add(ip_or_cloud_ip)
for ipv6_or_cloud_ip in ipv6_list:
block_num: int = len(ipv6_or_cloud_ip.split(constants.CommonEnum.SEP.value, 1))
if block_num == 1:
inner_ip_set.add(ipv6_or_cloud_ip)
else:
if "[" and "]" in ipv6_or_cloud_ip:
ipv6_or_cloud_ip = ipv6_or_cloud_ip.replace("[", "").replace("]", "")
cloud_inner_ipv6_set.add(ipv6_or_cloud_ip)
for key in key_list:
# 尝试将关键字解析为主机 ID
try:
Expand All @@ -90,6 +101,7 @@ def check(
{"key": "bk_host_id", "val": bk_host_id_set},
{"key": "bk_host_name", "val": bk_host_name_set},
{"key": "cloud_inner_ip", "val": cloud_inner_ip_set},
{"key": "cloud_inner_ipv6", "val": cloud_inner_ipv6_set},
]
return cls.details_base(scope_list, or_conditions, limit_host_ids=limit_host_ids)

Expand Down
48 changes: 48 additions & 0 deletions apps/core/ipchooser/tests/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,51 @@
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 unittest.mock import patch

from django.test import TestCase

from apps.core.ipchooser.handlers.host_handler import HostHandler
from apps.node_man import models
from apps.node_man.tests.utils import MockClient, cmdb_or_cache_biz, create_host


class TestHostHandler(TestCase):
@patch("apps.node_man.handlers.cmdb.CmdbHandler.cmdb_or_cache_biz", cmdb_or_cache_biz)
@patch("apps.node_man.handlers.cmdb.client_v2", MockClient)
def test_check(self):
create_host(1, bk_host_id=1000, ip="10.0.0.1", bk_cloud_id=0)
res = HostHandler.check(
scope_list=[{"scope_type": "biz", "scope_id": f"{i}", "bk_biz_id": i} for i in range(27, 40)],
limit_host_ids=None,
ip_list=["0:[10.0.0.1]"],
ipv6_list=[],
key_list=[],
)
self.assertEqual(res[0]["ip"], "10.0.0.1")
res = HostHandler.check(
scope_list=[{"scope_type": "biz", "scope_id": f"{i}", "bk_biz_id": i} for i in range(27, 40)],
limit_host_ids=None,
ip_list=["0:10.0.0.1"],
ipv6_list=[],
key_list=[],
)
self.assertEqual(res[0]["ip"], "10.0.0.1")
create_host(1, bk_host_id=1001, ip="10.0.0.2", bk_cloud_id=0)
models.Host.objects.filter(bk_host_id=1001).update(inner_ipv6="0000:0000:0000:0000:0000:ffff:0a00:0002")
res = HostHandler.check(
scope_list=[{"scope_type": "biz", "scope_id": f"{i}", "bk_biz_id": i} for i in range(27, 40)],
limit_host_ids=None,
ip_list=[],
ipv6_list=["0000:0000:0000:0000:0000:ffff:0a00:0002"],
key_list=[],
)
self.assertEqual(res[0]["ipv6"], "0000:0000:0000:0000:0000:ffff:0a00:0002")
res = HostHandler.check(
scope_list=[{"scope_type": "biz", "scope_id": f"{i}", "bk_biz_id": i} for i in range(27, 40)],
limit_host_ids=None,
ip_list=[],
ipv6_list=["0:[0000:0000:0000:0000:0000:ffff:0a00:0002]"],
key_list=[],
)
self.assertEqual(res[0]["ipv6"], "0000:0000:0000:0000:0000:ffff:0a00:0002")
51 changes: 50 additions & 1 deletion apps/node_man/handlers/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from collections import defaultdict
from typing import Any, Dict, Iterable, List, Set

from django.db.models import Count, Q
from django.db.models import CharField, Count, Q, QuerySet, Value
from django.db.models.functions import Concat
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _

Expand Down Expand Up @@ -143,6 +144,10 @@ def list(self, params: dict, username: str):
# 计算总数
hosts_status_count = hosts_status_sql.count()

if params.get("cloud_id_ip", None):
result = self.export_all_cloud_area_colon_ip(params["cloud_id_ip"], hosts_status_sql)
return {"total": len(result), "list": result}

if params["only_ip"] is False:
host_fields = core_ipchooser_constants.CommonEnum.DEFAULT_HOST_FIELDS.value + [
"bk_addressing",
Expand Down Expand Up @@ -559,3 +564,47 @@ def get_host_infos_gby_ip_key(ips: Iterable[str], ip_version: int):
ip_key: str = f"{host_info['bk_addressing']}:{host_info['bk_cloud_id']}:{host_info[ip_filed_name]}"
host_infos_gby_ip_key[ip_key].append(host_info)
return host_infos_gby_ip_key

@staticmethod
def export_all_cloud_area_colon_ip(cloud_id_ip_type: Dict, hosts_status_sql: QuerySet) -> List:
"""
获取管控区域+IP的组合
:param cloud_id_ip_type:云区域+IP参数类型
:param hosts_status_sql:
:return:
"""
if cloud_id_ip_type.get("ipv4", None):
ipv4_exists_queryset: QuerySet = hosts_status_sql.exclude(inner_ip="")
result = list(
filter(
None,
ipv4_exists_queryset.all()
.annotate(res=Concat("bk_cloud_id", Value(":"), "inner_ip", output_field=CharField()))
.values_list("res", flat=True),
)
)
elif cloud_id_ip_type.get("ipv6", None):
ipv6_exists_queryset: QuerySet = hosts_status_sql.exclude(inner_ipv6="")
result = list(
filter(
None,
ipv6_exists_queryset.all()
.annotate(
res=Concat("bk_cloud_id", Value(":["), "inner_ipv6", Value("]"), output_field=CharField())
)
.values_list("res", flat=True),
)
)
elif cloud_id_ip_type.get("ipv4_with_brackets", None):
ipv4_exists_queryset: QuerySet = hosts_status_sql.exclude(inner_ip="")
result = list(
filter(
None,
ipv4_exists_queryset.all()
.annotate(res=Concat("bk_cloud_id", Value(":["), "inner_ip", Value("]"), output_field=CharField()))
.values_list("res", flat=True),
)
)
else:
result = []
return result
5 changes: 5 additions & 0 deletions apps/node_man/handlers/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from apps.node_man import tools
from apps.node_man.constants import IamActionType
from apps.node_man.handlers.cmdb import CmdbHandler
from apps.node_man.handlers.host import HostHandler
from apps.node_man.handlers.validator import operate_validator
from apps.node_man.models import (
AccessPoint,
Expand Down Expand Up @@ -215,6 +216,10 @@ def list(params: Dict[str, Any]):
# 非法查询返回空列表
return {"total": 0, "list": []}

if params.get("cloud_id_ip", None):
result = HostHandler.export_all_cloud_area_colon_ip(params["cloud_id_ip"], hosts_status_sql)
return {"total": len(result), "list": result}

if params.get("simple"):
host_simples = list(hosts_status_sql[begin:end].values("bk_host_id", "bk_biz_id"))
return {"total": len(host_simples), "list": host_simples}
Expand Down
28 changes: 22 additions & 6 deletions apps/node_man/serializers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@

# 放在后台会导致循坏导入
class SubScopeInstSelectorSerializer(serializers.Serializer):
instance_selector = serializers.ListField(
child=serializers.DictField(),
required=False,
label="实例筛选器"
)
instance_selector = serializers.ListField(child=serializers.DictField(), required=False, label="实例筛选器")


# 安装插件配置
Expand Down Expand Up @@ -100,10 +96,30 @@ class PaginationSerializer(serializers.Serializer):

class HostFieldSelectorSerializer(serializers.Serializer):
only_ip = serializers.BooleanField(label=_("只返回IP"), required=False, default=False)
cloud_id_ip = serializers.DictField(label=_("只返回管控区域:IP"), required=False, default={})
return_field = serializers.ChoiceField(
label=_("仅返回的字段"), required=False, default="inner_ip", choices=["inner_ip", "inner_ipv6"]
label=_("仅返回的字段"),
required=False,
default="inner_ip",
choices=[
"inner_ip",
"inner_ipv6",
"cloud_ipv4",
"cloud_ipv4_with_brackets",
"cloud_ipv6_with_brackets",
],
)

def validate_cloud_id_ip(self, value):
if not isinstance(value, dict):
raise serializers.ValidationError("传入的值必须是字典类型")

for key, val in value.items():
if not isinstance(key, str) or not isinstance(val, bool):
raise serializers.ValidationError("字典中的键必须是字符串类型,值必须是布尔类型")

return value


class HostSearchSerializer(PaginationSerializer):
bk_biz_id = serializers.ListField(label=_("业务ID"), required=False, child=serializers.IntegerField())
Expand Down
48 changes: 48 additions & 0 deletions apps/node_man/tests/test_handlers/test_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,3 +611,51 @@ def test_wrong_case_about_bt_node_detection(self):
self.assertRegex(host_ip["inner_ip"], IP_REG)
self.assertEqual(len(hosts["list"]), 0)
self.assertLessEqual(len(hosts["list"]), page_size)

@patch("apps.node_man.handlers.cmdb.CmdbHandler.cmdb_or_cache_biz", cmdb_or_cache_biz)
@patch("apps.node_man.handlers.cmdb.client_v2", MockClient)
def test_export_cloud_area_colon_ip(self):
number = 10
create_host(number)
params = {
"pagesize": -1,
"only_ip": False,
"return_field": "cloud_ipv4",
"conditions": [],
"cloud_id_ip": {"ipv4": True},
}
res = HostHandler().list(params, "admin")
self.assertLessEqual(len(res["list"]), 10)

params = {
"pagesize": -1,
"only_ip": False,
"return_field": "cloud_ipv4_with_brackets",
"conditions": [],
"cloud_id_ip": {"ipv4_with_brackets": True},
}
res = HostHandler().list(params, "admin")
self.assertLessEqual(len(res["list"]), 10)

create_host(1, bk_host_id=43420, ip="10.0.0.3")
Host.objects.filter(bk_host_id=43420).update(inner_ipv6="0000:0000:0000:0000:0000:ffff:0a00:0003")
params = {
"pagesize": -1,
"only_ip": False,
"return_field": "cloud_ipv6_with_brackets",
"conditions": [],
"cloud_id_ip": {"ipv6": True},
}
res = HostHandler().list(params, "admin")
self.assertLessEqual(len(res["list"]), 1)

# 验证参数为False的情况
params = {
"pagesize": -1,
"only_ip": False,
"return_field": "cloud_ipv4",
"conditions": [],
"cloud_id_ip": {"ipv4": False},
}
res = HostHandler().list(params, "admin")
self.assertEqual(len(res["list"]), 0)
48 changes: 48 additions & 0 deletions apps/node_man/tests/test_handlers/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,3 +382,51 @@ def test_setup_path_using_invalid_ap(self):

for host in hosts["list"]:
self.assertIn(host["setup_path"], ["/usr/local/gse", "c:\\gse"])

@patch("apps.node_man.handlers.cmdb.CmdbHandler.cmdb_or_cache_biz", cmdb_or_cache_biz)
@patch("apps.node_man.handlers.cmdb.client_v2", MockClient)
def test_export_cloud_area_colon_ip(self):
number = 10
create_host(number)
params = {
"pagesize": -1,
"only_ip": False,
"return_field": "cloud_ipv4",
"conditions": [],
"cloud_id_ip": {"ipv4": True},
}
res = PluginHandler.list(params)
self.assertLessEqual(len(res["list"]), 10)

params = {
"pagesize": -1,
"only_ip": False,
"return_field": "cloud_ipv4_with_brackets",
"conditions": [],
"cloud_id_ip": {"ipv4_with_brackets": True},
}
res = PluginHandler.list(params)
self.assertLessEqual(len(res["list"]), 10)

create_host(1, bk_host_id=43420, ip="10.0.0.4")
Host.objects.filter(bk_host_id=43420).update(inner_ipv6="0000:0000:0000:0000:0000:ffff:0a00:0004")
params = {
"pagesize": -1,
"only_ip": False,
"return_field": "cloud_ipv6_with_brackets",
"conditions": [],
"cloud_id_ip": {"ipv6": True},
}
res = PluginHandler.list(params)
self.assertLessEqual(len(res["list"]), 1)

# 验证参数为False的情况
params = {
"pagesize": -1,
"only_ip": False,
"return_field": "cloud_ipv4",
"conditions": [],
"cloud_id_ip": {"ipv4": False},
}
res = PluginHandler.list(params)
self.assertEqual(len(res["list"]), 0)

0 comments on commit 802cecd

Please sign in to comment.