Skip to content

Commit

Permalink
节点管理-评审会: 主机「复制」按钮支持导出「管控区域 ID:IP」 (closed TencentBlueKing#1963)
Browse files Browse the repository at this point in the history
# Reviewed, transaction id: 3861
  • Loading branch information
Huayeaaa authored and chalice-1831 committed Jun 24, 2024
1 parent 0f2d876 commit a4a0e8a
Show file tree
Hide file tree
Showing 8 changed files with 222 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="127.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:[127.0.0.1]"],
ipv6_list=[],
key_list=[],
)
self.assertEqual(res[0]["ip"], "127.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:127.0.0.1"],
ipv6_list=[],
key_list=[],
)
self.assertEqual(res[0]["ip"], "127.0.0.1")
create_host(1, bk_host_id=1001, ip="127.0.0.2", bk_cloud_id=0)
models.Host.objects.filter(bk_host_id=1001).update(inner_ipv6="0000:0000:0000:0000:0000:ffff:7f00: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:7f00:0002"],
key_list=[],
)
self.assertEqual(res[0]["ipv6"], "0000:0000:0000:0000:0000:ffff:7f00: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:7f00:0002]"],
key_list=[],
)
self.assertEqual(res[0]["ipv6"], "0000:0000:0000:0000:0000:ffff:7f00:0002")
5 changes: 5 additions & 0 deletions apps/node_man/handlers/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
JobTask,
ProcessStatus,
)
from apps.node_man.tools.host import HostTools
from apps.utils import APIModel
from apps.utils.basic import filter_values

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"):
result = HostTools.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
5 changes: 5 additions & 0 deletions apps/node_man/handlers/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
Subscription,
SubscriptionTask,
)
from apps.node_man.tools.host import HostTools
from apps.utils import APIModel
from common.api import NodeApi

Expand Down Expand Up @@ -215,6 +216,10 @@ def list(params: Dict[str, Any]):
# 非法查询返回空列表
return {"total": 0, "list": []}

if params.get("cloud_id_ip"):
result = HostTools.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 @@ -618,3 +618,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="127.0.0.1")
Host.objects.filter(bk_host_id=43420).update(inner_ipv6="0000:0000:0000:0000:0000:ffff:7f00:0001")
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="127.0.0.1")
Host.objects.filter(bk_host_id=43420).update(inner_ipv6="0000:0000:0000:0000:0000:ffff:7f00:0001")
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)
35 changes: 34 additions & 1 deletion apps/node_man/tools/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
specific language governing permissions and limitations under the License.
"""
import base64
from typing import Type
from typing import Dict, List, Type

from bkcrypto.asymmetric.ciphers import BaseAsymmetricCipher
from django.db.models import QuerySet
from django.utils.translation import ugettext_lazy as _

from apps.core.encrypt import constants as core_encrypt_constants
Expand Down Expand Up @@ -62,3 +63,35 @@ def decrypt_with_friendly_exc_handle(
return cls.decrypt_with_friendly_exc_handle(
cipher=cipher, encrypt_message=decrypt_message, raise_exec=raise_exec
)

@staticmethod
def export_all_cloud_area_colon_ip(cloud_id_ip_type: Dict[str, bool], hosts_status_sql: QuerySet) -> List:
"""
获取管控区域+IP的组合
:param cloud_id_ip_type:云区域+IP参数类型
:param hosts_status_sql:主机查询结果集
:return:云区域+IP组合的列表
"""
cloud_id_and_inner_ip_qs: QuerySet = hosts_status_sql.values("bk_cloud_id", "inner_ip")
cloud_id_and_inner_ipv6_qs: QuerySet = hosts_status_sql.values("bk_cloud_id", "inner_ipv6")
if cloud_id_ip_type.get("ipv4", False):
result: List = [
f'{cloud_id_and_inner_ip_dict["bk_cloud_id"]}:{cloud_id_and_inner_ip_dict["inner_ip"]}'
for cloud_id_and_inner_ip_dict in cloud_id_and_inner_ip_qs
if cloud_id_and_inner_ip_dict["inner_ip"]
]
elif cloud_id_ip_type.get("ipv6", False):
result: List = [
f'{cloud_id_and_inner_ipv6_dict["bk_cloud_id"]}:[{cloud_id_and_inner_ipv6_dict["inner_ipv6"]}]'
for cloud_id_and_inner_ipv6_dict in cloud_id_and_inner_ipv6_qs
if cloud_id_and_inner_ipv6_dict["inner_ipv6"]
]
elif cloud_id_ip_type.get("ipv4_with_brackets", False):
result: List = [
f'{cloud_id_and_inner_ip_dict["bk_cloud_id"]}:[{cloud_id_and_inner_ip_dict["inner_ip"]}]'
for cloud_id_and_inner_ip_dict in cloud_id_and_inner_ip_qs
if cloud_id_and_inner_ip_dict["inner_ip"]
]
else:
result = []
return result

0 comments on commit a4a0e8a

Please sign in to comment.