Skip to content

Commit

Permalink
feature: 订阅支持按操作系统等主机属性进行范围筛(closed TencentBlueKing#1452)
Browse files Browse the repository at this point in the history
  • Loading branch information
neko12583 committed Aug 4, 2023
1 parent 39d0805 commit 398a74a
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 8 deletions.
7 changes: 4 additions & 3 deletions apps/backend/subscription/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from apps.node_man import constants, models, tools
from apps.node_man.models import ProcessStatus
from apps.node_man.serializers import policy
from apps.node_man.serializers.base import SubScopeInstSelectorSerializer
from apps.utils import basic


Expand All @@ -27,7 +28,7 @@ class GatewaySerializer(serializers.Serializer):
bk_app_code = serializers.CharField()


class ScopeSerializer(serializers.Serializer):
class ScopeSerializer(SubScopeInstSelectorSerializer):
bk_biz_id = serializers.IntegerField(required=False, default=None)
# TODO: 是否取消掉这个范围内的scope
bk_biz_scope = serializers.ListField(required=False)
Expand Down Expand Up @@ -124,7 +125,7 @@ class GetSubscriptionSerializer(GatewaySerializer):


class UpdateSubscriptionSerializer(GatewaySerializer):
class UpdateScopeSerializer(serializers.Serializer):
class UpdateScopeSerializer(SubScopeInstSelectorSerializer):
node_type = serializers.ChoiceField(choices=models.Subscription.NODE_TYPE_CHOICES)
nodes = serializers.ListField()
bk_biz_id = serializers.IntegerField(required=False, default=None)
Expand Down Expand Up @@ -157,7 +158,7 @@ class SwitchSubscriptionSerializer(GatewaySerializer):


class RunSubscriptionSerializer(GatewaySerializer):
class RunScopeSerializer(serializers.Serializer):
class RunScopeSerializer(SubScopeInstSelectorSerializer):
node_type = serializers.ChoiceField(choices=models.Subscription.NODE_TYPE_CHOICES, label="节点类型")
nodes = serializers.ListField(child=serializers.DictField(), label="拓扑节点列表")

Expand Down
41 changes: 37 additions & 4 deletions apps/backend/subscription/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from apps.utils.batch_request import batch_request, request_multi_thread
from apps.utils.cache import func_cache_decorator
from apps.utils.time_handler import strftime_local
from apps.core.ipchooser.tools.base import HostQuerySqlHelper

logger = logging.getLogger("app")

Expand Down Expand Up @@ -680,6 +681,7 @@ def wrapper(scope: Dict[str, Union[Dict, Any]], *args, **kwargs) -> Dict[str, Di
"object_type": scope["object_type"],
"node_type": scope["node_type"],
"nodes": list(nodes),
"instance_selector": scope.get("instance_selector")
},
**kwargs,
}
Expand Down Expand Up @@ -727,6 +729,11 @@ def get_instances_by_scope(scope: Dict[str, Union[Dict, int, Any]]) -> Dict[str,
"host|instance|host|yyyy": {...},
}
"""
instance_selector = scope.get("instance_selector")
# 不进行主机筛选时传入 None,传入空列表则识别为全部过滤
if instance_selector == []:
return {}

instances = []
bk_biz_id = scope["bk_biz_id"]
if bk_biz_id:
Expand Down Expand Up @@ -808,13 +815,39 @@ def get_instances_by_scope(scope: Dict[str, Union[Dict, int, Any]]) -> Dict[str,
"object_type": scope["object_type"],
"node_type": models.Subscription.NodeType.INSTANCE,
}

bk_host_ids = []

for instance in instances:
if data["object_type"] == models.Subscription.ObjectType.HOST:
data.update(instance["host"])
else:
data.update(instance["service"])
instance_data = instance[
"host"
] if data["object_type"] == models.Subscription.ObjectType.HOST else instance["service"]

data.update(instance_data)
bk_host_ids.append(instance_data.get("bk_host_id"))
instances_dict[create_node_id(data)] = instance

# 对 instances 进行二次过滤
if instance_selector and bk_host_ids:
instance_selector_host_ids = HostQuerySqlHelper.multiple_cond_sql(
params={"bk_host_id": bk_host_ids, "conditions": instance_selector},
biz_scope=[bk_biz_id],
return_all_node_type=True
).values_list("bk_host_id", flat=True)

selector_instances_dict = {}
for node_id, instance in instances_dict.items():
instance_data = instance[
"host"
] if data["object_type"] == models.Subscription.ObjectType.HOST else instance["service"]

if instance_data["bk_host_id"] in instance_selector_host_ids:
selector_instances_dict[
node_id
] = instance if data["object_type"] == models.Subscription.ObjectType.HOST else instance["service"]

return selector_instances_dict

return instances_dict


Expand Down
4 changes: 4 additions & 0 deletions apps/backend/subscription/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def create_subscription(self, request):
object_type=scope["object_type"],
node_type=scope["node_type"],
nodes=scope["nodes"],
instance_selector=scope.get("instance_selector"),
target_hosts=params.get("target_hosts"),
from_system=params["bk_app_code"] or "blueking",
enable=enable,
Expand Down Expand Up @@ -202,6 +203,9 @@ def update_subscription(self, request):
subscription.node_type = scope["node_type"]
subscription.nodes = scope["nodes"]
subscription.bk_biz_id = scope.get("bk_biz_id")
# 避免空列表误判
if scope.get("instance_selector") is not None:
subscription.instance_selector = scope["instance_selector"]
# 策略部署新增
subscription.plugin_name = params.get("plugin_name")
subscription.bk_biz_scope = params.get("bk_biz_scope")
Expand Down
69 changes: 69 additions & 0 deletions apps/backend/tests/subscription/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
CmdbClient,
list_biz_hosts_without_info_client,
)
from apps.node_man import models, constants

# 全局使用的mock
run_task = mock.patch("apps.backend.subscription.tasks.run_subscription_task").start()
Expand Down Expand Up @@ -57,6 +58,44 @@ def setUp(self):
self.get_process_by_biz_id_client.start()
self.batch_request_client.start()

models.Host.objects.create(
bk_host_id=1,
bk_biz_id=2,
bk_cloud_id=0,
inner_ip="127.0.0.1",
outer_ip=None,
login_ip="127.0.0.1",
data_ip="127.0.0.1",
os_type="WINDOWS",
node_type="AGENT",
ap_id=1,
)
models.Host.objects.create(
bk_host_id=2,
bk_biz_id=2,
bk_cloud_id=0,
inner_ip="127.0.0.2",
outer_ip=None,
login_ip="127.0.0.2",
data_ip="127.0.0.2",
os_type="LINUX",
node_type="AGENT",
ap_id=1,
)

models.ProcessStatus.objects.create(
bk_host_id=1,
name=models.ProcessStatus.GSE_AGENT_PROCESS_NAME,
proc_type=constants.ProcType.AGENT,
source_type=models.ProcessStatus.SourceType.DEFAULT,
)
models.ProcessStatus.objects.create(
bk_host_id=2,
name=models.ProcessStatus.GSE_AGENT_PROCESS_NAME,
proc_type=constants.ProcType.AGENT,
source_type=models.ProcessStatus.SourceType.DEFAULT,
)

def tearDown(self):
self.tools_client.stop()
self.commons_client.stop()
Expand Down Expand Up @@ -155,3 +194,33 @@ def test_get_service_instance_scope(self):
instance = instances[instance_id]
self.assertEqual(instance["service"]["id"], 10)
self.assertSetEqual({"process", "scope", "host", "service"}, set(instance.keys()))

def test_get_instance_selector_scope(self):
instances = get_instances_by_scope(
{
"bk_biz_id": 2,
"object_type": "HOST",
"node_type": "INSTANCE",
"instance_selector": [{"os_type": ["WINDOWS"]}],
"nodes": [
{"ip": "127.0.0.1", "bk_cloud_id": 0, "bk_supplier_id": 0},
{"ip": "127.0.0.2", "bk_cloud_id": 0, "bk_supplier_id": 0},
],
}
)
self.assertEqual(len(list(instances.keys())), 1)
self.assertIn("host|instance|host|1", instances)

def test_get_empty_list_instance_selector_scope(self):
instances = get_instances_by_scope(
{
"bk_biz_id": 2,
"object_type": "HOST",
"node_type": "INSTANCE",
"instance_selector": [],
"nodes": [
{"ip": "127.0.0.1", "bk_cloud_id": 0, "bk_supplier_id": 0},
],
}
)
self.assertEqual(len(list(instances.keys())), 0)
1 change: 1 addition & 0 deletions apps/node_man/handlers/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ def migrate_preview(cls, query_params: Dict[str, Any]) -> List[Dict[str, Any]]:
object_type=scope["object_type"],
node_type=scope["node_type"],
nodes=scope["nodes"],
instance_selector=scope.get("instance_selector"),
target_hosts=query_params.get("target_hosts"),
# SaaS侧均为主程序部署
is_main=True,
Expand Down
2 changes: 2 additions & 0 deletions apps/node_man/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1765,6 +1765,7 @@ class CategoryType(object):
object_type = models.CharField(_("对象类型"), max_length=20, choices=OBJECT_TYPE_CHOICES, db_index=True)
node_type = models.CharField(_("节点类型"), max_length=20, choices=NODE_TYPE_CHOICES, db_index=True)
nodes = JSONField(_("节点"), default=list)
instance_selector = JSONField(_("订阅任务范围主机属性筛选"), null=True, blank=True)
target_hosts = JSONField(_("下发的目标机器"), default=None, null=True)
from_system = models.CharField(_("所属系统"), max_length=30)
update_time = models.DateTimeField(_("更新时间"), auto_now=True, db_index=True)
Expand Down Expand Up @@ -1808,6 +1809,7 @@ def scope(self):
"node_type": self.node_type,
"nodes": self.nodes,
"need_register": need_register,
"instance_selector": self.instance_selector,
}

@classmethod
Expand Down
11 changes: 10 additions & 1 deletion apps/node_man/serializers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
from apps.node_man import constants, exceptions, models


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


# 安装插件配置
class StepSerializer(serializers.Serializer):
class SettingSerializer(serializers.Serializer):
Expand Down Expand Up @@ -46,7 +55,7 @@ def validate(self, data):


# 策略范围
class ScopeSerializer(serializers.Serializer):
class ScopeSerializer(SubScopeInstSelectorSerializer):
class NodeSerializer(serializers.Serializer):
bk_biz_id = serializers.IntegerField(label="业务ID")
bk_inst_id = serializers.IntegerField(required=False, label="实例ID")
Expand Down

0 comments on commit 398a74a

Please sign in to comment.