diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e599933bae..196ed9ed72 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: hooks: - id: check-merge-conflict - repo: https://github.com/psf/black - rev: stable + rev: 22.8.0 hooks: - id: black language_version: python3.6 diff --git a/api/collections/nodeman.py b/api/collections/nodeman.py index fb73483105..b99c01b3a6 100644 --- a/api/collections/nodeman.py +++ b/api/collections/nodeman.py @@ -122,3 +122,6 @@ def get_rsa_public_key(self, executor): "names": ["DEFAULT"], }, ) + + def install_channel(self): + return self._request(method="get", url=_get_nodeman_api_v2("install_channel"), data={}) diff --git a/pipeline_plugins/components/collections/sites/open/nodeman/create_task/v4_0.py b/pipeline_plugins/components/collections/sites/open/nodeman/create_task/v4_0.py index 6416ae60d0..3354932c53 100644 --- a/pipeline_plugins/components/collections/sites/open/nodeman/create_task/v4_0.py +++ b/pipeline_plugins/components/collections/sites/open/nodeman/create_task/v4_0.py @@ -88,6 +88,7 @@ def execute(self, data, parent_data): all_hosts, row_host_params_list = [], [] for host in nodeman_hosts: bk_cloud_id = host["nodeman_bk_cloud_id"] + install_channel_id = host.get("nodeman_bk_install_channel") use_inner_ip = True if host.get("inner_ip") else False # use_inner_ip 判定用户输入的的是ipv4还是ipv6 inner_ip_list = self.get_ip_list( @@ -135,7 +136,8 @@ def execute(self, data, parent_data): "os_type": host["os_type"], "is_manual": False, } - + if install_channel_id: + base_params["install_channel_id"] = install_channel_id # 支持表格中一行多ip操作, 拼装表格内的inner_ip参数 for index, inner_ip in enumerate(inner_ip_list): diff --git a/pipeline_plugins/components/collections/sites/open/nodeman/create_task/v6_0.py b/pipeline_plugins/components/collections/sites/open/nodeman/create_task/v6_0.py new file mode 100644 index 0000000000..61a91a2220 --- /dev/null +++ b/pipeline_plugins/components/collections/sites/open/nodeman/create_task/v6_0.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +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 django.utils.translation import ugettext_lazy as _ +from pipeline.component_framework.component import Component + +from gcloud.conf import settings + +__group_name__ = _("节点管理(Nodeman)") + +from pipeline_plugins.components.collections.sites.open.nodeman.create_task.v4_0 import ( + NodemanCreateTaskService as NodemanCreateTaskV4Service, +) + +VERSION = "v6.0" + + +class NodemanCreateTaskService(NodemanCreateTaskV4Service): + pass + + +class NodemanCreateTaskComponent(Component): + name = _("新建任务") + code = "nodeman_create_task" + bound_service = NodemanCreateTaskService + form = "%scomponents/atoms/nodeman/create_task/v6_0.js" % settings.STATIC_URL + version = VERSION + desc = _("v6.0版本 支持选择安装通道 \n" "注意:bk_apigateway版本>=1.12.17") diff --git a/pipeline_plugins/components/query/sites/open/nodeman.py b/pipeline_plugins/components/query/sites/open/nodeman.py index 51394cc4a3..383a585ebf 100644 --- a/pipeline_plugins/components/query/sites/open/nodeman.py +++ b/pipeline_plugins/components/query/sites/open/nodeman.py @@ -13,9 +13,9 @@ import logging import os +from django.conf.urls import url from django.http import JsonResponse from django.utils.translation import ugettext_lazy as _ -from django.conf.urls import url from api.collections.nodeman import BKNodeManClient from gcloud.iam_auth.utils import check_and_raise_raw_auth_fail_exception @@ -112,10 +112,35 @@ def nodeman_is_support_tjj(request): return JsonResponse({"result": True, "message": "success", "code": 0, "data": BKAPP_NODEMAN_SUPPORT_TJJ}) +def nodeman_get_install_channel(request, cloud_id: int): + client = BKNodeManClient(username=request.user.username) + install_channel_result = client.install_channel() + + if not install_channel_result["result"]: + message = handle_api_error(_("节点管理(NODEMAN)"), "nodeman.install_channel", {}, install_channel_result) + logger.error(message) + check_and_raise_raw_auth_fail_exception(install_channel_result, message) + return JsonResponse( + { + "result": install_channel_result["result"], + "code": install_channel_result.get("code", "-1"), + "message": message, + } + ) + + data = install_channel_result["data"] + result = [ + {"text": channel["name"], "value": channel["id"]} for channel in data if channel["bk_cloud_id"] == int(cloud_id) + ] + install_channel_result["data"] = result + return JsonResponse(install_channel_result) + + nodeman_urlpatterns = [ url(r"^nodeman_get_cloud_area/$", nodeman_get_cloud_area), url(r"^nodeman_get_ap_list/$", nodeman_get_ap_list), url(r"^nodeman_is_support_tjj/$", nodeman_is_support_tjj), url(r"^nodeman_get_plugin_list/(?P\w+)/$", nodeman_get_plugin_list), url(r"^nodeman_get_plugin_version/(?P\w+)/(?P\w+)/$", nodeman_get_plugin_version), + url(r"^nodeman_get_install_channel/(?P\w+)/$", nodeman_get_install_channel), ] diff --git a/pipeline_plugins/components/static/components/atoms/nodeman/create_task/v6_0.js b/pipeline_plugins/components/static/components/atoms/nodeman/create_task/v6_0.js new file mode 100644 index 0000000000..165acdf478 --- /dev/null +++ b/pipeline_plugins/components/static/components/atoms/nodeman/create_task/v6_0.js @@ -0,0 +1,656 @@ +/** + * Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community + * Edition) available. + * Copyright (C) 2017-2020 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * 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. + */ + +(function () { + function is_display_tag(self, op_type, value) { + if (op_type.indexOf(value) !== -1) { + self.show(); + } else { + self.hide(); + } + } + + function is_install_op(self, value) { + is_display_tag(self, ["INSTALL", "REINSTALL"], value); + } + + + function init_columns(self, node_type, op_type) { + let common_columns = [ + { + tag_code: "nodeman_bk_cloud_id", + type: "select", + attrs: { + name: gettext("管控区域ID"), + width: "180px", + remote: true, + items: [], + remote_url: $.context.get("site_url") + "pipeline/nodeman_get_cloud_area/", + remote_data_init: function (resp) { + if (resp.result === false) { + show_msg(resp.message, "error"); + } + resp.data.unshift({"text": gettext("直连区域"), "value": 0}); + return resp.data; + }, + validation: [ + { + type: "required", + } + ] + }, + }, + { + tag_code: "nodeman_bk_install_channel", + type: "select", + attrs: { + name: gettext("安装通道"), + hookable: false, + remote: true, + remote_url: "", + remote_data_init: function (resp) { + if (resp.result === false) { + show_msg(resp.message, 'error'); + } + if (resp.data.length != 0){ + this._set_value(resp.data[0]["value"]); + } + return resp.data; + }, + validation: [ + { + type: "required" + } + ] + }, + events: [ + { + source: "nodeman_bk_cloud_id", + type: "change", + action: function () { + var cloud_id = this.get_parent().get_child("nodeman_bk_cloud_id").value; + if (cloud_id !== '') { + this.remote_url = $.context.get('site_url') + 'pipeline/nodeman_get_install_channel/' + cloud_id + '/'; + this.remoteMethod(); + } + } + }, + ] + }, + { + tag_code: "nodeman_ap_id", + type: "select", + attrs: { + name: gettext("接入点"), + width: "180px", + remote: true, + items: [], + remote_url: $.context.get("site_url") + "pipeline/nodeman_get_ap_list/", + remote_data_init: function (resp) { + if (resp.result === false) { + show_msg(resp.message, "error"); + } + return resp.data; + }, + validation: [] + } + }, + { + tag_code: "inner_ip", + type: "textarea", + attrs: { + name: gettext("内网IP"), + placeholder: gettext("多个用英文逗号 `,` 或换行分隔"), + width: "180px", + editable: true + } + }, + { + tag_code: "inner_ipv6", + type: "textarea", + attrs: { + name: gettext("内网IP(IPV6)"), + placeholder: gettext("可为空,如纯ipv6主机,内网ipv和外网IP(IPV6)两个必须填一个"), + width: "180px", + editable: true, + } + }, + { + tag_code: "os_type", + type: "select", + attrs: { + name: gettext("操作系统类型"), + width: "180px", + items: [ + {value: "LINUX", text: gettext("LINUX")}, + {value: "WINDOWS", text: gettext("WINDOWS")}, + {value: "AIX", text: gettext("AIX")} + ], + default: "LINUX", + validation: [ + { + type: "required" + } + ], + } + }, + ]; + + let proxy_columns = [ + { + tag_code: "outer_ip", + type: "textarea", + attrs: { + name: gettext("外网IP"), + placeholder: gettext("可选,如填写需与内网ip一一对应,多个用英文逗号 `,` 或换行分隔"), + width: "180px", + editable: true, + validation: [] + }, + }, + { + tag_code: "data_ip", + type: "textarea", + attrs: { + name: gettext("数据IP"), + placeholder: gettext("可为空,如填写需与内网ip一一对应,适配复杂网络时填写,多个用英文逗号 `,` 或换行分隔"), + width: "180px", + editable: true, + } + }, + { + tag_code: "outer_ipv6", + type: "textarea", + attrs: { + name: gettext("外网IP(IPV6)"), + placeholder: gettext("可为空,如纯ipv6主机,外网ipv和外网IP(IPV6)两个必须填一个"), + width: "180px", + editable: true, + }, + }, + ]; + + let auth_columns = [ + { + tag_code: "login_ip", + type: "textarea", + attrs: { + name: gettext("登录IP"), + placeholder: gettext("可为空,如填写需与内网ip一一对应,适配复杂网络时填写,多个用英文逗号 `,` 或换行分隔"), + width: "180px", + editable: true + } + }, + { + tag_code: "account", + type: "input", + attrs: { + name: gettext("登录账号"), + width: "180px", + editable: true, + validation: [ + { + type: "required" + } + ], + } + }, + { + tag_code: "port", + type: "input", + attrs: { + name: gettext("端口号"), + width: "100px", + editable: true, + validation: [ + { + type: "required" + } + ], + } + }, + { + tag_code: "auth_type", + type: "select", + attrs: { + name: gettext("认证方式"), + width: "180px", + items: [ + {value: "PASSWORD", text: gettext("PASSWORD")}, + {value: "KEY", text: gettext("KEY")}, + {value: "TJJ_PASSWORD", text: gettext("TJJ")} + + ], + default: "PASSWORD", + validation: [ + { + type: "required" + } + ], + } + }, + { + tag_code: "auth_key", + type: "password", + attrs: { + name: gettext("认证密钥"), + width: "400px", + editable: true, + textareaMode: true, + validation: [ + { + type: "custom", + args: function (value, parent_value) { + let auth_type = parent_value.auth_type; + let result = { + result: true, + error_message: "" + }; + if (auth_type !== "TJJ_PASSWORD" && !value.value.length) { + result.result = false; + result.error_message = gettext("请输入认证密钥"); + } + return result; + } + } + ] + }, + }, + ]; + + let config_columns = [ + { + tag_code: "peer_exchange_switch_for_agent", + type: "radio", + attrs: { + name: gettext("BT节点探测"), + items: [ + {value: 1, name: gettext("是")}, + {value: 0, name: gettext("否")} + ], + default: 0, + validation: [ + { + type: "required" + }, + ] + }, + }, + { + tag_code: "speed_limit", + type: "input", + attrs: { + name: gettext("传输限速 M/s"), + width: "100px", + placeholder: gettext("请输入"), + validation: [ + { + type: "custom", + args: function (value) { + var result = { + result: true, + error_message: "" + }; + if (value && !Number(value)) { + result.result = false; + result.error_message = gettext("请输入数字"); + } + return result; + } + } + ] + }, + }, + ]; + + self.columns = common_columns; + + // 如果是 Proxy,补充 Proxy 信息 + if (node_type === "PROXY") { + self.columns.push(...proxy_columns); + } + // 安装 / 重装 / 卸载需要认证信息 + if (op_type === "INSTALL" || op_type === "UNINSTALL" || op_type === "REINSTALL") { + self.columns.push(...auth_columns); + } + // 非卸载场景需要配置信息 + if (op_type !== "UNINSTALL") { + self.columns.push(...config_columns); + } + }; + + let NODEMAN_TJJ_IS_HIDDEN = true; + + $.ajax({ + url: $.context.get('site_url') + 'pipeline/nodeman_is_support_tjj/', + type: 'GET', + dataType: 'json', + async: false, + success: function (resp) { + if (resp.data) { + NODEMAN_TJJ_IS_HIDDEN = false; + } + }, + error: function () { + show_msg('request nodeman is support tjj error', 'error'); + } + }); + $.atoms.nodeman_create_task = [ + { + tag_code: "bk_biz_id", + type: "select", + attrs: { + name: gettext("业务"), + allowCreate: true, + hookable: true, + remote: true, + remote_url: $.context.get("site_url") + "pipeline/cc_get_business_list/", + remote_data_init: function (resp) { + if (resp.result === false) { + show_msg(resp.message, 'error'); + } + return resp.data; + }, + disabled: !$.context.canSelectBiz(), + validation: [ + { + type: "required" + } + ] + }, + methods: { + _tag_init: function () { + if (this.value) { + return + } + this._set_value($.context.getBkBizId()); + } + } + }, + { + tag_code: "nodeman_op_info", + type: "combine", + attrs: { + name: gettext("操作类型"), + hookable: true, + children: [ + { + tag_code: "nodeman_node_type", + type: "radio", + attrs: { + name: gettext("节点类型"), + hookable: true, + items: [ + {value: "AGENT", name: gettext("AGENT")}, + {value: "PROXY", name: gettext("PROXY")}, + ], + default: "AGENT", + validation: [ + { + type: "required" + } + ], + events: [ + { + source: "nodeman_node_type", + type: "init", + action: function (value) { + // 统一以 change 事件抛出 + this.emit_event(this.tagCode, "change", this.value); + } + }, + ] + } + }, + { + tag_code: "nodeman_op_type", + type: "select", + attrs: { + name: gettext("操作类型"), + items: [ + {value: "INSTALL", text: gettext("安裝")}, + {value: "REINSTALL", text: gettext("重新安装")}, + {value: "UNINSTALL", text: gettext("卸载")}, + {value: "UPGRADE", text: gettext("升级")}, + {value: "RESTART", text: gettext("重启")}, + {value: "RELOAD", text: gettext("配置重载")}, + ], + default: "INSTALL", + validation: [ + { + type: "required" + } + ] + }, + events: [ + { + source: "nodeman_op_type", + type: "init", + action: function (value) { + this.emit_event(this.tagCode, "change", this.value); + } + }, + ] + }, + { + tag_code: "nodeman_install_latest_plugins", + type: "radio", + attrs: { + name: gettext("是否安装最新版本插件"), + items: [ + {value: true, name: gettext("是")}, + {value: false, name: gettext("否")} + ], + default: true, + validation: [ + { + type: "required" + }, + ] + }, + events: [ + { + source: "nodeman_op_type", + type: "change", + action: function (value) { + is_install_op(this, value); + } + }, + ] + }, + { + tag_code: "nodeman_hosts", + type: "datatable", + attrs: { + pagination: true, + name: gettext("主机"), + table_buttons: [ + { + type: "add_row", + text: gettext("添加"), + callback: function () { + this.add_row(); + } + }, + { + type: "import", + text: gettext("导入") + }, + { + type: "export", + text: gettext("导出"), + callback: function () { + this.export2Excel(); + } + }, + + ], + columns: [], + hookable: true, + validation: [ + { + type: "custom", + args: function (value) { + let self = this; + let result = { + result: true, + error_message: "" + }; + let op_type = self.get_parent && self.get_parent().get_child("nodeman_op_type").value; + let install_type = ["INSTALL", "REINSTALL"]; + if (install_type.indexOf(op_type) !== -1 && value === "") { + result.result = false; + result.error_message = gettext("请完善主机信息"); + } + return result; + + } + } + ] + }, + events: [ + { + source: "nodeman_op_type", + type: "change", + action: function (value) { + let node_type = this.get_parent().get_child("nodeman_node_type").value; + init_columns(this, node_type, value); + let op_type = ["INSTALL", "REINSTALL", "RELOAD"]; + if (node_type === "AGENT") { + op_type = ["INSTALL", "REINSTALL", "UNINSTALL", "RELOAD"]; + } + is_display_tag(this, op_type, value); + } + }, + { + source: "nodeman_node_type", + type: "change", + action: function (value) { + init_columns(this, value, this.get_parent().get_child("nodeman_op_type").value); + let op_type = ["INSTALL", "REINSTALL", "RELOAD"]; + if (value === "AGENT") { + op_type = ["INSTALL", "REINSTALL", "UNINSTALL", "RELOAD"]; + } + is_display_tag(this, op_type, this.get_parent().get_child("nodeman_op_type").value); + } + }, + ] + }, + { + tag_code: "nodeman_other_hosts", + type: "datatable", + attrs: { + pagination: true, + name: gettext("主机"), + table_buttons: [ + { + type: "add_row", + text: gettext("添加"), + callback: function () { + this.add_row(); + } + }, + { + type: "import", + text: gettext("导入") + }, + { + type: "export", + text: gettext("导出"), + callback: function () { + this.export2Excel(); + } + }, + + ], + columns: [ + { + tag_code: "nodeman_bk_cloud_id", + type: "input", + attrs: { + name: gettext("管控区域ID"), + placeholder: gettext("一行只能对应一个管控区域ID"), + hookable: true, + validation: [ + { + type: "required" + } + ] + } + }, + { + tag_code: "nodeman_ip_str", + type: "textarea", + attrs: { + name: gettext("IP"), + placeholder: gettext("多个用英文逗号 `,` 分隔"), + editable: true, + validation: [ + { + type: "required" + } + ] + } + } + ], + hookable: true, + validation: [ + { + type: "custom", + args: function (value) { + let self = this; + let result = { + result: true, + error_message: "" + }; + let op_type = self.get_parent && self.get_parent().get_child("nodeman_op_type").value; + let install_type = ["UNINSTALL", "UPGRADE"]; + if (install_type.indexOf(op_type) !== -1 && value === "") { + result.result = false; + result.error_message = gettext("请完善主机信息"); + } + return result; + + } + } + ] + }, + events: [ + { + source: "nodeman_op_type", + type: "change", + action: function (value) { + let op_type = ["UPGRADE", "RESTART"]; + if (this.get_parent().get_child("nodeman_node_type").value === "PROXY") { + op_type = ["UPGRADE", "RESTART", "UNINSTALL"]; + } + is_display_tag(this, op_type, value); + } + }, + { + source: "nodeman_node_type", + type: "change", + action: function (value) { + let op_type = ["UPGRADE", "RESTART"]; + if (value === "PROXY") { + op_type = ["UPGRADE", "RESTART", "UNINSTALL"]; + } + is_display_tag(this, op_type, this.get_parent().get_child("nodeman_op_type").value); + } + }, + ] + }, + ], + }, + }, + ]; +})();