Skip to content

Commit

Permalink
feature: 订阅支持按操作系统等主机属性进行范围筛(closed #1452)
Browse files Browse the repository at this point in the history
  • Loading branch information
neko12583 committed Jun 26, 2023
1 parent a64bb17 commit ef894d8
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 7 deletions.
10 changes: 9 additions & 1 deletion apps/backend/subscription/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@
from apps.utils import basic


class SubscriptionScopeInstanceSelectorSerializer(serializers.Serializer):
instance_selector = serializers.ListField(
child=serializers.DictField(),
required=False,
label="实例筛选器"
)


class GatewaySerializer(serializers.Serializer):
bk_username = serializers.CharField()
bk_app_code = serializers.CharField()


class ScopeSerializer(serializers.Serializer):
class ScopeSerializer(SubscriptionScopeInstanceSelectorSerializer):
bk_biz_id = serializers.IntegerField(required=False, default=None)
# TODO: 是否取消掉这个范围内的scope
bk_biz_scope = serializers.ListField(required=False)
Expand Down
46 changes: 42 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,9 @@ def get_instances_by_scope(scope: Dict[str, Union[Dict, int, Any]]) -> Dict[str,
"host|instance|host|yyyy": {...},
}
"""
if scope.get("instance_selector") == []:
return {}

instances = []
bk_biz_id = scope["bk_biz_id"]
if bk_biz_id:
Expand Down Expand Up @@ -808,13 +813,46 @@ 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_id_list = []

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_id_list.append(instance_data.get("bk_host_id"))
instances_dict[create_node_id(data)] = instance

# 对 instances 进行二次过滤
if scope.get("instance_selector") and bk_host_id_list:
conditions = [
{
"key": key, "value": value
} for selector_param in scope["instance_selector"] for key, value in selector_param.items()
]

host_queryset = HostQuerySqlHelper.multiple_cond_sql(
params={"bk_host_id": bk_host_id_list, "conditions": conditions},
biz_scope=[bk_biz_id],
return_all_node_type=True
)
instance_selector_host_id_set = {host.bk_host_id for host in host_queryset}

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_id_set:
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
1 change: 1 addition & 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
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)
3 changes: 2 additions & 1 deletion 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 Expand Up @@ -467,7 +468,7 @@ def migrate_preview(cls, query_params: Dict[str, Any]) -> List[Dict[str, Any]]:
step_obj.subscription = subscription
subscription.steps = step_objs
preview_result = tasks.run_subscription_task_and_create_instance(
subscription, subscription_task, preview_only=True
subscription, subscription_task, scope, preview_only=True
)
action_instance_map = defaultdict(list)
instance_actions = preview_result["instance_actions"]
Expand Down
28 changes: 28 additions & 0 deletions apps/node_man/migrations/0070_auto_20230625_1733.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.2.4 on 2023-06-25 09:33

from django.db import migrations, models
import django_mysql.models


class Migration(migrations.Migration):

dependencies = [
('node_man', '0069_merge_20230425_1727'),
]

operations = [
migrations.AlterModelOptions(
name='cloud',
options={'verbose_name': '管控区域(BK-Net)', 'verbose_name_plural': '管控区域(BK-Net)'},
),
migrations.AddField(
model_name='subscription',
name='instance_selector',
field=django_mysql.models.JSONField(blank=True, default=dict, null=True, verbose_name='订阅任务范围主机属性筛选'),
),
migrations.AlterField(
model_name='job',
name='job_type',
field=models.CharField(choices=[('INSTALL_AGENT', 'INSTALL_AGENT'), ('RESTART_AGENT', 'RESTART_AGENT'), ('REINSTALL_AGENT', 'REINSTALL_AGENT'), ('UNINSTALL_AGENT', 'UNINSTALL_AGENT'), ('REMOVE_AGENT', 'REMOVE_AGENT'), ('UPGRADE_AGENT', 'UPGRADE_AGENT'), ('IMPORT_AGENT', 'IMPORT_AGENT'), ('RESTART_AGENT', 'RESTART_AGENT'), ('RELOAD_AGENT', 'RELOAD_AGENT'), ('ACTIVATE_AGENT', 'ACTIVATE_AGENT'), ('MAIN_START_PLUGIN', 'MAIN_START_PLUGIN'), ('MAIN_STOP_PLUGIN', 'MAIN_STOP_PLUGIN'), ('MAIN_RESTART_PLUGIN', 'MAIN_RESTART_PLUGIN'), ('MAIN_RELOAD_PLUGIN', 'MAIN_RELOAD_PLUGIN'), ('MAIN_DELEGATE_PLUGIN', 'MAIN_DELEGATE_PLUGIN'), ('MAIN_UNDELEGATE_PLUGIN', 'MAIN_UNDELEGATE_PLUGIN'), ('MAIN_INSTALL_PLUGIN', 'MAIN_INSTALL_PLUGIN'), ('MAIN_STOP_AND_DELETE_PLUGIN', 'MAIN_STOP_AND_DELETE_PLUGIN'), ('DEBUG_PLUGIN', 'DEBUG_PLUGIN'), ('STOP_DEBUG_PLUGIN', 'STOP_DEBUG_PLUGIN'), ('PUSH_CONFIG_PLUGIN', 'PUSH_CONFIG_PLUGIN'), ('REMOVE_CONFIG_PLUGIN', 'REMOVE_CONFIG_PLUGIN'), ('PACKING_PLUGIN', 'PACKING_PLUGIN'), ('INSTALL_PROXY', 'INSTALL_PROXY'), ('RESTART_PROXY', 'RESTART_PROXY'), ('REINSTALL_PROXY', 'REINSTALL_PROXY'), ('REPLACE_PROXY', 'REPLACE_PROXY'), ('UNINSTALL_PROXY', 'UNINSTALL_PROXY'), ('UPGRADE_PROXY', 'UPGRADE_PROXY'), ('IMPORT_PROXY', 'IMPORT_PROXY'), ('RESTART_PROXY', 'RESTART_PROXY'), ('RELOAD_PROXY', 'RELOAD_PROXY')], default='INSTALL_PROXY', max_length=45, verbose_name='作业类型'),
),
]
2 changes: 2 additions & 0 deletions apps/node_man/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1891,6 +1891,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 @@ -1934,6 +1935,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 @@ -45,8 +45,17 @@ def validate(self, data):
return data


# 订阅任务范围主机属性筛选
class SubscriptionScopeInstanceSelectorSerializer(serializers.Serializer):
instance_selector = serializers.ListField(
child=serializers.DictField(),
required=False,
label="实例筛选器"
)


# 策略范围
class ScopeSerializer(serializers.Serializer):
class ScopeSerializer(SubscriptionScopeInstanceSelectorSerializer):
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 ef894d8

Please sign in to comment.