Skip to content

Commit

Permalink
feature: P-Agent 安装 IPv6 场景适配 (closed #1290)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZhuoZhuoCrayon committed Nov 30, 2022
1 parent dc9a8c6 commit 6e2f059
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 51 deletions.
19 changes: 11 additions & 8 deletions apps/backend/agent/solution_maker.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,12 @@ def __init__(
)

def get_http_proxy_url(self) -> str:
jump_server: models.Host = self.gse_servers_info["jump_server"]
jump_server_lan_ip: str = jump_server.inner_ip or jump_server.inner_ipv6
if basic.is_v6(jump_server_lan_ip):
jump_server_lan_ip = f"[{jump_server_lan_ip}]"
return "http://{jump_server_lan_ip}:{jump_server_port}".format(
jump_server_lan_ip=self.gse_servers_info["jump_server"].inner_ip,
jump_server_port=settings.BK_NODEMAN_NGINX_PROXY_PASS_PORT,
jump_server_lan_ip=jump_server_lan_ip, jump_server_port=settings.BK_NODEMAN_NGINX_PROXY_PASS_PORT
)

def get_setup_type_alias(self):
Expand All @@ -155,9 +158,12 @@ def get_package_url(self) -> str:
:return:
"""
if ExecutionSolutionTools.need_jump_server(self.host):
jump_server: models.Host = self.gse_servers_info["jump_server"]
jump_server_lan_ip: str = jump_server.inner_ip or jump_server.inner_ipv6
if basic.is_v6(jump_server_lan_ip):
jump_server_lan_ip = f"[{jump_server_lan_ip}]"
return "http://{jump_server_lan_ip}:{proxy_nginx_pass_port}".format(
jump_server_lan_ip=self.gse_servers_info["jump_server"].inner_ip,
proxy_nginx_pass_port=settings.BK_NODEMAN_NGINX_DOWNLOAD_PORT,
jump_server_lan_ip=jump_server_lan_ip, proxy_nginx_pass_port=settings.BK_NODEMAN_NGINX_DOWNLOAD_PORT
)
else:
return self.gse_servers_info["package_url"]
Expand Down Expand Up @@ -710,10 +716,7 @@ def get_run_cmd_base_params(self) -> typing.List[str]:
# 代理机器配置
f"-HPP '{settings.BK_NODEMAN_NGINX_PROXY_PASS_PORT}'",
# 代理机器主机信息
f"-I {self.gse_servers_info['jump_server'].inner_ip}",
f"-I6 {self.gse_servers_info['jump_server'].inner_ipv6}"
if self.gse_servers_info["jump_server"].inner_ipv6
else "",
f"-I {self.gse_servers_info['jump_server'].inner_ip or self.gse_servers_info['jump_server'].inner_ipv6}",
]

# 通道特殊配置
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/agent/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def fetch_gse_servers_info(
bt_file_server_hosts = upstream_servers["btfileserver"]
data_server_hosts = upstream_servers["dataserver"]
task_server_hosts = upstream_servers["taskserver"]
package_url = gen_nginx_download_url(jump_server.inner_ip)
package_url = gen_nginx_download_url(jump_server.inner_ip or jump_server.inner_ipv6)
default_callback_url = (
settings.BKAPP_NODEMAN_CALLBACK_URL
if host.node_type == constants.NodeType.AGENT
Expand All @@ -111,7 +111,7 @@ def fetch_gse_servers_info(
callback_url = host_ap.outer_callback_url or settings.BKAPP_NODEMAN_OUTER_CALLBACK_URL
else:
# PAGENT的场景
proxy_ips = list(set([proxy.inner_ip for proxy in proxies]))
proxy_ips = list(set([proxy.inner_ip or proxy.inner_ipv6 for proxy in proxies]))
bt_file_server_hosts = proxy_ips
data_server_hosts = proxy_ips
task_server_hosts = proxy_ips
Expand Down
6 changes: 3 additions & 3 deletions apps/backend/components/collections/agent_new/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ def execute_job_commands(self, sub_inst_id, installation_tool: InstallationTools
)
self.log_info(
sub_inst_ids=sub_inst_id,
log_content=_("已选择 {inner_ip} 作为本次安装的跳板机").format(inner_ip=jump_server.inner_ip),
log_content=_("已选择 {inner_ip} 作为本次安装的跳板机").format(inner_ip=jump_server.inner_ip or jump_server.inner_ipv6),
)
path = os.path.join(settings.BK_SCRIPTS_PATH, constants.SetupScriptFileName.SETUP_PAGENT_PY.value)
with open(path, encoding="utf-8") as fh:
Expand Down Expand Up @@ -453,8 +453,8 @@ def execute_job_commands(self, sub_inst_id, installation_tool: InstallationTools
"2. Proxy是否已正确完成所有安装步骤且状态正常。 \n"
"3. 点击上面链接跳转到作业平台查看任务执行情况。\n"
).format(
host_inner_ip=host.inner_ip,
jump_server_ip=jump_server.inner_ip,
host_inner_ip=host.inner_ip or host.inner_ipv6,
jump_server_ip=jump_server.inner_ip or host.inner_ipv6,
download_port=settings.BK_NODEMAN_NGINX_DOWNLOAD_PORT,
proxy_pass_port=settings.BK_NODEMAN_NGINX_PROXY_PASS_PORT,
),
Expand Down
6 changes: 3 additions & 3 deletions apps/node_man/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ class TimeUnit:
GSE_SVR_DISCOVERY_INTERVAL = 1 * TimeUnit.MINUTE
COLLECT_AUTO_TRIGGER_JOB_INTERVAL = 5 * TimeUnit.MINUTE
SYNC_CMDB_CLOUD_AREA_INTERVAL = 10 * TimeUnit.SECOND
SYNC_AGENT_STATUS_TASK_INTERVAL = 30 * TimeUnit.MINUTE
SYNC_PROC_STATUS_TASK_INTERVAL = 30 * TimeUnit.MINUTE
SYNC_AGENT_STATUS_TASK_INTERVAL = 3 * TimeUnit.MINUTE
SYNC_PROC_STATUS_TASK_INTERVAL = 15 * TimeUnit.MINUTE

CLEAN_EXPIRED_INFO_INTERVAL = 6 * TimeUnit.HOUR

Expand Down Expand Up @@ -517,7 +517,7 @@ def _get_member__alias_map(cls) -> Dict[Enum, str]:

# 周期任务相关
QUERY_EXPIRED_INFO_LENS = 2000
QUERY_AGENT_STATUS_HOST_LENS = 500
QUERY_AGENT_STATUS_HOST_LENS = 2000
QUERY_PROC_STATUS_HOST_LENS = 2000
QUERY_CMDB_LIMIT = 500
WRITE_CMDB_LIMIT = 500
Expand Down
1 change: 1 addition & 0 deletions apps/node_man/handlers/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ def get_host_infos_gby_ip_key(ips: Iterable[str], ip_version: int):
fields: List[str] = [
"inner_ip",
"inner_ipv6",
"bk_agent_id",
"outer_ip",
"outer_ipv6",
"login_ip",
Expand Down
49 changes: 28 additions & 21 deletions apps/node_man/handlers/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from django.db.models.aggregates import Count
from django.utils.translation import ugettext_lazy as _

from apps.adapters.api.gse import GseApiHelper
from apps.node_man import constants as const
from apps.node_man import tools
from apps.node_man.exceptions import (
Expand Down Expand Up @@ -266,7 +267,7 @@ def new_install_ip_checker(
biz_info: typing.Dict[str, typing.Any],
bk_cloud_info: typing.Dict[str, typing.Any],
biz_id__biz_name_map: typing.Dict[int, str],
host_id__process_status_info_map: typing.Dict[int, typing.Dict[str, typing.Any]],
host_id__agent_state_info_map: typing.Dict[int, typing.Dict[str, typing.Any]],
) -> bool:
"""
新装校验
Expand All @@ -276,7 +277,7 @@ def new_install_ip_checker(
:param biz_info: 期望安装到的目标业务信息
:param bk_cloud_info: 期望安装到的云区域信息
:param biz_id__biz_name_map: 主机 ID - 主机名称 映射
:param host_id__process_status_info_map: 主机 ID - Agent 进程信息映射关系
:param host_id__agent_state_info_map: 主机 ID - Agent 进程信息映射关系
:return:
"""
host_infos_with_the_same_ips: typing.List[
Expand All @@ -295,10 +296,12 @@ def new_install_ip_checker(
# 2. Agent 已经失联,新增
first_online_host: typing.Optional[typing.Dict] = None
for host_info_with_the_same_ip in host_infos_with_the_same_ips:
if (
host_id__process_status_info_map.get(host_info_with_the_same_ip["bk_host_id"], {}).get("status")
== const.ProcStateType.RUNNING
):
is_running: bool = (
host_id__agent_state_info_map.get(host_info_with_the_same_ip["bk_host_id"], {}).get("bk_agent_alive")
== const.BkAgentStatus.ALIVE.value
)

if is_running:
first_online_host = host_info_with_the_same_ip
break

Expand Down Expand Up @@ -486,21 +489,25 @@ def install_validate(

is_check_pass = True
if op_type in [const.OpType.INSTALL, const.OpType.REPLACE] and job_type != const.JobType.INSTALL_PROXY:
bk_host_ids: typing.List[int] = []
query_hosts: typing.List[typing.Dict[str, typing.Any]] = []
for host_infos in host_infos_gby_ip_key.values():
bk_host_ids.extend([host_info["bk_host_id"] for host_info in host_infos])
process_status_infos = ProcessStatus.objects.filter(
name=ProcessStatus.GSE_AGENT_PROCESS_NAME,
bk_host_id__in=bk_host_ids,
source_type=ProcessStatus.SourceType.DEFAULT,
).values("bk_host_id", "id", "status")

host_id__process_status_info_map: typing.Dict[int, typing.Dict[str, typing.Any]] = {}
for process_status_info in process_status_infos:
host_id__process_status_info_map[process_status_info["bk_host_id"]] = {
"id": process_status_info["id"],
"status": process_status_info["status"],
}
for host_info in host_infos:
query_hosts.append(
{
"bk_host_id": host_info["bk_host_id"],
"ip": host_info["inner_ip"] or host_info["inner_ipv6"],
"bk_cloud_id": host_info["bk_cloud_id"],
"bk_agent_id": host_info["bk_agent_id"],
}
)

agent_id__agent_state_info_map: typing.Dict[str, typing.Dict] = GseApiHelper.list_agent_state(query_hosts)
host_id__agent_state_info_map: typing.Dict[int, typing.Dict] = {}
for query_host in query_hosts:
agent_id: str = GseApiHelper.get_agent_id(query_host)
host_id__agent_state_info_map[query_host["bk_host_id"]] = agent_id__agent_state_info_map.get(
agent_id, {}
)

is_check_pass = new_install_ip_checker(
host_infos_gby_ip_key=host_infos_gby_ip_key,
Expand All @@ -509,7 +516,7 @@ def install_validate(
biz_info=biz_info,
bk_cloud_info=bk_cloud_info,
biz_id__biz_name_map=biz_id__biz_name_map,
host_id__process_status_info_map=host_id__process_status_info_map,
host_id__agent_state_info_map=host_id__agent_state_info_map,
)

elif op_type not in [const.OpType.INSTALL, const.OpType.REPLACE]:
Expand Down
2 changes: 1 addition & 1 deletion apps/node_man/serializers/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def validate(self, attrs):
attrs["bk_host_ids"] = bk_host_ids
attrs["bk_biz_scope"] = bk_biz_scope

if attrs["job_type"] not in ["REINSTALL_PROXY", "UPGRADE_PROXY"]:
if attrs["job_type"] not in ["REINSTALL_PROXY", "UPGRADE_PROXY", "UNINSTALL_PROXY"]:
return attrs

# 如果开启业务灰度,安装新版本 Agent
Expand Down
3 changes: 3 additions & 0 deletions dev_log/2.2.32/crayon_202211292126.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
feature:
- "P-Agent 安装 IPv6 场景适配 (closed #1290)"
6 changes: 3 additions & 3 deletions script_tools/agent_tools/agent2/setup_proxy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ is_port_connected_by_pid () {
sleep 1
stat -L -c %i /proc/"$pid"/fd/* 2>/dev/null \
| grep -qwFf - \
<( awk -v p="$port" 'BEGIN{ check=sprintf(":%04X01$", p)} $3$4 ~ check {print $10}' /proc/net/tcp) \
<( awk -v p="$port" 'BEGIN{ check=sprintf(":%04X01$", p)} $3$4 ~ check {print $10}' /proc/net/tcp*) \
&& return 0
done
return 1
Expand Down Expand Up @@ -456,9 +456,9 @@ remove_proxy () {
log remove_proxy - "trying to remove old proxy directory(${AGENT_SETUP_PATH}/${PROXY_CLEAN_UP_DIRS[@]})"

if [[ "$REMOVE" == "TRUE" ]]; then
unregister_agent_id
unregister_agent_id SKIP
clean_up_proxy_directory
log remove_agent DONE "agent removed"
log remove_proxy DONE "proxy removed"
exit 0
else
clean_up_proxy_directory
Expand Down
29 changes: 27 additions & 2 deletions script_tools/setup_pagent.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@

import argparse
import base64
import ipaddress
import json
import os
import re
import socket
import sys
import time
import traceback
from functools import partial
from io import StringIO
from pathlib import Path
from subprocess import Popen
from typing import Any, Dict, List
from typing import Any, Dict, List, Optional

PRIVATE_KEY_MERGED_TEXT = """
%%PRIVATE_KEY_MERGED_TEXT%%
Expand Down Expand Up @@ -45,6 +47,29 @@
JOB_PRIVATE_KEY_RE = re.compile(r"^(-{5}BEGIN .*? PRIVATE KEY-{5})(.*?)(-{5}END .*? PRIVATE KEY-{5}.?)$")


def is_ip(ip: str, _version: Optional[int] = None) -> bool:
"""
判断是否为合法 IP
:param ip:
:param _version: 是否为合法版本,缺省表示 both
:return:
"""
try:
ip_address = ipaddress.ip_address(ip)
except ValueError:
return False
if _version is None:
return True
return ip_address.version == _version


# 判断是否为合法 IPv6
is_v6 = partial(is_ip, _version=6)

# 判断是否为合法 IPv4
is_v4 = partial(is_ip, _version=4)


def arg_parser() -> argparse.ArgumentParser:
"""Commandline argument parser"""
parser = argparse.ArgumentParser(description="p-agent setup scripts")
Expand Down Expand Up @@ -277,7 +302,7 @@ def execute_shell_solution(


def is_port_listen(ip: str, port: int) -> bool:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = socket.socket((socket.AF_INET, socket.AF_INET6)[is_v6(ip)], socket.SOCK_STREAM)
r = s.connect_ex((ip, port))

if r == 0:
Expand Down
14 changes: 6 additions & 8 deletions script_tools/start_nginx.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,14 @@ fi

ipv6_valid_ip () {
local ip=$1
if [[ "${ip}" =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then
return 0
else
return 1
fi
regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$'
awk '$0 !~ /'"$regex"'/{print "not an ipv6=>"$0;exit 1}' <<< "$1"
}

nginx_dns_list=()
for dns_ip in ${DNS_LIST[@]}; do
if ipv6_valid_ip $dns_ip; then
nginx_dns_list+=(["${dns_ip}"])
nginx_dns_list+=(["$dns_ip"])
else
nginx_dns_list+=("$dns_ip")
fi
Expand Down Expand Up @@ -89,11 +87,11 @@ is_port_listen_by_pid () {
for port in "$@"; do
stat -L -c %%i /proc/"$pid"/fd/* 2>/dev/null | grep -qwFf - \
<( awk -v p="$port" 'BEGIN{ check=sprintf(":%%04X0A$", p)} $2$4 ~ check {print $10}' \
/proc/net/tcp) || ((ret+=1))
/proc/net/tcp*) || ((ret+=1))
done
done
return "$ret"
}
pid=$(cat /opt/nginx-portable/logs/nginx.pid);
is_port_listen_by_pid "$pid" %(bk_nodeman_nginx_download_port)s %(bk_nodeman_nginx_proxy_pass_port)s
exit $?
exit $?

0 comments on commit 6e2f059

Please sign in to comment.